Spicy
result.h
1 // Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <ostream>
6 #include <string>
7 #include <utility>
8 #include <variant>
9 
10 #include <hilti/rt/exception.h>
11 #include <hilti/rt/types/optional.h>
12 
13 namespace hilti::rt {
14 
15 namespace result {
16 
18 class Error {
19 public:
20  Error(std::string_view description = "<no description>", std::string_view context = "")
21  : _description(description), _context(context) {}
22 
23  std::string_view description() const { return _description; }
24  std::string_view context() const { return _context; }
25 
26  operator std::string() const { return _description; }
27  operator std::string_view() const { return _description; }
28 
29 private:
30  std::string _description;
31  std::string _context;
32 };
33 
34 inline std::ostream& operator<<(std::ostream& out, const Error& error) {
35  out << error.description();
36  return out;
37 }
38 
39 inline bool operator==(const Error& a, const Error& b) { return a.description() == b.description(); }
40 inline bool operator!=(const Error& a, const Error& b) { return ! (a == b); }
41 
43 class NoResult : public RuntimeError {
44 public:
45  NoResult(Error err) : RuntimeError(err.description()), _error(std::move(err)) {}
46 
47 private:
48  Error _error;
49 };
50 
52 class NoError : public RuntimeError {
53 public:
54  NoError() : RuntimeError("<no error>") {}
55 };
56 
57 } // namespace result
58 
59 struct Nothing {};
60 
61 inline bool operator==(const Nothing&, const Nothing&) { return true; }
62 inline bool operator!=(const Nothing&, const Nothing&) { return false; }
63 
64 namespace detail::adl {
65 inline std::string to_string(const Nothing& /*n*/, adl::tag /*unused*/) { return "<nothing>"; };
66 } // namespace detail::adl
67 
72 template<typename T>
73 class Result {
74 public:
75  Result() : _value(std::in_place_type_t<result::Error>(), result::Error("<result not initialized>")) {}
76 
78  Result(const T& t) : _value(std::in_place_type_t<T>(), t) {}
80  Result(T&& t) : _value(std::in_place_type_t<T>(), std::move(t)) {}
82  Result(const result::Error& e) : _value(std::in_place_type_t<result::Error>(), e) {}
84  Result(result::Error&& e) : _value(std::in_place_type_t<result::Error>(), std::move(e)) {}
85 
86  Result(const Result& o) = default;
87  Result(Result&& o) = default; // NOLINT (hicpp-noexcept-move)
88  ~Result() = default;
89 
96  const T& value() const& { return std::get<T>(_value); }
97 
104  T& value() & { return std::get<T>(_value); }
105 
112  T value() && { return std::get<T>(std::move(_value)); }
113 
121  template<typename E = result::NoResult>
122  const T& valueOrThrow() const& {
123  if ( ! hasValue() )
124  throw E(error());
125 
126  return value();
127  }
128 
136  template<typename E = result::NoResult>
137  T& valueOrThrow() & {
138  if ( ! hasValue() )
139  throw E(error());
140 
141  return value();
142  }
143 
151  template<typename E = result::NoResult>
152  T valueOrThrow() && {
153  if ( ! hasValue() )
154  throw E(error());
155 
156  return value();
157  }
158 
165  const result::Error& error() const& { return std::get<result::Error>(_value); }
166 
173  result::Error error() && { return std::get<result::Error>(std::move(_value)); }
174 
181  const result::Error& errorOrThrow() const {
182  if ( hasValue() )
183  throw result::NoError();
184 
185  return error();
186  }
187 
189  bool hasValue() const { return std::holds_alternative<T>(_value); }
190 
192  const T& operator*() const& { return value(); }
194  T& operator*() & { return value(); }
196  T operator*() && { return value(); }
198  const T* operator->() const& { return std::get_if<T>(&_value); }
200  T* operator->() & { return std::get_if<T>(&_value); }
202  T* operator->() && = delete;
203 
205  explicit operator bool() const { return hasValue(); }
206 
207  Result& operator=(const Result& other) = default;
208  Result& operator=(Result&& other) = default; // NOLINT (hicpp-noexcept-move)
209 
210  friend bool operator==(const Result& a, const Result& b) {
211  if ( a.hasValue() != b.hasValue() )
212  return false;
213 
214  if ( a.hasValue() )
215  return a.value() == b.value();
216  else
217  return a.error() == b.error();
218  }
219 
220  friend bool operator!=(const Result& a, const Result& b) { return ! (a == b); }
221 
222 private:
223  std::variant<T, result::Error> _value;
224 };
225 
227 template<typename T>
229  return Result<T>(std::forward<T>(t));
230 }
231 
232 } // namespace hilti::rt
Definition: result.h:73
Result(const result::Error &e)
Definition: result.h:82
T & value() &
Definition: result.h:104
const T * operator->() const &
Definition: result.h:198
T value() &&
Definition: result.h:112
const result::Error & error() const &
Definition: result.h:165
T * operator->() &
Definition: result.h:200
const T & valueOrThrow() const &
Definition: result.h:122
const result::Error & errorOrThrow() const
Definition: result.h:181
bool hasValue() const
Definition: result.h:189
T & operator*() &
Definition: result.h:194
result::Error error() &&
Definition: result.h:173
T * operator->() &&=delete
T operator*() &&
Definition: result.h:196
T valueOrThrow() &&
Definition: result.h:152
const T & value() const &
Definition: result.h:96
Result(const T &t)
Definition: result.h:78
Result(T &&t)
Definition: result.h:80
const T & operator*() const &
Definition: result.h:192
T & valueOrThrow() &
Definition: result.h:137
Result(result::Error &&e)
Definition: result.h:84
Definition: result.h:18
Definition: result.h:52
Definition: result.h:43
Definition: any.h:7
std::string to_string(T &&x)
Definition: extension-points.h:26
Result< T > make_result(T &&t)
Definition: result.h:228
Definition: result.h:59