Asylo
status.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_UTIL_STATUS_H_
20 #define ASYLO_UTIL_STATUS_H_
21 
22 #include <ostream>
23 #include <string>
24 #include <type_traits>
25 #include <utility>
26 
27 #include "absl/meta/type_traits.h"
28 #include "absl/strings/string_view.h"
29 #include "asylo/util/logging.h"
30 #include "asylo/util/error_codes.h" // IWYU pragma: export
31 #include "asylo/util/error_space.h" // IWYU pragma: export
32 #include "asylo/util/status.pb.h"
33 #include "asylo/util/status_error_space.h"
34 #include "asylo/util/status_internal.h"
35 
36 namespace asylo {
37 
38 /// Status contains information about an error. Status contains an error code
39 /// from some error space and a message string suitable for logging or
40 /// debugging.
41 class Status {
42  public:
43  /// Builds an OK Status in the canonical error space.
44  Status();
45 
46  /// Constructs a Status object containing an error code and message.
47  ///
48  /// \param space The ErrorSpace this code belongs to.
49  /// \param code An integer error code.
50  /// \param message The associated error message.
51  Status(const error::ErrorSpace *space, int code, absl::string_view message);
52 
53  /// Constructs a Status object containing an error code and a message. The
54  /// error space is deduced from `code`.
55  ///
56  /// \param code A symbolic error code.
57  /// \param message The associated error message.
58  template <typename Enum>
59  Status(Enum code, absl::string_view message) {
60  Set(code, message);
61  }
62 
63  Status(const Status &other) = default;
64 
65  // Non-default move constructor since the moved status should be set to
66  // indicate an invalid state, which changes the code and error_space.
67  Status(Status &&other);
68 
69  /// Constructs a Status object from `StatusT`. `StatusT` must be a status-type
70  /// object. I.e.,
71  ///
72  /// * It must have a two-parameter constructor that takes an enum as its
73  /// first parameter and a string as its second parameter.
74  /// * It must have non-static error_code(), error_message(), and ok()
75  /// methods.
76  ///
77  /// This constructor is provided for the convenience of Asylo-SDK consumers
78  /// utilizing other status types such as `::grpc::Status`.
79  ///
80  /// \param other A status-like object to copy.
81  template <typename StatusT,
82  typename E = typename absl::enable_if_t<
83  status_internal::status_type_traits<StatusT>::is_status>>
84  explicit Status(const StatusT &other) {
85  Set(status_internal::status_type_traits<StatusT>::CanonicalCode(other),
86  other.error_message());
87  }
88 
89  Status &operator=(const Status &other) = default;
90 
91  // Non-default move assignment operator since the moved status should be set
92  // to indicate an invalid state, which changes the code and error_space.
93  Status &operator=(Status &&other);
94 
95  /// Constructs an OK status object.
96  ///
97  /// \return A Status indicating no error occurred.
98  static Status OkStatus();
99 
100  /// Copy this object to a status type `StatusT`. The method first converts the
101  /// ::asylo::Status object to its canonical form, and then constructs a
102  /// `StatusT` from the error code and message fields of the converted object.
103  /// `StatusT` must be a status-type object. I.e.,
104  ///
105  /// * It must have a two-parameter constructor that takes an enum as its
106  /// first parameter and a string as its second parameter.
107  /// * It must have non-static error_code(), error_message(), and ok()
108  /// methods.
109  ///
110  /// This operator is provided for the convenience of the Asylo SDK users
111  /// that utilize other status types, such as `::grpc::Status`.
112  ///
113  /// \return A status-like object copied from this object.
114  template <typename StatusT,
115  typename E = typename absl::enable_if_t<
116  status_internal::status_type_traits<StatusT>::is_status>>
117  StatusT ToOtherStatus() {
118  Status status = ToCanonical();
119  return StatusT(status_internal::ErrorCodeHolder(status.error_code_),
120  status.message_);
121  }
122 
123  /// Gets the integer error code for this object.
124  ///
125  /// \return The associated integer error code.
126  int error_code() const;
127 
128  /// Gets the string error message for this object.
129  ///
130  /// \return The associated error message.
131  absl::string_view error_message() const;
132 
133  /// Gets the error space for this object.
134  ///
135  /// \return The associated error space.
136  const error::ErrorSpace *error_space() const;
137 
138  /// Indicates whether this object is OK (indicates no error).
139  ///
140  /// \return True if this object indicates no error.
141  bool ok() const;
142 
143  /// Gets a string representation of this object.
144  ///
145  /// Gets a string containing the error space name, error code name, and error
146  /// message if this object is a non-OK Status, or just a string containing the
147  /// error code name if this object is an OK Status.
148  ///
149  /// \return A string representation of this object.
150  std::string ToString() const;
151 
152  /// Gets a copy of this object in the canonical error space.
153  ///
154  /// This operation has no effect if the Status object is already in the
155  /// canonical error space. Otherwise, this translation involves the following:
156  ///
157  /// * Error code is converted to the equivalent error code in the canonical
158  /// error space.
159  /// * The new error message is set to the `ToString()` representation of the
160  /// old Status object in order to preserve the previous error code
161  /// information.
162  Status ToCanonical() const;
163 
164  /// Gets the canonical error code for this object's error code.
165  ///
166  /// \return A canonical `error::GoogleError` code.
167  error::GoogleError CanonicalCode() const;
168 
169  /// Exports the contents of this object into `status_proto`. This method sets
170  /// all fields in `status_proto`.
171  ///
172  /// \param[out] status_proto A protobuf object to populate.
173  void SaveTo(StatusProto *status_proto) const;
174 
175  /// Populates this object using the contents of the given `status_proto`.
176  ///
177  /// If the error space given by `status_proto.space()` is unrecognized, sets
178  /// the error space to the canonical error space and sets the error code using
179  /// the value given by `status_proto.canonical_code()`. If there is no
180  /// canonical code, sets the error code to `error::GoogleError::UNKNOWN`. Note
181  /// that the error message is only set if `status_proto` represents a non-ok
182  /// Status.
183  ///
184  /// If the given `status_proto` is invalid, sets the error code of this
185  /// object to `error::StatusError::INVALID_STATUS_PROTO`. A StatusProto is
186  /// valid if and only if all the following conditions hold:
187  ///
188  /// * If `code()` is 0, then `canonical_code()` is set to 0.
189  /// * If `canonical_code()` is 0, then `code()` is set to 0.
190  /// * If the error space is recognized, then `canonical_code()` is equal to
191  /// the equivalent canonical code given by the error space.
192  ///
193  /// \param status_proto A protobuf object to set this object from.
194  void RestoreFrom(const StatusProto &status_proto);
195 
196  /// Indicates whether this object is the same as `code`.
197  ///
198  /// This object is considered to be the same as `code if `code` matches both
199  /// the error code and error space of this object.
200  ///
201  /// \return True if this object matches `code`.
202  template <typename Enum>
203  bool Is(Enum code) const {
204  return (static_cast<int>(code) == error_code_) &&
205  (error::error_enum_traits<Enum>::get_error_space() == error_space_);
206  }
207 
208  private:
209  // Sets this object to hold an error code |code| and error message |message|.
210  template <typename Enum, typename StringT>
211  void Set(Enum code, StringT &&message) {
212  error_space_ = error::error_enum_traits<Enum>::get_error_space();
213  error_code_ = static_cast<int>(code);
214  if (error_code_ != 0) {
215  message_ = std::string(std::forward<StringT>(message));
216  } else {
217  message_.clear();
218  }
219  }
220 
221  // Returns true if the error code for this object is in the canonical error
222  // space.
223  bool IsCanonical() const;
224 
225  const error::ErrorSpace *error_space_;
226  int error_code_;
227 
228  // An optional error-message if error_code_ is non-zero. If error_code_ is
229  // zero, then message_ is empty.
230  std::string message_;
231 };
232 
233 bool operator==(const Status &lhs, const Status &rhs);
234 
235 bool operator!=(const Status &lhs, const Status &rhs);
236 
237 std::ostream &operator<<(std::ostream &os, const Status &status);
238 
239 /// Checks that the `Status` object in `val` is OK.
240 #define ASYLO_CHECK_OK(val) CHECK_EQ(::asylo::Status::OkStatus(), (val))
241 
242 } // namespace asylo
243 
244 #endif // ASYLO_UTIL_STATUS_H_