diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-26 20:47:45 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-26 20:47:45 +0100 |
| commit | 90aa957ca9b7c217af7569009d1675e0f3ff8e9b (patch) | |
| tree | e6a61ca2b6928e6414372b9b1484ce80fa2fb0b3 /nihil.ucl | |
| parent | 1db86c401df11423c945634d8b2a483e97afa878 (diff) | |
| download | nihil-90aa957ca9b7c217af7569009d1675e0f3ff8e9b.tar.gz nihil-90aa957ca9b7c217af7569009d1675e0f3ff8e9b.tar.bz2 | |
ucl, config: use monadic error handling more
Diffstat (limited to 'nihil.ucl')
| -rw-r--r-- | nihil.ucl/object_cast.ccm | 29 | ||||
| -rw-r--r-- | nihil.ucl/parser.cc | 71 | ||||
| -rw-r--r-- | nihil.ucl/parser.ccm | 101 | ||||
| -rw-r--r-- | nihil.ucl/tests/array.cc | 53 | ||||
| -rw-r--r-- | nihil.ucl/tests/boolean.cc | 18 | ||||
| -rw-r--r-- | nihil.ucl/tests/emit.cc | 24 | ||||
| -rw-r--r-- | nihil.ucl/tests/integer.cc | 16 | ||||
| -rw-r--r-- | nihil.ucl/tests/object.cc | 15 | ||||
| -rw-r--r-- | nihil.ucl/tests/parse.cc | 14 | ||||
| -rw-r--r-- | nihil.ucl/tests/real.cc | 17 | ||||
| -rw-r--r-- | nihil.ucl/tests/string.cc | 15 |
11 files changed, 257 insertions, 116 deletions
diff --git a/nihil.ucl/object_cast.ccm b/nihil.ucl/object_cast.ccm index b10ffbc..07588a1 100644 --- a/nihil.ucl/object_cast.ccm +++ b/nihil.ucl/object_cast.ccm @@ -4,12 +4,15 @@ module; +#include <coroutine> #include <cstdlib> +#include <expected> #include <ucl.h> export module nihil.ucl:object_cast; +import nihil; import :type; import :object; import :array; @@ -25,21 +28,22 @@ namespace nihil::ucl { template<datatype To> struct convert_check { - auto check(::ucl_object_t const *from) -> void + [[nodiscard]] auto check(::ucl_object_t const *from) + -> std::expected<void, type_mismatch> { auto from_type = static_cast<object_type>(::ucl_object_type(from)); auto to_type = To::ucl_type; // Converting from anything to object is permitted. if (to_type == object_type::object) - return; + return {}; // Converting between two equal types is permitted. if (from_type == to_type) - return; + return {}; // Otherwise, this is an error. - throw type_mismatch(to_type, from_type); + return std::unexpected(type_mismatch(to_type, from_type)); } }; @@ -47,7 +51,8 @@ struct convert_check template<typename T> struct convert_check<array<T>> { - auto check(::ucl_object_t const *from) -> void + [[nodiscard]] auto check(::ucl_object_t const *from) + -> std::expected<void, type_mismatch> { using To = array<T>; auto from_type = static_cast<object_type>(::ucl_object_type(from)); @@ -55,13 +60,17 @@ struct convert_check<array<T>> // If the source type is not an array, this is an error. if (from_type != object_type::array) - throw type_mismatch(to_type, from_type); + co_return std::unexpected( + type_mismatch(to_type, from_type)); for (std::size_t i = 0, size = ::ucl_array_size(from); i < size; ++i) { auto const *arr_obj = ::ucl_array_find_index(from, i); - convert_check<typename To::value_type>{}.check(arr_obj); + co_await convert_check<typename To::value_type>{} + .check(arr_obj); } + + co_return {}; } }; @@ -69,12 +78,12 @@ struct convert_check<array<T>> * Convert a UCL object to another type. */ export template<datatype To> -auto object_cast(object const &from) -> To +auto object_cast(object const &from) -> std::expected<To, type_mismatch> { auto uobj = from.get_ucl_object(); - convert_check<To>{}.check(uobj); - return To(nihil::ucl::ref, uobj); + co_await convert_check<To>{}.check(uobj); + co_return To(nihil::ucl::ref, uobj); } } // namespace nihil::ucl diff --git a/nihil.ucl/parser.cc b/nihil.ucl/parser.cc index 816116d..611fe50 100644 --- a/nihil.ucl/parser.cc +++ b/nihil.ucl/parser.cc @@ -4,6 +4,7 @@ module; +#include <expected> #include <functional> #include <string> @@ -11,15 +12,23 @@ module; module nihil.ucl; +import nihil; + namespace nihil::ucl { -parse_error::parse_error(std::string what) - : error(std::move(what)) +auto make_parser(int flags) -> std::expected<parser, nihil::error> { + auto *p = ::ucl_parser_new(flags); + if (p != nullptr) + return p; + + // TODO: Is there a way to get the actual error here? + return std::unexpected(nihil::error("failed to create parser")); } -auto macro_handler::handle(unsigned char const *data, std::size_t len, void *ud) --> bool +auto macro_handler::handle(unsigned char const *data, + std::size_t len, void *ud) + -> bool { auto handler = static_cast<macro_handler *>(ud); auto string = std::string_view( @@ -28,48 +37,66 @@ auto macro_handler::handle(unsigned char const *data, std::size_t len, void *ud) return handler->callback(string); } -parser::parser(int flags) +parser::parser(::ucl_parser *uclp) + : m_parser(uclp) { - if ((_parser = ::ucl_parser_new(flags)) != nullptr) - return; +} - throw error("failed to create UCL parser"); +parser::~parser() +{ + if (m_parser) + ::ucl_parser_free(m_parser); } -parser::parser() - : parser(0) +parser::parser(parser &&other) noexcept + : m_parser(std::exchange(other.m_parser, nullptr)) + , m_macros(std::move(other.m_macros)) { } -parser::~parser() +auto parser::operator=(this parser &self, parser &&other) noexcept + -> parser & { - if (_parser) - ::ucl_parser_free(_parser); + if (&self != &other) { + if (self.m_parser) + ::ucl_parser_free(self.m_parser); + + self.m_parser = std::exchange(other.m_parser, nullptr); + self.m_macros = std::move(other.m_macros); + } + + return self; } auto parser::register_value( this parser &self, std::string_view variable, std::string_view value) --> void + -> void { ::ucl_parser_register_variable( - self._parser, + self.get_parser(), std::string(variable).c_str(), std::string(value).c_str()); } auto parser::top(this parser &self) -> map<object> { - if (self._parser == nullptr) - throw error("attempt to call top() on an empty parser"); + auto obj = ::ucl_parser_get_object(self.get_parser()); + if (obj != nullptr) + // ucl_parser_get_object() refs the object for us. + return {noref, obj}; - auto obj = ::ucl_parser_get_object(self._parser); - if (obj == nullptr) - throw error("attempt to call top() on an empty parser"); + throw std::logic_error( + "attempt to call top() on an invalid ucl::parser"); +} - // ucl_parser_get_objects() refs the object for us. - return {noref, obj}; +auto parser::get_parser(this parser &self) -> ::ucl_parser * +{ + if (self.m_parser == nullptr) + throw std::logic_error("attempt to fetch a null ucl::parser"); + + return self.m_parser; } } // namespace nihil::ucl diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm index f817b76..efddd5f 100644 --- a/nihil.ucl/parser.ccm +++ b/nihil.ucl/parser.ccm @@ -4,6 +4,8 @@ module; +#include <coroutine> +#include <expected> #include <format> #include <functional> #include <memory> @@ -21,13 +23,6 @@ import :map; namespace nihil::ucl { -/* - * Exception thrown when an issue occurs parsing UCL. - */ -export struct parse_error : error { - parse_error(std::string what); -}; - // UCL parser flags. export inline constexpr int parser_key_lower = UCL_PARSER_KEY_LOWERCASE; export inline constexpr int parser_zerocopy = UCL_PARSER_ZEROCOPY; @@ -49,88 +44,118 @@ struct macro_handler { /* * A UCL parser. This wraps the C ucl_parser API. + * + * parser itself is not exported; use make_parser() to create one. */ -export struct parser { - // Create a new parser with the given flags. - parser(int flags); - - // Create a new parser with the default flags. - parser(); +struct parser { + // Create a parser from a UCL parser. + parser(::ucl_parser *); // Destroy our parser when we're destroyed. ~parser(); + // Not copyable. + parser(parser const &) = delete; + auto operator=(this parser &, parser const &) -> parser & = delete; + + // Movable. + parser(parser &&) noexcept; + auto operator=(this parser &, parser &&) noexcept -> parser &; + // Add a parser macro. Unlike ucl_parser_register_macro, this doesn't // take a userdata parameter; it's assumed the user will use lambda // capture or similar if needed. template<std::invocable<std::string_view> F> auto register_macro(this parser &self, std::string_view name, - F &&func) -> void + F &&func) + -> void requires (std::same_as<bool, std::invoke_result<F>>) { auto handler = std::make_unique<macro_handler>( std::move(func)); auto cname = std::string(name); - ::ucl_parser_register_macro(self._parser, cname.c_str(), - ¯o_handler::handle, - handler.get()); + ::ucl_parser_register_macro( + self.get_parser(), cname.c_str(), + ¯o_handler::handle, handler.get()); - self._macros.emplace_back(std::move(handler)); + self.m_macros.emplace_back(std::move(handler)); } // Add a parser variable. auto register_value(this parser &self, std::string_view variable, - std::string_view value) -> void; + std::string_view value) + -> void; // Add data to the parser. - auto add(this parser &self, - std::ranges::contiguous_range auto &&data) - -> void + [[nodiscard]] auto add(this parser &self, + std::ranges::contiguous_range auto &&data) + -> std::expected<void, nihil::error> // Only bytes (chars) are permitted. requires(sizeof(std::ranges::range_value_t<decltype(data)>) == 1) { - // UCL accepts unsigned chars, but this is quite unhelpful - // when reading from files or strings. + auto *p = self.get_parser(); auto dptr = reinterpret_cast<unsigned char const *>( std::ranges::data(data)); - auto ret = ::ucl_parser_add_chunk(self._parser, dptr, - std::ranges::size(data)); - if (ret == false) - throw parse_error(::ucl_parser_get_error(self._parser)); + auto ret = ::ucl_parser_add_chunk( + p, dptr, std::ranges::size(data)); + + if (ret == true) + return {}; + + return std::unexpected(nihil::error(::ucl_parser_get_error(p))); } - auto add(this parser &self, std::ranges::range auto &&data) - -> void + [[nodiscard]] auto add(this parser &self, + std::ranges::range auto &&data) + -> std::expected<void, nihil::error> requires (!std::ranges::contiguous_range<decltype(data)>) { auto cdata = std::vector<char>( std::from_range, std::forward<decltype(data)>(data)); - return self.add(std::move(cdata)); + co_await self.add(std::move(cdata)); + co_return {}; } // Return the top object of this parser. - auto top(this parser &self) -> map<object>; + [[nodiscard]] auto top(this parser &self) -> map<object>; + + // Return the stored parser object. + [[nodiscard]] auto get_parser(this parser &self) -> ::ucl_parser *; private: // The parser object. Should never be null, unless we've been // moved-from. - ucl_parser *_parser = nullptr; + ucl_parser *m_parser; // Functions added by register_macro. We have to store these as // pointers because we pass the address to libucl. - std::vector<std::unique_ptr<macro_handler>> _macros; + std::vector<std::unique_ptr<macro_handler>> m_macros; }; +// Create a parser with the given flags. +export [[nodiscard]] auto +make_parser(int flags = 0) -> std::expected<parser, nihil::error>; + // Utility function to parse something and return the top-level object. -export auto parse(std::ranges::range auto &&data) -> map<object> { - auto p = parser(); - p.add(std::forward<decltype(data)>(data)); - return p.top(); +export [[nodiscard]] auto +parse(int flags, std::ranges::range auto &&data) + -> std::expected<map<object>, nihil::error> +{ + auto p = co_await make_parser(flags); + co_await p.add(std::forward<decltype(data)>(data)); + co_return p.top(); +} + +export [[nodiscard]] auto +parse(std::ranges::range auto &&data) + -> std::expected<map<object>, nihil::error> +{ + co_return co_await parse(0, std::forward<decltype(data)>(data)); } } // namespace nihil::ucl diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc index ce0976f..fb23178 100644 --- a/nihil.ucl/tests/array.cc +++ b/nihil.ucl/tests/array.cc @@ -4,12 +4,14 @@ #include <algorithm> #include <concepts> +#include <expected> #include <ranges> #include <string> #include <catch2/catch_test_macros.hpp> #include <ucl.h> +import nihil; import nihil.ucl; TEST_CASE("ucl: array: invariants", "[ucl]") @@ -154,8 +156,14 @@ TEST_CASE("ucl: array: parse", "[ucl]") using namespace std::literals; using namespace nihil::ucl; - auto obj = parse("value = [1, 42, 666]"sv); - auto arr = object_cast<array<integer>>(obj["value"]); + auto obj_err = parse("value = [1, 42, 666]"sv); + REQUIRE(obj_err); + auto obj = *obj_err; + + auto err = object_cast<array<integer>>(obj["value"]); + REQUIRE(err); + + auto arr = *err; REQUIRE(arr.size() == 3); REQUIRE(arr[0] == 1); REQUIRE(arr[1] == 42); @@ -167,7 +175,9 @@ TEST_CASE("ucl: array: emit", "[ucl]") using namespace nihil::ucl; auto ucl = parse("array = [1, 42, 666];"); - auto output = std::format("{:c}", ucl); + REQUIRE(ucl); + + auto output = std::format("{:c}", *ucl); REQUIRE(output == "array [\n" " 1,\n" @@ -211,7 +221,8 @@ TEST_CASE("ucl: array: bad object_cast", "[ucl]") auto arr = array<integer>(); - REQUIRE_THROWS_AS(object_cast<integer>(arr), type_mismatch); + auto cast_ok = object_cast<integer>(arr); + REQUIRE(!cast_ok); } TEST_CASE("ucl: array: heterogeneous elements", "[ucl]") @@ -219,19 +230,27 @@ TEST_CASE("ucl: array: heterogeneous elements", "[ucl]") using namespace std::literals; using namespace nihil::ucl; - auto obj = parse("array [ 42, true, \"test\" ];"); - auto arr = object_cast<array<>>(obj["array"]); + auto obj_err = parse("array [ 42, true, \"test\" ];"); + REQUIRE(obj_err); + auto obj = *obj_err; + auto err = object_cast<array<>>(obj["array"]); + REQUIRE(err); + + auto arr = *err; REQUIRE(arr.size() == 3); auto int_obj = object_cast<integer>(arr[0]); - REQUIRE(int_obj == 42); + REQUIRE(int_obj); + REQUIRE(*int_obj == 42); auto bool_obj = object_cast<boolean>(arr[1]); - REQUIRE(bool_obj == true); + REQUIRE(bool_obj); + REQUIRE(*bool_obj == true); auto string_obj = object_cast<string>(arr[2]); - REQUIRE(string_obj == "test"); + REQUIRE(string_obj); + REQUIRE(*string_obj == "test"); } TEST_CASE("ucl: array: heterogenous cast", "[ucl]") @@ -243,10 +262,14 @@ TEST_CASE("ucl: array: heterogenous cast", "[ucl]") arr.push_back(boolean(true)); // Converting to an array<integer> should fail. - REQUIRE_THROWS_AS(object_cast<array<integer>>(arr), type_mismatch); + auto cast_ok = object_cast<array<integer>>(arr); + REQUIRE(!cast_ok); // Converting to array<object> should succeed. - auto obj_arr = object_cast<array<object>>(arr); + auto err = object_cast<array<object>>(arr); + REQUIRE(err); + + auto obj_arr = *err; REQUIRE(obj_arr[0] == integer(42)); } @@ -261,10 +284,14 @@ TEST_CASE("ucl: array: homogeneous cast", "[ucl]") auto obj = object(ref, arr.get_ucl_object()); // Converting to array<string> should fail. - REQUIRE_THROWS_AS(object_cast<array<string>>(obj), type_mismatch); + auto cast_ok = object_cast<array<string>>(obj); + REQUIRE(!cast_ok); // Converting to an array<integer> should succeed. - auto obj_arr = object_cast<array<integer>>(obj); + auto err = object_cast<array<integer>>(obj); + REQUIRE(err); + + auto obj_arr = *err; REQUIRE(obj_arr[0] == 1); REQUIRE(obj_arr[1] == 42); } diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/tests/boolean.cc index 49dc408..495071d 100644 --- a/nihil.ucl/tests/boolean.cc +++ b/nihil.ucl/tests/boolean.cc @@ -62,8 +62,11 @@ TEST_CASE("ucl: boolean: key()", "[ucl]") { using namespace nihil::ucl; - auto obj = parse("a_bool = true"); - REQUIRE(object_cast<boolean>(obj["a_bool"]).key() == "a_bool"); + auto err = parse("a_bool = true"); + REQUIRE(err); + + auto obj = *err; + REQUIRE(object_cast<boolean>(obj["a_bool"])->key() == "a_bool"); auto b = nihil::ucl::boolean(true); REQUIRE(b.key() == ""); @@ -95,10 +98,14 @@ TEST_CASE("ucl: boolean: parse", "[ucl]") { using namespace std::literals; - auto obj = nihil::ucl::parse("value = true"sv); + auto err = nihil::ucl::parse("value = true"sv); + REQUIRE(err); + + auto obj = *err; + auto v = obj["value"]; REQUIRE(v.key() == "value"); - REQUIRE(object_cast<nihil::ucl::boolean>(v) == true); + REQUIRE(*object_cast<nihil::ucl::boolean>(v) == true); } TEST_CASE("ucl: boolean: emit", "[ucl]") @@ -111,9 +118,10 @@ TEST_CASE("ucl: boolean: emit", "[ucl]") TEST_CASE("ucl: boolean: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("bool = true;"); + REQUIRE(ucl); auto output = std::string(); - emit(ucl, nihil::ucl::emitter::configuration, + emit(*ucl, nihil::ucl::emitter::configuration, std::back_inserter(output)); REQUIRE(output == "bool = true;\n"); diff --git a/nihil.ucl/tests/emit.cc b/nihil.ucl/tests/emit.cc index a8487c6..d75255b 100644 --- a/nihil.ucl/tests/emit.cc +++ b/nihil.ucl/tests/emit.cc @@ -15,11 +15,13 @@ TEST_CASE("ucl: emit to std::ostream", "[ucl]") using namespace std::literals; auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + auto strm = std::ostringstream(); - strm << obj; + strm << *obj; // The ostream emitter produces JSON. - REQUIRE(strm.str() == std::format("{:j}", obj)); + REQUIRE(strm.str() == std::format("{:j}", *obj)); } TEST_CASE("ucl: emit JSON with std::format", "[ucl]") @@ -27,7 +29,9 @@ TEST_CASE("ucl: emit JSON with std::format", "[ucl]") using namespace std::literals; auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); - auto str = std::format("{:j}", obj); + REQUIRE(obj); + + auto str = std::format("{:j}", *obj); REQUIRE(str == "{\n" @@ -39,7 +43,7 @@ TEST_CASE("ucl: emit JSON with std::format", "[ucl]") "}"); // Make sure JSON is the default format. - auto str2 = std::format("{}", obj); + auto str2 = std::format("{}", *obj); REQUIRE(str == str2); } @@ -48,7 +52,9 @@ TEST_CASE("ucl: emit compact JSON with std::format", "[ucl]") using namespace std::literals; auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); - auto str = std::format("{:J}", obj); + REQUIRE(obj); + + auto str = std::format("{:J}", *obj); REQUIRE(str == "{\"int\":[1,42,666]}"); } @@ -58,7 +64,9 @@ TEST_CASE("ucl: emit configuration with std::format", "[ucl]") using namespace std::literals; auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); - auto str = std::format("{:c}", obj); + REQUIRE(obj); + + auto str = std::format("{:c}", *obj); REQUIRE(str == "int [\n" @@ -73,7 +81,9 @@ TEST_CASE("ucl: emit YAML with std::format", "[ucl]") using namespace std::literals; auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); - auto str = std::format("{:y}", obj); + REQUIRE(obj); + + auto str = std::format("{:y}", *obj); REQUIRE(str == "int: [\n" diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/tests/integer.cc index 811a864..05647fe 100644 --- a/nihil.ucl/tests/integer.cc +++ b/nihil.ucl/tests/integer.cc @@ -63,8 +63,11 @@ TEST_CASE("ucl: integer: key()", "[ucl]") { using namespace nihil::ucl; - auto obj = parse("an_int = 42"); - REQUIRE(object_cast<integer>(obj["an_int"]).key() == "an_int"); + auto err = parse("an_int = 42"); + REQUIRE(err); + + auto obj = *err; + REQUIRE(object_cast<integer>(obj["an_int"])->key() == "an_int"); auto i = nihil::ucl::integer(42); REQUIRE(i.key() == ""); @@ -96,7 +99,11 @@ TEST_CASE("ucl: integer: parse", "[ucl]") { using namespace std::literals; - auto obj = nihil::ucl::parse("value = 42"sv); + auto err = nihil::ucl::parse("value = 42"sv); + REQUIRE(err); + + auto obj = *err; + auto v = obj["value"]; REQUIRE(v.key() == "value"); REQUIRE(object_cast<nihil::ucl::integer>(v) == 42); @@ -112,9 +119,10 @@ TEST_CASE("ucl: integer: emit", "[ucl]") TEST_CASE("ucl: integer: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("int = 42;"); + REQUIRE(ucl); auto output = std::string(); - emit(ucl, nihil::ucl::emitter::configuration, + emit(*ucl, nihil::ucl::emitter::configuration, std::back_inserter(output)); REQUIRE(output == "int = 42;\n"); diff --git a/nihil.ucl/tests/object.cc b/nihil.ucl/tests/object.cc index 1bbcf4f..3ad180e 100644 --- a/nihil.ucl/tests/object.cc +++ b/nihil.ucl/tests/object.cc @@ -26,12 +26,19 @@ TEST_CASE("ucl object: compare", "[ucl]") using namespace std::literals; auto obj_41 = nihil::ucl::parse("int = 41;"sv); + REQUIRE(obj_41); + auto obj_42 = nihil::ucl::parse("int = 42;"sv); + REQUIRE(obj_42); + auto obj_42_2 = nihil::ucl::parse("int = 42;"sv); + REQUIRE(obj_42_2); + auto obj_43 = nihil::ucl::parse("int = 43;"sv); + REQUIRE(obj_43); - REQUIRE(obj_42 == obj_42_2); - REQUIRE(obj_42 != obj_43); - REQUIRE(obj_42 < obj_43); - REQUIRE(obj_42 > obj_41); + REQUIRE(*obj_42 == *obj_42_2); + REQUIRE(*obj_42 != *obj_43); + REQUIRE(*obj_42 < *obj_43); + REQUIRE(*obj_42 > *obj_41); } diff --git a/nihil.ucl/tests/parse.cc b/nihil.ucl/tests/parse.cc index 3cf5742..c56974e 100644 --- a/nihil.ucl/tests/parse.cc +++ b/nihil.ucl/tests/parse.cc @@ -15,13 +15,18 @@ TEST_CASE("ucl parse: iterate array", "[ucl]") using namespace std::literals; using namespace nihil::ucl; - auto obj = parse("value = [1, 42, 666];"sv); + auto err = parse("value = [1, 42, 666];"sv); + REQUIRE(err); + + auto obj = *err; auto arr = obj["value"]; REQUIRE(arr.key() == "value"); - auto vec = std::vector(std::from_range, - object_cast<array<integer>>(arr)); + auto ints = object_cast<array<integer>>(arr); + REQUIRE(ints); + + auto vec = std::vector(std::from_range, *ints); REQUIRE(vec.size() == 3); REQUIRE(vec[0] == 1); @@ -36,8 +41,9 @@ TEST_CASE("ucl parse: iterate hash", "[ucl]") auto input = "int = 42; bool = true; str = \"test\";"sv; auto obj = parse(input); + REQUIRE(obj); - for (auto &&[key, value] : obj) { + for (auto &&[key, value] : *obj) { REQUIRE(key == value.key()); if (key == "int") diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/tests/real.cc index b11c113..be4e213 100644 --- a/nihil.ucl/tests/real.cc +++ b/nihil.ucl/tests/real.cc @@ -31,7 +31,7 @@ TEST_CASE("ucl: real: invariants", "[ucl]") TEST_CASE("ucl: real: construct", "[ucl]") { auto obj = nihil::ucl::real(42.1); - REQUIRE_THAT(object_cast<nihil::ucl::real>(obj).value(), + REQUIRE_THAT(object_cast<nihil::ucl::real>(obj)->value(), Catch::Matchers::WithinRel(42.1)); } @@ -80,19 +80,26 @@ TEST_CASE("ucl: real: parse", "[ucl]") { using namespace std::literals; - auto obj = nihil::ucl::parse("value = 42.1"sv); + auto err = nihil::ucl::parse("value = 42.1"sv); + REQUIRE(err); + + auto obj = *err; + auto v = obj["value"]; REQUIRE(v.key() == "value"); - REQUIRE_THAT(object_cast<nihil::ucl::real>(v).value(), + REQUIRE_THAT(object_cast<nihil::ucl::real>(v)->value(), Catch::Matchers::WithinRel(42.1)); } TEST_CASE("ucl: real: emit", "[ucl]") { - auto ucl = nihil::ucl::parse("real = 42.2"); + auto err = nihil::ucl::parse("real = 42.2"); + REQUIRE(err); + + auto obj = *err; auto output = std::string(); - emit(ucl, nihil::ucl::emitter::configuration, + emit(obj, nihil::ucl::emitter::configuration, std::back_inserter(output)); REQUIRE(output == "real = 42.2;\n"); diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/tests/string.cc index e7eb0ad..995e95a 100644 --- a/nihil.ucl/tests/string.cc +++ b/nihil.ucl/tests/string.cc @@ -109,8 +109,11 @@ TEST_CASE("ucl: string: key()", "[ucl]") { using namespace nihil::ucl; - auto obj = parse("a_string = \"test\""); - REQUIRE(object_cast<string>(obj["a_string"]).key() == "a_string"); + auto err = parse("a_string = \"test\""); + REQUIRE(err); + + auto obj = *err; + REQUIRE(object_cast<string>(obj["a_string"])->key() == "a_string"); auto s = nihil::ucl::string("test"); REQUIRE(s.key() == ""); @@ -188,7 +191,10 @@ TEST_CASE("ucl: string: parse", "[ucl]") { using namespace std::literals; - auto obj = nihil::ucl::parse("value = \"te\\\"st\""sv); + auto err = nihil::ucl::parse("value = \"te\\\"st\""sv); + REQUIRE(err); + + auto obj = *err; auto v = obj["value"]; REQUIRE(v.key() == "value"); REQUIRE(object_cast<nihil::ucl::string>(v) == "te\"st"); @@ -204,9 +210,10 @@ TEST_CASE("ucl: string: emit", "[ucl]") TEST_CASE("ucl: string: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("str = \"te\\\"st\";"); + REQUIRE(ucl); auto output = std::string(); - emit(ucl, nihil::ucl::emitter::configuration, + emit(*ucl, nihil::ucl::emitter::configuration, std::back_inserter(output)); REQUIRE(output == "str = \"te\\\"st\";\n"); |
