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 <boost/optional.hpp>
34 #include <iosfwd>
35 #include <type_traits>
36 #include <utility>
37 
38 #include "mongo/base/static_assert.h"
39 #include "mongo/base/status.h"
40 #include "mongo/platform/compiler.h"
41 
42 #define MONGO_INCLUDE_INVARIANT_H_WHITELISTED
43 #include "mongo/util/invariant.h"
44 #undef MONGO_INCLUDE_INVARIANT_H_WHITELISTED
45 
46 
47 namespace mongo {
48 
49 template <typename T>
50 class StatusWith;
51 
52 // Using extern constexpr to prevent the compiler from allocating storage as a poor man's c++17
53 // inline constexpr variable.
54 // TODO delete extern in c++17 because inline is the default for constexper variables.
55 template <typename T>
56 extern constexpr bool isStatusWith = false;
57 template <typename T>
58 extern constexpr bool isStatusWith<StatusWith<T>> = true;
59 
60 template <typename T>
61 extern constexpr bool isStatusOrStatusWith =
62     std::is_same<T, mongo::Status>::value || isStatusWith<T>;
63 
64 template <typename T>
65 using StatusOrStatusWith = std::conditional_t<std::is_void<T>::value, Status, StatusWith<T>>;
66 
67 /**
68  * StatusWith is used to return an error or a value.
69  * This class is designed to make exception-free code cleaner by not needing as many out
70  * parameters.
71  *
72  * Example:
73  * StatusWith<int> fib( int n ) {
74  *   if ( n < 0 )
75  *       return StatusWith<int>( ErrorCodes::BadValue, "parameter to fib has to be >= 0" );
76  *   if ( n <= 1 ) return StatusWith<int>( 1 );
77  *   StatusWith<int> a = fib( n - 1 );
78  *   StatusWith<int> b = fib( n - 2 );
79  *   if ( !a.isOK() ) return a;
80  *   if ( !b.isOK() ) return b;
81  *   return StatusWith<int>( a.getValue() + b.getValue() );
82  * }
83  */
84 template <typename T>
85 class MONGO_WARN_UNUSED_RESULT_CLASS StatusWith {
86     MONGO_STATIC_ASSERT_MSG(!isStatusOrStatusWith<T>,
87                             "StatusWith<Status> and StatusWith<StatusWith<T>> are banned.");
88 
89 public:
90     using value_type = T;
91 
92     /**
93      * for the error case
94      */
StatusWith(ErrorCodes::Error code,StringData reason)95     MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, StringData reason)
96         : _status(code, reason) {}
StatusWith(ErrorCodes::Error code,std::string reason)97     MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, std::string reason)
98         : _status(code, std::move(reason)) {}
StatusWith(ErrorCodes::Error code,const char * reason)99     MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, const char* reason)
100         : _status(code, reason) {}
StatusWith(ErrorCodes::Error code,const mongoutils::str::stream & reason)101     MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code,
102                                             const mongoutils::str::stream& reason)
103         : _status(code, reason) {}
104 
105     /**
106      * for the error case
107      */
StatusWith(Status status)108     MONGO_COMPILER_COLD_FUNCTION StatusWith(Status status) : _status(std::move(status)) {
109         dassert(!isOK());
110     }
111 
112     /**
113      * for the OK case
114      */
StatusWith(T t)115     StatusWith(T t) : _status(Status::OK()), _t(std::move(t)) {}
116 
getValue()117     const T& getValue() const {
118         dassert(isOK());
119         return *_t;
120     }
121 
getValue()122     T& getValue() {
123         dassert(isOK());
124         return *_t;
125     }
126 
getStatus()127     const Status& getStatus() const {
128         return _status;
129     }
130 
isOK()131     bool isOK() const {
132         return _status.isOK();
133     }
134 
135 
136     /**
137      * This method is a transitional tool, to facilitate transition to compile-time enforced status
138      * checking.
139      *
140      * NOTE: DO NOT ADD NEW CALLS TO THIS METHOD. This method serves the same purpose as
141      * `.getStatus().ignore()`; however, it indicates a situation where the code that presently
142      * ignores a status code has not been audited for correctness. This method will be removed at
143      * some point. If you encounter a compiler error from ignoring the result of a `StatusWith`
144      * returning function be sure to check the return value, or deliberately ignore the return
145      * value. The function is named to be auditable independently from unaudited `Status` ignore
146      * cases.
147      */
status_with_transitional_ignore()148     inline void status_with_transitional_ignore() && noexcept {};
149     inline void status_with_transitional_ignore() const& noexcept = delete;
150 
151 private:
152     Status _status;
153     boost::optional<T> _t;
154 };
155 
156 template <typename T, typename... Args>
makeStatusWith(Args &&...args)157 StatusWith<T> makeStatusWith(Args&&... args) {
158     return StatusWith<T>{T(std::forward<Args>(args)...)};
159 }
160 
161 template <typename T>
162 std::ostream& operator<<(std::ostream& stream, const StatusWith<T>& sw) {
163     if (sw.isOK())
164         return stream << sw.getValue();
165     return stream << sw.getStatus();
166 }
167 
168 //
169 // EqualityComparable(StatusWith<T>, T). Intentionally not providing an ordering relation.
170 //
171 
172 template <typename T>
173 bool operator==(const StatusWith<T>& sw, const T& val) {
174     return sw.isOK() && sw.getValue() == val;
175 }
176 
177 template <typename T>
178 bool operator==(const T& val, const StatusWith<T>& sw) {
179     return sw.isOK() && val == sw.getValue();
180 }
181 
182 template <typename T>
183 bool operator!=(const StatusWith<T>& sw, const T& val) {
184     return !(sw == val);
185 }
186 
187 template <typename T>
188 bool operator!=(const T& val, const StatusWith<T>& sw) {
189     return !(val == sw);
190 }
191 
192 //
193 // EqualityComparable(StatusWith<T>, Status)
194 //
195 
196 template <typename T>
197 bool operator==(const StatusWith<T>& sw, const Status& status) {
198     return sw.getStatus() == status;
199 }
200 
201 template <typename T>
202 bool operator==(const Status& status, const StatusWith<T>& sw) {
203     return status == sw.getStatus();
204 }
205 
206 template <typename T>
207 bool operator!=(const StatusWith<T>& sw, const Status& status) {
208     return !(sw == status);
209 }
210 
211 template <typename T>
212 bool operator!=(const Status& status, const StatusWith<T>& sw) {
213     return !(status == sw);
214 }
215 
216 //
217 // EqualityComparable(StatusWith<T>, ErrorCode)
218 //
219 
220 template <typename T>
221 bool operator==(const StatusWith<T>& sw, const ErrorCodes::Error code) {
222     return sw.getStatus() == code;
223 }
224 
225 template <typename T>
226 bool operator==(const ErrorCodes::Error code, const StatusWith<T>& sw) {
227     return code == sw.getStatus();
228 }
229 
230 template <typename T>
231 bool operator!=(const StatusWith<T>& sw, const ErrorCodes::Error code) {
232     return !(sw == code);
233 }
234 
235 template <typename T>
236 bool operator!=(const ErrorCodes::Error code, const StatusWith<T>& sw) {
237     return !(code == sw);
238 }
239 
240 }  // namespace mongo
241