aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.error/error.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.error/error.ccm')
-rw-r--r--nihil.error/error.ccm322
1 files changed, 0 insertions, 322 deletions
diff --git a/nihil.error/error.ccm b/nihil.error/error.ccm
deleted file mode 100644
index 833f7f1..0000000
--- a/nihil.error/error.ccm
+++ /dev/null
@@ -1,322 +0,0 @@
-// This source code is released into the public domain.
-export module nihil.error;
-
-// error: a type representing an error.
-//
-// An error consists of an immediate cause, which may be a string or
-// std:error_code, and an optional proximate cause, which is another error
-// object. Any number of error objects may be stacked.
-//
-// For example, a failure to open a file might be a stack of two errors:
-//
-// - string, "failed to open /etc/somefile",
-// - std::error_code, "No such file or directory".
-//
-// Calling .str() will format the entire stack starting at that error,
-// for example: "failed to open /etc/somefile: No such file or directory".
-//
-// Errors may be moved and (relatively) cheaply copied, since the cause
-// chain is refcounted.
-//
-// error derives from std::exception, so it may be thrown and caught and
-// provides a useful what(). When throwing errors, creating a derived
-// error will make it easier to distinguish errors when catching them.
-
-import nihil.std;
-import nihil.match;
-
-export import :sys_error;
-
-namespace nihil {
-
-// Things which can be errors.
-using error_t = std::variant<std::monostate, std::string, std::error_code, std::error_condition>;
-
-// A proxy class used when constructing errors. This has implicit constructors from various types,
-// which means we don't have to handle every possible combination of types in error itself.
-export struct error_proxy
-{
- // Construct from...
-
- // ... a string_view
- error_proxy(std::string_view const what) // NOLINT
- : m_error(std::string(what))
- {
- }
-
- // ... an std::string; so we can move the string into place if it's an rvalue.
- error_proxy(auto &&what) // NOLINT
- requires(std::same_as<std::remove_cvref_t<decltype(what)>, std::string>)
- : m_error(std::forward<decltype(what)>(what))
- {
- }
-
- // ... a C string
- error_proxy(char const *what) // NOLINT
- : m_error(std::string(what))
- {
- }
-
- // ... an std::error_code
- error_proxy(std::error_code const what) // NOLINT
- : m_error(what)
- {
- }
-
- // ... an std::error_condition
- error_proxy(std::error_condition const what) // NOLINT
- : m_error(what)
- {
- }
-
- // ... an error code enum
- template <typename T>
- requires(std::is_error_code_enum<T>::value)
- error_proxy(T what) // NOLINT
- : m_error(make_error_code(what))
- {
- }
-
- // ... an error condition enum
- template <typename T>
- requires(std::is_error_condition_enum<T>::value)
- error_proxy(T what) // NOLINT
- : m_error(make_error_condition(what))
- {
- }
-
- // Not copyable.
- error_proxy(error_proxy const &) = delete;
- auto operator=(error_proxy const &) -> error_proxy & = delete;
-
- // Not movable.
- error_proxy(error_proxy &&) = delete;
- auto operator=(error_proxy &&) -> error_proxy & = delete;
-
- ~error_proxy() = default;
-
- // Let error extract the error_t.
- [[nodiscard]] auto error() && -> error_t &&
- {
- return std::move(m_error);
- }
-
-private:
- // The error.
- error_t m_error;
-};
-
-// The error class.
-export struct error : std::exception
-{
- // Create an empty error, representing success.
- error() = default;
-
- // Destroy an error.
- ~error() override = default;
-
- // Create an error from an error proxy.
- explicit error(error_proxy &&proxy)
- : m_error(std::move(std::move(proxy).error()))
- {
- }
-
- // Create an error from an error proxy and an error cause.
- error(error_proxy &&proxy, error cause)
- : m_error(std::move(std::move(proxy).error()))
- , m_cause(std::make_shared<error>(std::move(cause)))
- {
- }
-
- // Create an error from an error proxy and an error_proxy cause.
- // For example, error("cannot open file", std::errc::permission_denied).
- error(error_proxy &&proxy, error_proxy &&cause)
- : m_error(std::move(std::move(proxy).error()))
- , m_cause(std::make_shared<error>(std::move(cause)))
- {
- }
-
- // Copyable.
- error(error const &) = default;
- auto operator=(error const &) -> error & = default;
-
- // Movable.
- error(error &&) noexcept = default;
- auto operator=(error &&) noexcept -> error & = default;
-
- // Return the cause of this error.
- [[nodiscard]] auto cause(this error const &self) -> std::shared_ptr<error> const &
- {
- return self.m_cause;
- }
-
- // Return the root cause of this error, which may be this object.
- // For errors caused by an OS error, this will typically be the
- // error_code error.
- [[nodiscard]] auto root_cause(this error const &self) -> error const &
- {
- auto const *cause = &self;
- while (cause->m_cause)
- cause = cause->m_cause.get();
- return *cause;
- }
-
- // Format this error without its cause as a string.
- [[nodiscard]] auto this_str(this error const &self) -> std::string
- {
- return self.m_error | match {
- [] (std::monostate) -> std::string {
- return "No error";
- },
- [] (std::error_code const &m) {
- return m.message();
- },
- [] (std::error_condition const &m) {
- return m.message();
- },
- [] (std::string const &m) {
- return m;
- }
- };
- }
-
- // Format this error and its cause as a string.
- [[nodiscard]] auto full_str(this error const &self) -> std::string
- {
- auto str = self.this_str();
-
- auto cause = self.cause();
- while (cause) {
- str += ": " + cause->this_str();
- cause = cause->cause();
- }
-
- return str;
- }
-
- // Return this error's error_code, if any.
- [[nodiscard]] auto code(this error const &self) -> std::optional<std::error_code>
- {
- auto const *code = std::get_if<std::error_code>(&self.m_error);
- if (code != nullptr)
- return {*code};
- return {};
- }
-
- // Return this error's error_condition, if any.
- [[nodiscard]] auto condition(this error const &self) -> std::optional<std::error_condition>
- {
- auto const *condition = std::get_if<std::error_condition>(&self.m_error);
- if (condition != nullptr)
- return {*condition};
- return {};
- }
-
- // Format this error and its cause as a C string and return it. This is for
- // compatibility with std::exception. The lifetime of the returned string
- // is the same as the error object.
- [[nodiscard]] auto what() const noexcept -> char const * final
- {
- if (!m_what)
- m_what = this->full_str();
- return m_what->c_str();
- }
-
- // Allow error to be implicitly converted to std::expected and std::unexpected, to make
- // using it with std::expected easier.
-
- template <typename T>
- operator std::expected<T, error> () && // NOLINT
- {
- return std::unexpected{std::move(*this)};
- }
-
- template<typename T>
- operator std::expected<T, error> () const // NOLINT
- {
- return std::unexpected{*this};
- }
-
- operator std::unexpected<error> () && // NOLINT
- {
- return std::unexpected{std::move(*this)};
- }
-
- operator std::unexpected<error> () const // NOLINT
- {
- return std::unexpected{*this};
- }
-
-private:
- // This error.
- error_t m_error = make_error_code(std::errc());
-
- // The cause of this error, if any.
- std::shared_ptr<error> m_cause;
-
- // For std::exception::what(), we need to keep the string valid
- // until we're destroyed.
- mutable std::optional<std::string> m_what;
-
- // Equality comparison.
- [[nodiscard]] friend auto operator==(error const &lhs, error const &rhs) -> bool
- {
- return lhs.m_error == rhs.m_error;
- }
-
- [[nodiscard]] friend auto operator<=>(error const &lhs, error const &rhs) -> std::strong_ordering
- {
- return lhs.m_error <=> rhs.m_error;
- }
-
- // Compare an error with an std::error_code.
- [[nodiscard]] friend auto operator==(error const &lhs, std::error_code const &rhs) -> bool
- {
- return lhs.code() == rhs;
- }
-
- // Compare an error to an std::error_condition.
- [[nodiscard]] friend auto operator==(error const &lhs, std::error_condition const &rhs) -> bool
- {
- return lhs.condition() == rhs;
- }
-
- // Compare an error to an std::error_code enum.
- [[nodiscard]] friend auto operator==(error const &lhs, auto rhs) -> bool
- requires(std::is_error_code_enum<decltype(rhs)>::value)
- {
- return lhs.code() == rhs;
- }
-
- // Compare an error to an std::error_condition enum.
- [[nodiscard]] friend auto operator==(error const &lhs, auto rhs) -> bool
- requires(std::is_error_condition_enum<decltype(rhs)>::value)
- {
- return lhs.condition() == rhs;
- }
-
- // Print an error to a stream.
- friend auto operator<<(std::ostream &strm, error const &e) -> std::ostream &
- {
- return strm << e.full_str();
- }
-};
-
-} // namespace nihil
-
-// Make error formattable.
-export template <>
-struct std::formatter<nihil::error, char>
-{
- template <typename ParseContext>
- constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
- {
- return ctx.begin();
- }
-
- template <typename FormatContext>
- auto format(nihil::error const &e, FormatContext &ctx) const -> FormatContext::iterator
- {
- return std::ranges::copy(e.full_str(), ctx.out()).out;
- }
-};