diff options
Diffstat (limited to 'nihil.ucl/string.ccm')
| -rw-r--r-- | nihil.ucl/string.ccm | 229 |
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); + } +}; |
