aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-26 20:47:45 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-26 20:47:45 +0100
commit90aa957ca9b7c217af7569009d1675e0f3ff8e9b (patch)
treee6a61ca2b6928e6414372b9b1484ce80fa2fb0b3 /nihil.ucl
parent1db86c401df11423c945634d8b2a483e97afa878 (diff)
downloadnihil-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.ccm29
-rw-r--r--nihil.ucl/parser.cc71
-rw-r--r--nihil.ucl/parser.ccm101
-rw-r--r--nihil.ucl/tests/array.cc53
-rw-r--r--nihil.ucl/tests/boolean.cc18
-rw-r--r--nihil.ucl/tests/emit.cc24
-rw-r--r--nihil.ucl/tests/integer.cc16
-rw-r--r--nihil.ucl/tests/object.cc15
-rw-r--r--nihil.ucl/tests/parse.cc14
-rw-r--r--nihil.ucl/tests/real.cc17
-rw-r--r--nihil.ucl/tests/string.cc15
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(),
- &macro_handler::handle,
- handler.get());
+ ::ucl_parser_register_macro(
+ self.get_parser(), cname.c_str(),
+ &macro_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");