1616
1717//
1818
19+ #include < bsoncxx/v1/array/value.hpp>
20+ #include < bsoncxx/v1/document/value.hpp>
21+ #include < bsoncxx/v1/document/view.hpp>
22+ #include < bsoncxx/v1/stdx/string_view.hpp>
23+ #include < bsoncxx/v1/types/id.hpp>
24+ #include < bsoncxx/v1/types/view.hpp>
25+
26+ #include < mongocxx/v1/server_error.hh>
27+
28+ #include < cstring>
1929#include < memory>
2030#include < string>
2131#include < system_error>
2232#include < utility>
2333
34+ #include < bsoncxx/private/bson.hh>
2435#include < bsoncxx/private/immortal.hh>
2536#include < bsoncxx/private/make_unique.hh>
2637
38+ #include < mongocxx/private/mongoc.hh>
39+
2740namespace mongocxx {
2841namespace v1 {
2942
43+ namespace {
44+
45+ bool common_equivalence (v1::source_errc errc, int v, std::error_condition const & ec) noexcept {
46+ if (ec.category () == v1::source_error_category ()) {
47+ return v != 0 ? errc == static_cast <v1::source_errc>(ec.value ()) : false ;
48+ }
49+
50+ if (ec.category () == v1::type_error_category ()) {
51+ return v != 0 ? v1::type_errc::runtime_error == static_cast <v1::type_errc>(ec.value ()) : false ;
52+ }
53+
54+ return false ;
55+ }
56+
57+ std::error_category const & mongoc_error_category () {
58+ class type final : public std::error_category {
59+ char const * name () const noexcept override {
60+ return " mongoc_error_code_t" ;
61+ }
62+
63+ std::string message (int v) const noexcept override {
64+ return std::string (this ->name ()) + ' :' + std::to_string (v);
65+ }
66+
67+ bool equivalent (int v, std::error_condition const & ec) const noexcept override {
68+ return common_equivalence (v1::source_errc::mongoc, v, ec);
69+ }
70+ };
71+
72+ static bsoncxx::immortal<type> const instance;
73+
74+ return instance.value ();
75+ }
76+
77+ std::error_category const & mongocrypt_error_category () {
78+ class type final : public std::error_category {
79+ char const * name () const noexcept override {
80+ return " mongocrypt_status_t" ;
81+ }
82+
83+ std::string message (int v) const noexcept override {
84+ return std::string (this ->name ()) + ' :' + std::to_string (v);
85+ }
86+
87+ bool equivalent (int v, std::error_condition const & ec) const noexcept override {
88+ return common_equivalence (v1::source_errc::mongocrypt, v, ec);
89+ }
90+ };
91+
92+ static bsoncxx::immortal<type> const instance;
93+
94+ return instance.value ();
95+ }
96+
97+ std::error_category const & unknown_error_category () {
98+ class type final : public std::error_category {
99+ char const * name () const noexcept override {
100+ return " unknown" ;
101+ }
102+
103+ std::string message (int v) const noexcept override {
104+ return std::string (this ->name ()) + ' :' + std::to_string (v);
105+ }
106+
107+ bool equivalent (int v, std::error_condition const & ec) const noexcept override {
108+ if (ec.category () == v1::source_error_category ()) {
109+ return false ;
110+ }
111+
112+ if (ec.category () == v1::type_error_category ()) {
113+ return v != 0 ? v1::type_errc::runtime_error == static_cast <v1::type_errc>(ec.value ()) : false ;
114+ }
115+
116+ return false ;
117+ }
118+ };
119+
120+ static bsoncxx::immortal<type> const instance;
121+
122+ return instance.value ();
123+ }
124+
125+ } // namespace
126+
30127std::error_category const & source_error_category () {
31128 class type final : public std::error_category {
32129 char const * name () const noexcept override {
@@ -85,7 +182,22 @@ std::error_category const& type_error_category() {
85182 return instance.value ();
86183}
87184
88- class exception ::impl {};
185+ class exception ::impl {
186+ public:
187+ bsoncxx::v1::array::value _error_labels;
188+ };
189+
190+ bool exception::has_error_label (bsoncxx::v1::stdx::string_view label) const {
191+ for (auto const & e : _impl->_error_labels ) {
192+ if (e.type_view () == bsoncxx::v1::types::b_string{label}) {
193+ return true ;
194+ }
195+ }
196+ return false ;
197+ }
198+
199+ exception::exception (std::error_code ec, char const * message, std::unique_ptr<impl> impl)
200+ : std::system_error{ec, message}, _impl{std::move (impl)} {}
89201
90202exception::exception (std::error_code ec, std::unique_ptr<impl> impl) : std::system_error{ec}, _impl{std::move (impl)} {}
91203
@@ -99,9 +211,130 @@ exception::exception(std::error_code ec, std::unique_ptr<impl> impl) : std::syst
99211// vtable.
100212void exception::key_function () const {}
101213
214+ namespace {
215+
216+ v1::exception make_exception (bson_error_t const & error) {
217+ auto const code = static_cast <int >(error.code );
218+ auto const raw_category = static_cast <int >(error.reserved );
219+ auto const message = static_cast <char const *>(error.message );
220+ auto const has_message = message[0 ] != ' \0 ' ;
221+
222+ // Undocumented: see mongoc-error-private.h.
223+ switch (raw_category) {
224+ // MONGOC_ERROR_CATEGORY_BSON / BSON_ERROR_CATEGORY
225+ // Unlikely. Convert to MONGOC_ERROR_BSON_INVALID (18).
226+ case 1 : {
227+ std::string what;
228+ what += " bson error code " ;
229+ what += std::to_string (code);
230+ if (has_message) {
231+ what += " : " ;
232+ what += message;
233+ }
234+ return v1::exception::internal::make (MONGOC_ERROR_BSON_INVALID, mongoc_error_category (), what.c_str ());
235+ }
236+
237+ // MONGOC_ERROR_CATEGORY
238+ case 2 : {
239+ if (has_message) {
240+ return v1::exception::internal::make (code, mongoc_error_category (), message);
241+ } else {
242+ return v1::exception::internal::make (code, mongoc_error_category ());
243+ }
244+ }
245+
246+ // MONGOC_ERROR_CATEGORY_SERVER
247+ // Unlikely. Throw as `v1::exception` but use the correct error category.
248+ case 3 : {
249+ if (has_message) {
250+ return v1::exception::internal::make (code, v1::server_error::internal::category (), message);
251+ } else {
252+ return v1::exception::internal::make (code, v1::server_error::internal::category ());
253+ }
254+ }
255+
256+ // MONGOC_ERROR_CATEGORY_CRYPT
257+ case 4 : {
258+ if (has_message) {
259+ return v1::exception::internal::make (code, mongocrypt_error_category (), message);
260+ } else {
261+ return v1::exception::internal::make (code, mongocrypt_error_category ());
262+ }
263+ }
264+
265+ // MONGOC_ERROR_CATEGORY_SASL
266+ // Unlikely. Convert to MONGOC_ERROR_CLIENT_AUTHENTICATE (11).
267+ case 5 : {
268+ std::string what;
269+ what += " sasl error code " ;
270+ what += std::to_string (code);
271+ if (has_message) {
272+ what += " : " ;
273+ what += message;
274+ }
275+ return v1::exception::internal::make (
276+ MONGOC_ERROR_CLIENT_AUTHENTICATE, mongoc_error_category (), what.c_str ());
277+ }
278+
279+ // Unlikely.
280+ default : {
281+ std::string what;
282+ what += " unknown error category " ;
283+ what += std::to_string (raw_category);
284+ if (has_message) {
285+ what += " : " ;
286+ what += message;
287+ }
288+
289+ return v1::exception::internal::make (code, unknown_error_category (), what.c_str ());
290+ }
291+ }
292+ }
293+
294+ } // namespace
295+
102296exception exception::internal::make (std::error_code ec) {
103297 return {ec, bsoncxx::make_unique<impl>()};
104298}
105299
300+ exception exception::internal::make (int code, std::error_category const & category, char const * message) {
301+ return {std::error_code{code, category}, message, bsoncxx::make_unique<impl>()};
302+ }
303+
304+ exception exception::internal::make (int code, std::error_category const & category) {
305+ return {std::error_code{code, category}, bsoncxx::make_unique<impl>()};
306+ }
307+
308+ void exception::internal::set_error_labels (exception& self, bsoncxx::v1::document::view v) {
309+ auto & _error_labels = self._impl ->_error_labels ;
310+
311+ auto const e = v[" errorLabels" ];
312+
313+ if (e && e.type_id () == bsoncxx::v1::types::id::k_array) {
314+ _error_labels = e.get_array ().value ;
315+ } else {
316+ _error_labels = bsoncxx::v1::array::value{};
317+ }
318+ }
319+
320+ void throw_exception (bson_error_t const & error) {
321+ throw make_exception (error);
322+ }
323+
324+ void throw_exception (bson_error_t const & error, bsoncxx::v1::document::value doc) {
325+ // Server-side error.
326+ if (auto const code = doc[" code" ]) {
327+ if (code.type_id () == bsoncxx::v1::types::id::k_int32) {
328+ auto const ex = make_exception (error);
329+ throw v1::server_error::internal::make (int {code.get_int32 ().value }, ex.what (), std::move (doc), ex.code ());
330+ }
331+ }
332+
333+ // Client-side error.
334+ auto ex = make_exception (error);
335+ exception::internal::set_error_labels (ex, doc);
336+ throw ex;
337+ }
338+
106339} // namespace v1
107340} // namespace mongocxx
0 commit comments