From e1009551ac01e0ab6768855cc45cd18de98ca802 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Wed, 26 Mar 2025 00:02:31 +0000 Subject: initial serialization support --- .gitignore | 1 + libnvxx/Makefile | 4 +- libnvxx/nvxx.cc | 9 + libnvxx/nvxx.h | 4 + libnvxx/nvxx_base.h | 10 +- libnvxx/nvxx_iterator.cc | 2 - libnvxx/nvxx_iterator.h | 4 + libnvxx/nvxx_serialize.cc | 130 ++++++++++++++ libnvxx/nvxx_serialize.h | 381 ++++++++++++++++++++++++++++++++++++++++ libnvxx/tests/Makefile | 2 +- libnvxx/tests/nvxx_serialize.cc | 247 ++++++++++++++++++++++++++ 11 files changed, 788 insertions(+), 6 deletions(-) create mode 100644 libnvxx/nvxx_serialize.cc create mode 100644 libnvxx/nvxx_serialize.h create mode 100644 libnvxx/tests/nvxx_serialize.cc diff --git a/.gitignore b/.gitignore index 9da6e2b..1af43ec 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ Kyuafile /libnvxx/tests/nvxx_basic /libnvxx/tests/nvxx_iterator +/libnvxx/tests/nvxx_serialize diff --git a/libnvxx/Makefile b/libnvxx/Makefile index 966b4db..ed412e1 100644 --- a/libnvxx/Makefile +++ b/libnvxx/Makefile @@ -25,8 +25,8 @@ LIB_CXX= nvxx LIBDIR= ${PREFIX}/lib INCLUDEDIR= ${PREFIX}/include SHLIB_MAJOR= 1 -INCS= nvxx.h nvxx_base.h -SRCS= nvxx.cc nvxx_iterator.cc +INCS= nvxx.h nvxx_base.h nvxx_serialize.h +SRCS= nvxx.cc nvxx_iterator.cc nvxx_serialize.cc CXXSTD= c++23 CXXFLAGS+= -W -Wall -Wextra -Werror LDADD= -lnv diff --git a/libnvxx/nvxx.cc b/libnvxx/nvxx.cc index 7c1db5b..69b57f5 100644 --- a/libnvxx/nvxx.cc +++ b/libnvxx/nvxx.cc @@ -115,6 +115,15 @@ nv_list::nv_list(::nvlist_t *nvl) noexcept { } +nv_list::nv_list(const_nv_list const &other) + : __nv_list_base(::nvlist_clone(other.ptr()), + __detail::__nvlist_owning::__owning) +{ + if (__m_nv == nullptr) + throw std::system_error( + std::error_code(errno, std::system_category())); +} + nv_list::nv_list(nv_list const &other) : __nv_list_base(::nvlist_clone(other.__m_nv), __detail::__nvlist_owning::__owning) diff --git a/libnvxx/nvxx.h b/libnvxx/nvxx.h index 85eb684..53c95cc 100644 --- a/libnvxx/nvxx.h +++ b/libnvxx/nvxx.h @@ -27,7 +27,11 @@ * nvl: lightweight wrapper around nv(9). */ +#include +#include + #include "nvxx_base.h" #include "nvxx_iterator.h" +#include "nvxx_serialize.h" #endif /* !_NVXX_H_INCLUDED */ diff --git a/libnvxx/nvxx_base.h b/libnvxx/nvxx_base.h index 545caca..aa0a765 100644 --- a/libnvxx/nvxx_base.h +++ b/libnvxx/nvxx_base.h @@ -23,7 +23,9 @@ #ifndef _NVXX_BASE_H_INCLUDED #define _NVXX_BASE_H_INCLUDED -#include +#ifndef _NVXX_H_INCLUDED +# error include instead of including this header directly +#endif #include #include @@ -483,6 +485,12 @@ struct nv_list final */ explicit nv_list(::nvlist_t *) noexcept; + /* + * Create an nv_list object by copying an existing const_nv_list object + * with nvlist_clone(). On failure, throws std::system_error. + */ + explicit nv_list(const_nv_list const &); + /* * Create an nv_list by copying an existing nv_list object with * nvlist_clone(). On failure, throws std::system_error. diff --git a/libnvxx/nvxx_iterator.cc b/libnvxx/nvxx_iterator.cc index 7312152..692594a 100644 --- a/libnvxx/nvxx_iterator.cc +++ b/libnvxx/nvxx_iterator.cc @@ -20,8 +20,6 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include - #include "nvxx.h" namespace bsd { diff --git a/libnvxx/nvxx_iterator.h b/libnvxx/nvxx_iterator.h index c65f546..4c780ef 100644 --- a/libnvxx/nvxx_iterator.h +++ b/libnvxx/nvxx_iterator.h @@ -23,6 +23,10 @@ #ifndef _NVXX_ITERATOR_H_INCLUDED #define _NVXX_ITERATOR_H_INCLUDED +#ifndef _NVXX_H_INCLUDED +# error include instead of including this header directly +#endif + #include #include #include diff --git a/libnvxx/nvxx_serialize.cc b/libnvxx/nvxx_serialize.cc new file mode 100644 index 0000000..8676467 --- /dev/null +++ b/libnvxx/nvxx_serialize.cc @@ -0,0 +1,130 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or distribute + * this software, either in source code form or as a compiled binary, for any + * purpose, commercial or non-commercial, and by any means. + * + * In jurisdictions that recognize copyright laws, the author or authors of + * this software dedicate any and all copyright interest in the software to the + * public domain. We make this dedication for the benefit of the public at + * large and to the detriment of our heirs and successors. We intend this + * dedication to be an overt act of relinquishment in perpetuity of all present + * and future rights to this software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "nvxx.h" + +namespace bsd { + +/* + * type encoders/decoders + */ + +/* bool */ + +void +nv_encoder::encode(nv_list &nvl, std::string_view key, bool value) +{ + nvl.add_bool(key, value); +} + +bool +nv_decoder::decode(const_nv_list const &nvl, std::string_view key) +{ + return (nvl.get_bool(key)); +} + +/* uint64_t */ + +void +nv_encoder::encode(nv_list &nvl, + std::string_view key, + std::uint64_t value) +{ + nvl.add_number(key, value); +} + +std::uint64_t +nv_decoder::decode(const_nv_list const &nvl, + std::string_view key) +{ + return (nvl.get_number(key)); +} + +/* string */ + +void +nv_encoder::encode(nv_list &nvl, + std::string_view key, + std::string const &value) +{ + nvl.add_string(key, value); +} + +std::string +nv_decoder::decode(const_nv_list const &nvl, + std::string_view key) +{ + return (std::string(nvl.get_string(key))); +} + +/* string_view */ + +void +nv_encoder::encode(nv_list &nvl, + std::string_view key, + std::string_view value) +{ + nvl.add_string(key, value); +} + +std::string_view +nv_decoder::decode(const_nv_list const &nvl, + std::string_view key) +{ + return (nvl.get_string(key)); +} + +/* nv_list */ + +void +nv_encoder::encode(nv_list &nvl, + std::string_view key, + nv_list const &value) +{ + nvl.add_nvlist(key, value); +} + +nv_list +nv_decoder::decode(const_nv_list const &nvl, + std::string_view key) +{ + return (nv_list(nvl.get_nvlist(key))); +} + +/* const_nv_list */ + +void +nv_encoder::encode(nv_list &nvl, + std::string_view key, + const_nv_list const &value) +{ + nvl.add_nvlist(key, value); +} + +const_nv_list +nv_decoder::decode(const_nv_list const &nvl, + std::string_view key) +{ + return (nvl.get_nvlist(key)); +} + +} // namespace bsd diff --git a/libnvxx/nvxx_serialize.h b/libnvxx/nvxx_serialize.h new file mode 100644 index 0000000..37deab6 --- /dev/null +++ b/libnvxx/nvxx_serialize.h @@ -0,0 +1,381 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or distribute + * this software, either in source code form or as a compiled binary, for any + * purpose, commercial or non-commercial, and by any means. + * + * In jurisdictions that recognize copyright laws, the author or authors of + * this software dedicate any and all copyright interest in the software to the + * public domain. We make this dedication for the benefit of the public at + * large and to the detriment of our heirs and successors. We intend this + * dedication to be an overt act of relinquishment in perpetuity of all present + * and future rights to this software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _NVXX_SERIALIZE_H +#define _NVXX_SERIALIZE_H + +#ifndef _NVXX_H_INCLUDED +# error include instead of including this header directly +#endif + +#include +#include +#include +#include +#include +#include + +namespace bsd { + +namespace __detail { + +template +concept __range_of = std::ranges::range<_R> && + std::same_as<_T, + std::remove_cvref_t>>; + +template +concept __from_range_container_of = + __range_of<_Container, _T> + && (std::move_constructible<_Container> + || std::copy_constructible<_Container>) + && requires (std::span<_T> __values) { + _Container(std::from_range, __values); + }; + +template +concept __push_back_container_of = + __range_of<_Container, _T> + && std::default_initializable<_Container> + && (std::move_constructible<_Container> + || std::copy_constructible<_Container>) + && requires (_Container &__c, _T __value) { + __c.push_back(__value); + }; + +} // namespace __detail + +/* + * Encoders/decoders for basic types. + */ + +template +struct nv_encoder; +template +struct nv_decoder; + +/* bool */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, bool); +}; + +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_bool_range(__key, std::forward<_U>(__range)); + } +}; + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) -> bool; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, __nvl.get_bool_array(__key)); + } +}; + +/* uint64_t */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, std::uint64_t); +}; + +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_number_range(__key, std::forward<_U>(__range)); + } +}; + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) -> std::uint64_t; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, __nvl.get_number_array(__key)); + } +}; + +/* string */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, std::string const &); +}; + +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_string_range(__key, + __range | std::views::transform([] (auto const &__s) { + return std::string_view(__s); + })); + } +}; + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) -> std::string; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, + __nvl.get_string_array(__key) + | std::views::transform([] (auto const &__s) { + return std::string(__s); + })); + } +}; + +/* string_view */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, std::string_view); +}; + +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_string_range(__key, std::forward<_U>(__range)); + } +}; + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) + -> std::string_view; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, __nvl.get_string_array(__key)); + } +}; + +/* nv_list */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, nv_list const &); +}; + +#ifdef notyet // XXX: implement add_nvlist_range() +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_nvlist_range(__key, std::forward<_U>(__range)); + } +}; +#endif + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) -> nv_list; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, + __nvl.get_nvlist_array(__key) + | std::views::transform([] (auto const &__s) { + return nv_list(__s); + })); + } +}; + +/* const_nv_list */ + +template<> +struct nv_encoder { + void encode(nv_list &, std::string_view, const_nv_list const &); +}; + +#ifdef notyet // XXX: implement add_nvlist_range() +template<__detail::__range_of _R> +struct nv_encoder<_R> { + template + void encode(nv_list &__nvl, std::string_view __key, _U &&__range) { + __nvl.add_nvlist_range(__key, std::forward<_U>(__range)); + } +}; +#endif + +template<> +struct nv_decoder { + auto decode(const_nv_list const &, std::string_view) -> const_nv_list; +}; + +template<__detail::__from_range_container_of _C> +struct nv_decoder<_C> { + auto decode(const_nv_list const &__nvl, std::string_view __key) -> _C { + return _C(std::from_range, __nvl.get_nvlist_array(__key)); + } +}; + +/* optional */ + +template +struct nv_encoder> { + auto encode(nv_list &__nvl, + std::string_view __key, + std::optional<_T> const &__value) { + if (__value) + nv_encoder<_T>{}.encode(__nvl, __key, *__value); + } +}; + +template +struct nv_decoder> { + auto decode(const_nv_list const &__nvl, + std::string_view __key) -> std::optional<_T> { + if (__nvl.exists(__key)) + return {nv_decoder<_T>{}.decode(__nvl, __key)}; + else + return {}; + } +}; + +/* + * object (de)serialization + */ + +template +struct nv_field { + nv_field(std::string __name, _Member _Object::* __ptr) + : __field_name(__name) + , __field_ptr(__ptr) + { + } + + auto serialize(nv_list &__nvl, _Object const &__object) { + nv_encoder<_Member>{}.encode(__nvl, + __field_name, + __object.*__field_ptr); + } + + auto deserialize(const_nv_list const &__nvl, _Object &__object) { + __object.*__field_ptr = nv_decoder<_Member>{} + .decode(__nvl, __field_name); + } + +private: + std::string __field_name; + _Member _Object::* __field_ptr; +}; + +namespace __detail { + +template +struct __field_sequence { + __field_sequence(_First __first_, _Second __second_) + : __first(__first_) + , __second(__second_) + { + } + + template + auto serialize(nv_list &__nvl, _Object const &__object) { + __first.serialize(__nvl, __object); + __second.serialize(__nvl, __object); + } + + template + auto deserialize(const_nv_list const &__nvl, _Object &__object) { + __first.deserialize(__nvl, __object); + __second.deserialize(__nvl, __object); + } + +private: + _First __first; + _Second __second; +}; + +} // namespace __detail + +template +auto operator>> (nv_field<_Object, _M1> const &__f1, + nv_field<_Object, _M2> const &__f2) +{ + return __detail::__field_sequence(__f1, __f2); +} + +template +auto operator>> ( + __detail::__field_sequence<__first, __second> const &__f1, + nv_field<_Object, _M> const &__f2 + ) +{ + return __detail::__field_sequence(__f1, __f2); +} + +template +nv_field(std::string_view, _Member _Object::*) + -> nv_field, _Member>; + +template +struct nv_schema; + +template +auto nv_serialize(_T &&__o) -> nv_list +{ + using __schema_type = nv_schema>; + auto __schema = __schema_type{}.get(); + auto __nvl = nv_list(); + + __schema.serialize(__nvl, __o); + return __nvl; +} + +template +auto nv_deserialize(const_nv_list const &__nvl) -> _T +{ + using __schema_type = nv_schema>; + auto __schema = __schema_type{}.get(); + + auto __obj = _T(); + __schema.deserialize(__nvl, __obj); + return __obj; +} + +} // namespace bsd + +#endif /* !_NVXX_SERIALIZE_H */ diff --git a/libnvxx/tests/Makefile b/libnvxx/tests/Makefile index db4b538..af44f03 100644 --- a/libnvxx/tests/Makefile +++ b/libnvxx/tests/Makefile @@ -20,7 +20,7 @@ PREFIX?= /usr/local TESTSDIR?= ${PREFIX}/tests/nvxx -ATF_TESTS_CXX= nvxx_basic nvxx_iterator +ATF_TESTS_CXX= nvxx_basic nvxx_iterator nvxx_serialize CXXSTD= c++23 # Note that we can't use -Werror here because it breaks ATF. CXXFLAGS+= -W -Wall -Wextra diff --git a/libnvxx/tests/nvxx_serialize.cc b/libnvxx/tests/nvxx_serialize.cc new file mode 100644 index 0000000..e3fbd5b --- /dev/null +++ b/libnvxx/tests/nvxx_serialize.cc @@ -0,0 +1,247 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or distribute + * this software, either in source code form or as a compiled binary, for any + * purpose, commercial or non-commercial, and by any means. + * + * In jurisdictions that recognize copyright laws, the author or authors of + * this software dedicate any and all copyright interest in the software to the + * public domain. We make this dedication for the benefit of the public at + * large and to the detriment of our heirs and successors. We intend this + * dedication to be an overt act of relinquishment in perpetuity of all present + * and future rights to this software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nvxx.h" + +#define TEST_CASE(name) \ + ATF_TEST_CASE_WITHOUT_HEAD(name) \ + ATF_TEST_CASE_BODY(name) + +using namespace bsd; + +/* + * bool + */ + +TEST_CASE(nv_encoder_bool) +{ + auto v = true; + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", v); + + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v, v2); +} + +TEST_CASE(nv_encoder_bool_vector) +{ + auto v = std::vector{true, false, false, true}; + auto nvl = nv_list{}; + + nv_encoder>{}.encode(nvl, "test", v); + + auto v2 = nv_decoder>{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2)); +} + +/* + * std::uint64_t + */ + +TEST_CASE(nv_encoder_uint64) +{ + auto v = std::uint64_t{42}; + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", v); + + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v, v2); +} + +TEST_CASE(nv_encoder_uint64_vector) +{ + auto v = std::vector{1, 2, 42, 666}; + auto nvl = nv_list{}; + + nv_encoder>{}.encode(nvl, "test", v); + + auto v2 = nv_decoder>{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2)); +} + +/* + * string + */ + +TEST_CASE(nv_encoder_string) +{ + auto v = std::string("testing"); + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", v); + + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v, v2); +} + +TEST_CASE(nv_encoder_string_vector) +{ + auto v = std::vector{"foo", "bar", "baz", "quux"}; + auto nvl = nv_list{}; + + nv_encoder>{}.encode(nvl, "test", v); + + auto v2 = nv_decoder>{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2)); +} + +/* + * string_view + */ + +TEST_CASE(nv_encoder_string_view) +{ + auto v = std::string_view("testing"); + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", v); + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v, v2); +} + +TEST_CASE(nv_encoder_string_view_vector) +{ + auto v = std::vector{"foo", "bar", "baz", "quux"}; + auto nvl = nv_list{}; + + nv_encoder>{}.encode(nvl, "test", v); + + auto v2 = nv_decoder>{} + .decode(nvl, "test"); + ATF_REQUIRE_EQ(true, std::ranges::equal(v, v2)); +} + +/* + * nv_list + */ + +TEST_CASE(nv_encoder_nv_list) +{ + auto v = nv_list{}; + v.add_number("int", 42); + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", v); + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v.get_number("int"), v2.get_number("int")); +} + +/* + * const_nv_list + */ + +TEST_CASE(nv_encoder_const_nv_list) +{ + auto v = nv_list{}; + v.add_number("int", 42); + auto nvl = nv_list{}; + + nv_encoder{}.encode(nvl, "test", const_nv_list(v)); + auto v2 = nv_decoder{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(v.get_number("int"), v2.get_number("int")); +} + +/* + * std::optional<> + */ + +TEST_CASE(nv_encoder_optional) +{ + auto v = std::optional{42}; + auto nvl = nv_list{}; + + nv_encoder>{}.encode(nvl, "test", v); + + auto v2 = nv_decoder< + std::optional>{}.decode(nvl, "test"); + ATF_REQUIRE_EQ(true, v2.has_value()); + ATF_REQUIRE_EQ(*v, *v2); + + v2 = nv_decoder>{}.decode(nvl, "nonesuch"); + ATF_REQUIRE_EQ(false, v2.has_value()); +} + +/* + * nv_(de)serialize() + */ + +struct object { + std::uint64_t int_value{}; + std::string string_value{}; + std::vector array_value; +}; + +namespace bsd { + +template<> +struct nv_schema<::object> { + auto get() { + return bsd::nv_field("int value", &object::int_value) + >> bsd::nv_field("string value", &object::string_value) + >> bsd::nv_field("array value", &object::array_value); + } +}; + +} // namespace bsd + +TEST_CASE(nv_serialize) +{ + auto obj = object{42, "quux", {42, 666, 1024}}; + auto nvl = bsd::nv_serialize(obj); + + auto obj2 = bsd::nv_deserialize(nvl); + ATF_REQUIRE_EQ(42, obj2.int_value); + ATF_REQUIRE_EQ("quux", obj2.string_value); + ATF_REQUIRE_EQ(true, std::ranges::equal(obj2.array_value, + std::vector{42, 666, 1024})); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, nv_encoder_bool); + ATF_ADD_TEST_CASE(tcs, nv_encoder_bool_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_uint64); + ATF_ADD_TEST_CASE(tcs, nv_encoder_uint64_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_string); + ATF_ADD_TEST_CASE(tcs, nv_encoder_string_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_string_view); + ATF_ADD_TEST_CASE(tcs, nv_encoder_string_view_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_nv_list); + //ATF_ADD_TEST_CASE(tcs, nv_encoder_nv_list_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_const_nv_list); + //ATF_ADD_TEST_CASE(tcs, nv_encoder_const_nv_list_vector); + ATF_ADD_TEST_CASE(tcs, nv_encoder_optional); + + ATF_ADD_TEST_CASE(tcs, nv_serialize); +} -- cgit v1.2.3