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