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