aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/error.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/error.hh')
-rw-r--r--src/libutil/error.hh186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
new file mode 100644
index 000000000..1e6102ce1
--- /dev/null
+++ b/src/libutil/error.hh
@@ -0,0 +1,186 @@
+#pragma once
+
+
+#include "ref.hh"
+#include "types.hh"
+
+#include <cstring>
+#include <list>
+#include <memory>
+#include <map>
+#include <optional>
+
+#include "fmt.hh"
+
+/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
+ * its (virtual) destructor and what() in c++11 mode, in violation of spec
+ */
+#ifdef __GNUC__
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#define EXCEPTION_NEEDS_THROW_SPEC
+#endif
+#endif
+
+namespace nix {
+
+/*
+
+This file defines two main structs/classes used in nix error handling.
+
+ErrorInfo provides a standard payload of error information, with conversion to string
+happening in the logger rather than at the call site.
+
+BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
+an ErrorInfo.
+
+ErrorInfo structs are sent to the logger as part of an exception, or directly with the
+logError or logWarning macros.
+
+See the error-demo.cc program for usage examples.
+
+*/
+
+typedef enum {
+ lvlError = 0,
+ lvlWarn,
+ lvlInfo,
+ lvlTalkative,
+ lvlChatty,
+ lvlDebug,
+ lvlVomit
+} Verbosity;
+
+// ErrPos indicates the location of an error in a nix file.
+struct ErrPos {
+ int line = 0;
+ int column = 0;
+ string file;
+
+ operator bool() const
+ {
+ return line != 0;
+ }
+
+ // convert from the Pos struct, found in libexpr.
+ template <class P>
+ ErrPos& operator=(const P &pos)
+ {
+ line = pos.line;
+ column = pos.column;
+ file = pos.file;
+ return *this;
+ }
+
+ template <class P>
+ ErrPos(const P &p)
+ {
+ *this = p;
+ }
+};
+
+struct NixCode {
+ ErrPos errPos;
+ std::optional<string> prevLineOfCode;
+ std::optional<string> errLineOfCode;
+ std::optional<string> nextLineOfCode;
+};
+
+struct ErrorInfo {
+ Verbosity level;
+ string name;
+ string description;
+ std::optional<hintformat> hint;
+ std::optional<NixCode> nixCode;
+
+ static std::optional<string> programName;
+};
+
+std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo);
+
+/* BaseError should generally not be caught, as it has Interrupted as
+ a subclass. Catch Error instead. */
+class BaseError : public std::exception
+{
+protected:
+ string prefix_; // used for location traces etc.
+ mutable ErrorInfo err;
+
+ mutable std::optional<string> what_;
+ const string& calcWhat() const;
+
+public:
+ unsigned int status = 1; // exit status
+
+ template<typename... Args>
+ BaseError(unsigned int status, const Args & ... args)
+ : err { .level = lvlError,
+ .hint = hintfmt(args...)
+ }
+ , status(status)
+ { }
+
+ template<typename... Args>
+ BaseError(const std::string & fs, const Args & ... args)
+ : err { .level = lvlError,
+ .hint = hintfmt(fs, args...)
+ }
+ { }
+
+ BaseError(hintformat hint)
+ : err { .level = lvlError,
+ .hint = hint
+ }
+ { }
+
+ BaseError(ErrorInfo && e)
+ : err(std::move(e))
+ { }
+
+ BaseError(const ErrorInfo & e)
+ : err(e)
+ { }
+
+ virtual const char* sname() const { return "BaseError"; }
+
+#ifdef EXCEPTION_NEEDS_THROW_SPEC
+ ~BaseError() throw () { };
+ const char * what() const throw () { return calcWhat().c_str(); }
+#else
+ const char * what() const noexcept override { return calcWhat().c_str(); }
+#endif
+
+ const string & msg() const { return calcWhat(); }
+ const string & prefix() const { return prefix_; }
+ BaseError & addPrefix(const FormatOrString & fs);
+
+ const ErrorInfo & info() { calcWhat(); return err; }
+};
+
+#define MakeError(newClass, superClass) \
+ class newClass : public superClass \
+ { \
+ public: \
+ using superClass::superClass; \
+ virtual const char* sname() const override { return #newClass; } \
+ }
+
+MakeError(Error, BaseError);
+
+class SysError : public Error
+{
+public:
+ int errNo;
+
+ template<typename... Args>
+ SysError(const Args & ... args)
+ :Error("")
+ {
+ errNo = errno;
+ auto hf = hintfmt(args...);
+ err.hint = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
+ }
+
+ virtual const char* sname() const override { return "SysError"; }
+};
+
+}