aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/string.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.ucl/string.ccm')
-rw-r--r--nihil.ucl/string.ccm229
1 files changed, 229 insertions, 0 deletions
diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm
new file mode 100644
index 0000000..c757bf1
--- /dev/null
+++ b/nihil.ucl/string.ccm
@@ -0,0 +1,229 @@
+/*
+ * This source code is released into the public domain.
+ */
+
+module;
+
+#include <cstdlib>
+#include <expected>
+#include <format>
+#include <iosfwd>
+#include <string>
+
+#include <ucl.h>
+
+export module nihil.ucl:string;
+
+import :object;
+import :type;
+
+namespace nihil::ucl {
+
+export struct string final : object {
+ using contained_type = std::string_view;
+ inline static constexpr object_type ucl_type = object_type::string;
+
+ // string is a container of char
+ using value_type = char const;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = value_type &;
+ using pointer = value_type *;
+ using iterator = pointer;
+
+ /*
+ * Create a new empty string. Throws std::system_error on failure.
+ */
+ string();
+
+ /*
+ * Create a string from a value. Throws std::system_error on failure.
+ */
+ explicit string(std::string_view);
+
+ /*
+ * Create a string from a C literal. Throws std::system_error
+ * on failure.
+ */
+ explicit string(char const *);
+
+ /*
+ * Create a string from a contiguous range. The range's value type
+ * must be char. Throws std::system_error on failure.
+ */
+ template<std::ranges::contiguous_range Range>
+ requires (!std::same_as<std::string_view, Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+ explicit string(Range &&range)
+ : string(std::string_view(std::ranges::begin(range),
+ std::ranges::end(range)))
+ {}
+
+ /*
+ * Create a string from a non-contiguous range. This requires a
+ * temporary value due to limitations of the UCL C API.
+ */
+ template<std::ranges::range Range>
+ requires (!std::ranges::contiguous_range<Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+ explicit string(Range &&range)
+ : string(std::string(std::from_range, range))
+ {}
+
+ /*
+ * Create a string from an iterator pair. The iterator's value type
+ * must be char. If the iterator pair is not contiguous, the value
+ * will be copied to a temporary first.
+ *
+ * Throws std::system_error on failure.
+ */
+ template<std::input_iterator Iterator>
+ requires (std::same_as<char, std::iter_value_t<Iterator>>)
+ string(Iterator first, Iterator last)
+ : string(std::ranges::subrange(first, last))
+ {}
+
+ /*
+ * Create a new string from a UCL object. Throws type_mismatch
+ * on failure.
+ */
+ string(ref_t, ::ucl_object_t const *uobj);
+ string(noref_t, ::ucl_object_t *uobj);
+
+ // Return the value of this string.
+ [[nodiscard]] auto value(this string const &self) -> contained_type;
+
+ // Return the size of this string.
+ [[nodiscard]] auto size(this string const &self) -> size_type;
+
+ // Test if this string is empty.
+ [[nodiscard]] auto empty(this string const &self) -> bool;
+
+ // Access this string's data
+ [[nodiscard]] auto data(this string const &self) -> pointer;
+
+ // Iterator access
+ [[nodiscard]] auto begin(this string const &self) -> iterator;
+ [[nodiscard]] auto end(this string const &self) -> iterator;
+};
+
+/*
+ * String constructors. These return an error instead of throwing.
+ */
+
+// Empty string
+export [[nodiscard]] auto
+make_string() -> std::expected<string, error>;
+
+// From string_view
+export [[nodiscard]] auto
+make_string(std::string_view) -> std::expected<string, error>;
+
+// From C literal
+export [[nodiscard]] auto
+make_string(char const *) -> std::expected<string, error>;
+
+// From contiguous range
+export template<std::ranges::contiguous_range Range>
+requires (!std::same_as<std::string_view, Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+[[nodiscard]] auto make_string(Range &&range)
+{
+ return make_string(std::string_view(range));
+}
+
+// From non-contiguous range
+export template<std::ranges::range Range>
+requires (!std::ranges::contiguous_range<Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+[[nodiscard]] auto make_string(Range &&range)
+{
+ return make_string(std::string(std::from_range, range));
+}
+
+// From iterator pair
+export template<std::input_iterator Iterator>
+requires (std::same_as<char, std::iter_value_t<Iterator>>)
+[[nodiscard]] auto make_string(Iterator first, Iterator last)
+{
+ return make_string(std::ranges::subrange(first, last));
+}
+
+/*
+ * Comparison operators.
+ */
+
+export [[nodiscard]] auto operator== (string const &a, string const &b) -> bool;
+export [[nodiscard]] auto operator<=> (string const &a, string const &b)
+ -> std::strong_ordering;
+
+/*
+ * For convenience, allow comparison with C++ strings without having to
+ * construct a temporary UCL object.
+ */
+
+export [[nodiscard]] auto operator==(string const &lhs,
+ std::string_view rhs) -> bool;
+
+export [[nodiscard]] auto operator==(string const &lhs,
+ std::string const &rhs) -> bool;
+
+export [[nodiscard]] auto operator==(string const &lhs,
+ char const *rhs) -> bool;
+
+export [[nodiscard]] auto operator<=>(string const &lhs,
+ std::string_view rhs)
+ -> std::strong_ordering;
+
+export [[nodiscard]] auto operator<=>(string const &lhs,
+ std::string const &rhs)
+ -> std::strong_ordering;
+
+export [[nodiscard]] auto operator<=>(string const &lhs,
+ char const *rhs)
+ -> std::strong_ordering;
+
+/*
+ * Print a string to a stream.
+ */
+export auto operator<<(std::ostream &, string const &) -> std::ostream &;
+
+/*
+ * Literal operator.
+ */
+inline namespace literals {
+ export constexpr auto operator""_ucl (char const *s, std::size_t n)
+ -> string
+ {
+ return string(std::string_view(s, n));
+ }
+} // namespace nihil::ucl::literals
+
+} // namespace nihil::ucl
+
+namespace nihil { inline namespace literals {
+ export using namespace ::nihil::ucl::literals;
+}} // namespace nihil::literals
+
+/*
+ * std::formatter for a string. This provides the same format operations
+ * as std::formatter<std::string_view>.
+ */
+export template<>
+struct std::formatter<nihil::ucl::string, char>
+{
+ std::formatter<std::string_view> base_formatter;
+
+ template<class ParseContext>
+ constexpr ParseContext::iterator parse(ParseContext& ctx)
+ {
+ return base_formatter.parse(ctx);
+ }
+
+ template<class FmtContext>
+ FmtContext::iterator format(nihil::ucl::string const &o,
+ FmtContext& ctx) const
+ {
+ return base_formatter.format(o.value(), ctx);
+ }
+};