aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/type.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-22 16:56:39 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-22 16:56:39 +0100
commitd6c3858418c4c00adb18d927135f73ed5a54564a (patch)
tree6a3bc3e70b3a63eadbf99955070adf1fa2e303e7 /nihil.ucl/type.ccm
parent429be0c13e16b51b8fc7695c5f3ff65ac057fca7 (diff)
downloadnihil-d6c3858418c4c00adb18d927135f73ed5a54564a.tar.gz
nihil-d6c3858418c4c00adb18d927135f73ed5a54564a.tar.bz2
nihil.ucl: improve type safety
Diffstat (limited to 'nihil.ucl/type.ccm')
-rw-r--r--nihil.ucl/type.ccm93
1 files changed, 93 insertions, 0 deletions
diff --git a/nihil.ucl/type.ccm b/nihil.ucl/type.ccm
new file mode 100644
index 0000000..2aef1a6
--- /dev/null
+++ b/nihil.ucl/type.ccm
@@ -0,0 +1,93 @@
+/*
+ * This source code is released into the public domain.
+ */
+
+module;
+
+#include <concepts>
+#include <string>
+
+#include <ucl.h>
+
+export module nihil.ucl:type;
+
+import :error;
+
+namespace nihil::ucl {
+
+// Our strongly-typed version of ::ucl_type.
+export enum struct object_type {
+ object = UCL_OBJECT,
+ array = UCL_ARRAY,
+ integer = UCL_INT,
+ real = UCL_FLOAT,
+ string = UCL_STRING,
+ boolean = UCL_BOOLEAN,
+ time = UCL_TIME,
+ userdata = UCL_USERDATA,
+ null = UCL_NULL,
+};
+
+// Get the name of a type.
+export auto str(object_type type) -> std::string_view {
+ using namespace std::literals;
+
+ switch (type) {
+ case object_type::object:
+ return "object"sv;
+ case object_type::array:
+ return "array"sv;
+ case object_type::integer:
+ return "integer"sv;
+ case object_type::real:
+ return "real"sv;
+ case object_type::string:
+ return "string"sv;
+ case object_type::boolean:
+ return "boolean"sv;
+ case object_type::time:
+ return "time"sv;
+ case object_type::userdata:
+ return "userdata"sv;
+ case object_type::null:
+ return "null"sv;
+ default:
+ // Don't fail here, since UCL might add more types that we
+ // don't know about.
+ return "unknown"sv;
+ }
+}
+
+// Concept of a UCL data type.
+export template<typename T>
+concept datatype = requires(T o) {
+ { o.get_ucl_object() } -> std::convertible_to<::ucl_object_t const *>;
+ { o.type() } -> std::same_as<object_type>;
+ { T::ucl_type } -> std::convertible_to<object_type>;
+};
+
+// Exception thrown when a type assertion fails.
+export struct type_mismatch : error {
+ type_mismatch(object_type expected_type, object_type actual_type)
+ : error("UCL type mismatch: expected type '{}' != actual type '{}'",
+ str(expected_type), str(actual_type))
+ , _expected_type(expected_type)
+ , _actual_type(actual_type)
+ {}
+
+ // The type we expected.
+ auto expected_type(this type_mismatch const &self) -> object_type {
+ return self._expected_type;
+ }
+
+ // The type we got.
+ auto actual_type(this type_mismatch const &self) -> object_type {
+ return self._actual_type;
+ }
+
+private:
+ object_type _expected_type;
+ object_type _actual_type;
+};
+
+} // namespace nihil::ucl