1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <iosfwd>
34 #include <string>
35 
36 #include "mongo/base/error_codes.h"
37 #include "mongo/platform/atomic_word.h"
38 #include "mongo/platform/compiler.h"
39 
40 namespace mongoutils {
41 namespace str {
42 class stream;
43 }  // namespace str
44 }  // namespace mongoutils
45 
46 namespace mongo {
47 
48 /**
49  * Status represents an error state or the absence thereof.
50  *
51  * A Status uses the standardized error codes -- from file 'error_codes.h' -- to
52  * determine an error's cause. It further clarifies the error with a textual
53  * description.
54  *
55  * Example usage:
56  *
57  *    Status sumAB(int a, int b, int* c) {
58  *       if (overflowIfSum(a,b)) {
59  *           return Status(ErrorCodes::ERROR_OVERFLOW, "overflow in sumAB", 16494);
60  *       }
61  *
62  *       *c = a+b;
63  *       return Status::OK();
64  *   }
65  */
66 class MONGO_WARN_UNUSED_RESULT_CLASS Status {
67 public:
68     // Short-hand for returning an OK status.
69     static inline Status OK();
70 
71     /**
72      * Builds an error status given the error code and a textual description of what
73      * caused the error.
74      *
75      * For OK Statuses prefer using Status::OK(). If code is OK, the remaining arguments are
76      * ignored.
77      *
78      * For adding context to the reason string, use withContext/addContext rather than making a new
79      * Status manually.
80      */
81     MONGO_COMPILER_COLD_FUNCTION Status(ErrorCodes::Error code, std::string reason);
82     MONGO_COMPILER_COLD_FUNCTION Status(ErrorCodes::Error code, const char* reason);
83     MONGO_COMPILER_COLD_FUNCTION Status(ErrorCodes::Error code, StringData reason);
84     MONGO_COMPILER_COLD_FUNCTION Status(ErrorCodes::Error code,
85                                         const mongoutils::str::stream& reason);
86 
87     inline Status(const Status& other);
88     inline Status& operator=(const Status& other);
89 
90     inline Status(Status&& other) noexcept;
91     inline Status& operator=(Status&& other) noexcept;
92 
93     inline ~Status();
94 
95     /**
96      * Returns a new Status with the same data as this, but with the reason string prefixed with
97      * reasonPrefix and our standard " :: caused by :: " separator. The new reason is not visible to
98      * any other Statuses that share the same ErrorInfo object.
99      *
100      * No-op when called on an OK status.
101      */
102     Status withContext(StringData reasonPrefix) const;
addContext(StringData reasonPrefix)103     void addContext(StringData reasonPrefix) {
104         *this = this->withContext(reasonPrefix);
105     }
106 
107     /**
108      * Only compares codes. Ignores reason strings.
109      */
110     bool operator==(const Status& other) const {
111         return code() == other.code();
112     }
113     bool operator!=(const Status& other) const {
114         return !(*this == other);
115     }
116 
117     /**
118      * Compares this Status's code with an error code.
119      */
120     bool operator==(const ErrorCodes::Error other) const {
121         return code() == other;
122     }
123     bool operator!=(const ErrorCodes::Error other) const {
124         return !(*this == other);
125     }
126 
127     //
128     // accessors
129     //
130 
131     inline bool isOK() const;
132 
133     inline ErrorCodes::Error code() const;
134 
135     inline std::string codeString() const;
136 
137 
138     /**
139      * Returns the reason string or the empty string if isOK().
140      */
reason()141     const std::string& reason() const {
142         if (_error)
143             return _error->reason;
144 
145         static const std::string empty;
146         return empty;
147     }
148 
149     std::string toString() const;
150 
151     /**
152      * Returns true if this Status's code is a member of the given category.
153      */
154     template <ErrorCategory category>
isA()155     bool isA() const {
156         return ErrorCodes::isA<category>(code());
157     }
158 
159     /**
160      * Call this method to indicate that it is your intention to ignore a returned status. Ignoring
161      * is only possible if the value being ignored is an xvalue -- it is not appropriate to create a
162      * status variable and then ignore it.
163      */
ignore()164     inline void ignore() && noexcept {}
165     inline void ignore() const& noexcept = delete;
166 
167     /**
168      * This method is a transitional tool, to facilitate transition to compile-time enforced status
169      * checking.
170      *
171      * NOTE: DO NOT ADD NEW CALLS TO THIS METHOD. This method serves the same purpose as
172      * `.ignore()`; however, it indicates a situation where the code that presently ignores a status
173      * code has not been audited for correctness. This method will be removed at some point. If you
174      * encounter a compiler error from ignoring the result of a status-returning function be sure to
175      * check the return value, or deliberately ignore the return value.
176      */
transitional_ignore()177     inline void transitional_ignore() && noexcept {};
178     inline void transitional_ignore() const& noexcept = delete;
179 
180     //
181     // Below interface used for testing code only.
182     //
183 
184     inline AtomicUInt32::WordType refCount() const;
185 
186 private:
187     inline Status();
188 
189     struct ErrorInfo {
190         AtomicUInt32 refs;             // reference counter
191         const ErrorCodes::Error code;  // error code
192         const std::string reason;      // description of error cause
193 
194         static ErrorInfo* create(ErrorCodes::Error code, std::string reason);
195 
196         ErrorInfo(ErrorCodes::Error code, std::string reason);
197     };
198 
199     ErrorInfo* _error;
200 
201     /**
202      * Increment/Decrement the reference counter inside an ErrorInfo
203      *
204      * @param error  ErrorInfo to be incremented
205      */
206     static inline void ref(ErrorInfo* error);
207     static inline void unref(ErrorInfo* error);
208 };
209 
210 inline bool operator==(const ErrorCodes::Error lhs, const Status& rhs);
211 
212 inline bool operator!=(const ErrorCodes::Error lhs, const Status& rhs);
213 
214 std::ostream& operator<<(std::ostream& os, const Status& status);
215 
216 }  // namespace mongo
217 
218 #include "mongo/base/status-inl.h"
219