diff --git a/src/mongocxx/include/mongocxx/v1/read_concern.hpp b/src/mongocxx/include/mongocxx/v1/read_concern.hpp index 7d6d95084b..d65bf8fb5c 100644 --- a/src/mongocxx/include/mongocxx/v1/read_concern.hpp +++ b/src/mongocxx/include/mongocxx/v1/read_concern.hpp @@ -190,6 +190,11 @@ class read_concern { } /// @} /// + + class internal; + + private: + /* explicit(false) */ read_concern(void* impl); }; } // namespace v1 diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern-fwd.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern-fwd.hpp index 518ff94e69..c4013943d1 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern-fwd.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern-fwd.hpp @@ -14,6 +14,8 @@ #pragma once +#include + #include namespace mongocxx { @@ -26,7 +28,7 @@ class read_concern; namespace mongocxx { -using ::mongocxx::v_noabi::read_concern; +using v_noabi::read_concern; } // namespace mongocxx @@ -36,3 +38,6 @@ using ::mongocxx::v_noabi::read_concern; /// @file /// Declares @ref mongocxx::v_noabi::read_concern. /// +/// @par Includes +/// - @ref mongocxx/v1/read_concern-fwd.hpp +/// diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern.hpp index 9c254ee8b9..cfb669959b 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/read_concern.hpp @@ -14,20 +14,26 @@ #pragma once -#include - -#include -#include -#include -#include #include // IWYU pragma: export -#include + +// + +#include // IWYU pragma: export + +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include + +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include // IWYU pragma: keep: backward compatibility, to be removed. #include #include // IWYU pragma: keep: backward compatibility, to be removed. #include -#include +#include // IWYU pragma: keep: backward compatibility, to be removed. #include @@ -46,6 +52,9 @@ namespace v_noabi { /// - [Read Concern (MongoDB Manual)](https://www.mongodb.com/docs/manual/reference/read-concern/) /// class read_concern { + private: + v1::read_concern _rc; + public: /// /// A class to represent the read concern level for read operations. @@ -71,32 +80,31 @@ class read_concern { /// run with this read_concern will use the server's default read_concern instead of /// specifying one. /// - MONGOCXX_ABI_EXPORT_CDECL() read_concern(); - - /// - /// Copy constructs a read_concern. - /// - MONGOCXX_ABI_EXPORT_CDECL() read_concern(read_concern const&); + read_concern() = default; /// - /// Copy assigns a read_concern. + /// Construct with the @ref mongocxx::v1 equivalent. /// - MONGOCXX_ABI_EXPORT_CDECL(read_concern&) operator=(read_concern const&); + /* explicit(false) */ read_concern(v1::read_concern rc) : _rc{std::move(rc)} {} /// - /// Move constructs a read_concern. + /// Convert to the @ref mongocxx::v1 equivalent. /// - MONGOCXX_ABI_EXPORT_CDECL() read_concern(read_concern&&) noexcept; - + /// @par Postconditions: + /// - `other` is in an assign-or-destroy-only state. /// - /// Move assigns a read_concern. + /// @warning Invalidates all associated iterators and views. /// - MONGOCXX_ABI_EXPORT_CDECL(read_concern&) operator=(read_concern&&) noexcept; + explicit operator v1::read_concern() && { + return std::move(_rc); + } /// - /// Destroys a read_concern. + /// Convert to the @ref mongocxx::v1 equivalent. /// - MONGOCXX_ABI_EXPORT_CDECL() ~read_concern(); + explicit operator v1::read_concern() const& { + return _rc; + } /// /// Sets the read concern level. @@ -118,7 +126,9 @@ class read_concern { /// /// @return The read concern level. /// - MONGOCXX_ABI_EXPORT_CDECL(level) acknowledge_level() const; + level acknowledge_level() const { + return static_cast(_rc.acknowledge_level()); + } /// /// Sets the read concern string. Any valid read concern string (e.g. "local", @@ -129,8 +139,9 @@ class read_concern { /// @param rc_string /// The read concern string. /// - MONGOCXX_ABI_EXPORT_CDECL(void) - acknowledge_string(bsoncxx::v_noabi::stdx::string_view rc_string); + void acknowledge_string(bsoncxx::v1::stdx::string_view rc_string) { + _rc.acknowledge_string(rc_string); + } /// /// Gets the current read concern string. @@ -140,7 +151,13 @@ class read_concern { /// /// @return The read concern string. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::string_view) acknowledge_string() const; + bsoncxx::v1::stdx::string_view acknowledge_string() const { + auto ret = _rc.acknowledge_string(); + if (ret.empty()) { + ret = ""; + } + return ret; + } /// /// Gets the document form of this read_concern. @@ -148,7 +165,9 @@ class read_concern { /// @return /// Document representation of this read_concern. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::document::value) to_document() const; + bsoncxx::v_noabi::document::value to_document() const { + return bsoncxx::v_noabi::from_v1(_rc.to_document()); + } /// /// @relates mongocxx::v_noabi::read_concern @@ -156,24 +175,32 @@ class read_concern { /// Compares two read_concern objects for (in)-equality. /// /// @{ - friend MONGOCXX_ABI_EXPORT_CDECL(bool) operator==(read_concern const&, read_concern const&); - friend MONGOCXX_ABI_EXPORT_CDECL(bool) operator!=(read_concern const&, read_concern const&); + friend bool operator==(read_concern const& lhs, read_concern const& rhs) { + return lhs.acknowledge_level() == rhs.acknowledge_level(); + } + + friend bool operator!=(read_concern const& lhs, read_concern const& rhs) { + return !(lhs == rhs); + } /// @} /// - private: - friend ::mongocxx::v_noabi::client; - friend ::mongocxx::v_noabi::collection; - friend ::mongocxx::v_noabi::database; - friend ::mongocxx::v_noabi::options::transaction; - friend ::mongocxx::v_noabi::uri; - - class impl; + class internal; +}; - read_concern(std::unique_ptr&& implementation); +/// +/// Convert to the @ref mongocxx::v_noabi equivalent of `v`. +/// +inline v_noabi::read_concern from_v1(v1::read_concern v) { + return {std::move(v)}; +} - std::unique_ptr _impl; -}; +/// +/// Convert to the @ref mongocxx::v1 equivalent of `v`. +/// +inline v1::read_concern to_v1(v_noabi::read_concern v) { + return v1::read_concern{std::move(v)}; +} } // namespace v_noabi } // namespace mongocxx @@ -184,3 +211,6 @@ class read_concern { /// @file /// Provides @ref mongocxx::v_noabi::read_concern. /// +/// @par Includes +/// - @ref mongocxx/v1/read_concern.hpp +/// diff --git a/src/mongocxx/lib/mongocxx/v1/read_concern.cpp b/src/mongocxx/lib/mongocxx/v1/read_concern.cpp index caaab1f89e..5957fae524 100644 --- a/src/mongocxx/lib/mongocxx/v1/read_concern.cpp +++ b/src/mongocxx/lib/mongocxx/v1/read_concern.cpp @@ -12,4 +12,159 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include + +// + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace mongocxx { +namespace v1 { + +namespace { + +mongoc_read_concern_t* to_mongoc(void* ptr) { + return static_cast(ptr); +} + +} // namespace + +read_concern::~read_concern() { + libmongoc::read_concern_destroy(to_mongoc(_impl)); +} + +read_concern::read_concern(read_concern&& other) noexcept : _impl{exchange(other._impl, nullptr)} {} + +read_concern& read_concern::operator=(read_concern&& other) noexcept { + if (this != &other) { + libmongoc::read_concern_destroy(to_mongoc(exchange(_impl, exchange(other._impl, nullptr)))); + } + return *this; +} + +read_concern::read_concern(read_concern const& other) : _impl{libmongoc::read_concern_copy(to_mongoc(other._impl))} {} + +read_concern& read_concern::operator=(read_concern const& other) { + if (this != &other) { + libmongoc::read_concern_destroy( + to_mongoc(exchange(_impl, libmongoc::read_concern_copy(to_mongoc(other._impl))))); + } + return *this; +} + +read_concern::read_concern() : _impl{libmongoc::read_concern_new()} {} + +read_concern& read_concern::acknowledge_level(level v) { + // Return value is always true: no input validation by mongoc. + switch (v) { + case level::k_local: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), MONGOC_READ_CONCERN_LEVEL_LOCAL); + break; + case level::k_majority: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), MONGOC_READ_CONCERN_LEVEL_MAJORITY); + break; + case level::k_linearizable: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), MONGOC_READ_CONCERN_LEVEL_LINEARIZABLE); + break; + + case level::k_server_default: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), nullptr); + break; + + case level::k_available: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), MONGOC_READ_CONCERN_LEVEL_AVAILABLE); + break; + case level::k_snapshot: + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), MONGOC_READ_CONCERN_LEVEL_SNAPSHOT); + break; + + default: + case level::k_unknown: + // Precondition violation: undocumented but well-defined behavior. + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), nullptr); + break; + } + + return *this; +} + +read_concern::level read_concern::acknowledge_level() const { + auto const level_cstr = libmongoc::read_concern_get_level(to_mongoc(_impl)); + + if (!level_cstr) { + return level::k_server_default; + } + + std::unordered_map const map = { + {MONGOC_READ_CONCERN_LEVEL_LOCAL, level::k_local}, + {MONGOC_READ_CONCERN_LEVEL_MAJORITY, level::k_majority}, + {MONGOC_READ_CONCERN_LEVEL_LINEARIZABLE, level::k_linearizable}, + {MONGOC_READ_CONCERN_LEVEL_AVAILABLE, level::k_available}, + {MONGOC_READ_CONCERN_LEVEL_SNAPSHOT, level::k_snapshot}, + }; + + auto const iter = map.find(level_cstr); + + return iter != map.end() ? iter->second : level::k_unknown; +} + +read_concern& read_concern::acknowledge_string(bsoncxx::v1::stdx::string_view v) { + // Return value is always true: no input validation by mongoc. + (void)libmongoc::read_concern_set_level(to_mongoc(_impl), v.empty() ? nullptr : std::string{v}.c_str()); + return *this; +} + +bsoncxx::v1::stdx::string_view read_concern::acknowledge_string() const { + bsoncxx::v1::stdx::string_view ret; + + if (auto const level_cstr = libmongoc::read_concern_get_level(to_mongoc(_impl))) { + ret = bsoncxx::v1::stdx::string_view{level_cstr}; + } + + return ret; +} + +bsoncxx::v1::document::value read_concern::to_document() const { + scoped_bson doc; + + if (auto const level_cstr = libmongoc::read_concern_get_level(to_mongoc(_impl))) { + doc += scoped_bson{BCON_NEW("level", BCON_UTF8(level_cstr))}; + } + + return std::move(doc).value(); +} + +bool operator==(read_concern const& lhs, read_concern const& rhs) { + auto const lhs_cstr = libmongoc::read_concern_get_level(to_mongoc(lhs._impl)); + auto const rhs_cstr = libmongoc::read_concern_get_level(to_mongoc(rhs._impl)); + + if (!lhs_cstr != !rhs_cstr) { + return false; + } + + return !lhs_cstr || std::strcmp(lhs_cstr, rhs_cstr) == 0; +} + +read_concern::read_concern(void* impl) : _impl{impl} {} + +read_concern read_concern::internal::make(mongoc_read_concern_t* rc) { + return {rc}; +} + +mongoc_read_concern_t const* read_concern::internal::as_mongoc(read_concern const& self) { + return to_mongoc(self._impl); +} + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/lib/mongocxx/v1/read_concern.hh b/src/mongocxx/lib/mongocxx/v1/read_concern.hh new file mode 100644 index 0000000000..0e88c24879 --- /dev/null +++ b/src/mongocxx/lib/mongocxx/v1/read_concern.hh @@ -0,0 +1,35 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include // IWYU pragma: export + +// + +#include +#include + +namespace mongocxx { +namespace v1 { + +class read_concern::internal { + public: + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(read_concern) make(mongoc_read_concern_t* rc); + + static mongoc_read_concern_t const* as_mongoc(read_concern const& self); +}; + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp index 710ec9279d..0895375d86 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -37,6 +38,7 @@ #include +#include #include namespace mongocxx { @@ -151,8 +153,7 @@ client::operator bool() const noexcept { } void client::read_concern_deprecated(mongocxx::v_noabi::read_concern rc) { - auto client_t = _get_impl().client_t; - libmongoc::client_set_read_concern(client_t, rc._impl->read_concern_t); + libmongoc::client_set_read_concern(_get_impl().client_t, v_noabi::read_concern::internal::as_mongoc(rc)); } void client::read_concern(mongocxx::v_noabi::read_concern rc) { @@ -160,8 +161,8 @@ void client::read_concern(mongocxx::v_noabi::read_concern rc) { } mongocxx::v_noabi::read_concern client::read_concern() const { - auto rc = libmongoc::client_get_read_concern(_get_impl().client_t); - return {bsoncxx::make_unique(libmongoc::read_concern_copy(rc))}; + return v1::read_concern::internal::make( + libmongoc::read_concern_copy(libmongoc::client_get_read_concern(_get_impl().client_t))); } void client::read_preference_deprecated(mongocxx::v_noabi::read_preference rp) { diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/collection.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/collection.cpp index b6b220a578..6b2818dc22 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/collection.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/collection.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -1311,12 +1312,12 @@ void collection::drop( } void collection::read_concern(mongocxx::v_noabi::read_concern rc) { - libmongoc::collection_set_read_concern(_get_impl().collection_t, rc._impl->read_concern_t); + libmongoc::collection_set_read_concern(_get_impl().collection_t, v_noabi::read_concern::internal::as_mongoc(rc)); } mongocxx::v_noabi::read_concern collection::read_concern() const { - auto rc = libmongoc::collection_get_read_concern(_get_impl().collection_t); - return {bsoncxx::make_unique(libmongoc::read_concern_copy(rc))}; + return v1::read_concern::internal::make( + libmongoc::read_concern_copy(libmongoc::collection_get_read_concern(_get_impl().collection_t))); } void collection::read_preference(mongocxx::v_noabi::read_preference rp) { diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/database.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/database.cpp index 3c41968d8a..667fa2f17e 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/database.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/database.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -341,12 +342,12 @@ void database::drop( } void database::read_concern(mongocxx::v_noabi::read_concern rc) { - libmongoc::database_set_read_concern(_get_impl().database_t, rc._impl->read_concern_t); + libmongoc::database_set_read_concern(_get_impl().database_t, v_noabi::read_concern::internal::as_mongoc(rc)); } mongocxx::v_noabi::read_concern database::read_concern() const { - auto rc = libmongoc::database_get_read_concern(_get_impl().database_t); - return {bsoncxx::make_unique(libmongoc::read_concern_copy(rc))}; + return v1::read_concern::internal::make( + libmongoc::read_concern_copy(libmongoc::database_get_read_concern(_get_impl().database_t))); } void database::read_preference(mongocxx::v_noabi::read_preference rp) { diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/transaction.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/transaction.hh index 67b303bf6e..bb88f92a92 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/transaction.hh +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/options/transaction.hh @@ -14,15 +14,20 @@ #pragma once -#include // IWYU pragma: export +#include // IWYU pragma: export // +#include #include #include #include +#include + +#include + #include #include #include @@ -63,16 +68,16 @@ class transaction::impl { impl& operator=(impl&&) = default; void read_concern(mongocxx::v_noabi::read_concern const& rc) { - libmongoc::transaction_opts_set_read_concern(_transaction_opt_t.get(), rc._impl->read_concern_t); + libmongoc::transaction_opts_set_read_concern( + _transaction_opt_t.get(), v_noabi::read_concern::internal::as_mongoc(rc)); } - bsoncxx::v_noabi::stdx::optional read_concern() const { - auto rc = libmongoc::transaction_opts_get_read_concern(_transaction_opt_t.get()); - if (!rc) { - return bsoncxx::v_noabi::stdx::nullopt; + bsoncxx::v_noabi::stdx::optional read_concern() const { + bsoncxx::v_noabi::stdx::optional ret; + if (auto const rc = libmongoc::transaction_opts_get_read_concern(_transaction_opt_t.get())) { + ret.emplace(v1::read_concern::internal::make(libmongoc::read_concern_copy(rc))); } - mongocxx::v_noabi::read_concern rci(bsoncxx::make_unique(libmongoc::read_concern_copy(rc))); - return bsoncxx::v_noabi::stdx::optional(std::move(rci)); + return ret; } void write_concern(mongocxx::v_noabi::write_concern const& wc) { diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.cpp index b213139868..35e36c84b5 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.cpp @@ -12,120 +12,66 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include +#include -#include -#include -#include +// -#include +#include -#include +#include +#include #include -namespace mongocxx { -namespace v_noabi { +namespace { +namespace static_assertions { +namespace level { -read_concern::read_concern() : _impl{bsoncxx::make_unique(libmongoc::read_concern_new())} {} +using lv1 = mongocxx::v1::read_concern::level; +using lv_noabi = mongocxx::v_noabi::read_concern::level; -read_concern::read_concern(std::unique_ptr&& implementation) : _impl{std::move(implementation)} {} +template +struct check { + static_assert( + static_cast(lhs) == static_cast(rhs), + "read_concern::level: v1 and v_noabi must have the same values"); +}; -read_concern::read_concern(read_concern&&) noexcept = default; -read_concern& read_concern::operator=(read_concern&&) noexcept = default; +template struct check; +template struct check; +template struct check; +template struct check; +template struct check; +template struct check; +template struct check; -read_concern::read_concern(read_concern const& other) - : _impl(bsoncxx::make_unique(libmongoc::read_concern_copy(other._impl->read_concern_t))) {} +} // namespace level +} // namespace static_assertions +} // namespace -read_concern& read_concern::operator=(read_concern const& other) { - _impl = bsoncxx::make_unique(libmongoc::read_concern_copy(other._impl->read_concern_t)); - return *this; -} - -read_concern::~read_concern() = default; +namespace mongocxx { +namespace v_noabi { void read_concern::acknowledge_level(read_concern::level rc_level) { switch (rc_level) { case read_concern::level::k_local: - libmongoc::read_concern_set_level(_impl->read_concern_t, MONGOC_READ_CONCERN_LEVEL_LOCAL); - break; case read_concern::level::k_majority: - libmongoc::read_concern_set_level(_impl->read_concern_t, MONGOC_READ_CONCERN_LEVEL_MAJORITY); - break; case read_concern::level::k_linearizable: - libmongoc::read_concern_set_level(_impl->read_concern_t, MONGOC_READ_CONCERN_LEVEL_LINEARIZABLE); - break; case read_concern::level::k_server_default: - // libmongoc uses a NULL level to mean "use the server's default read_concern." - libmongoc::read_concern_set_level(_impl->read_concern_t, nullptr); - break; case read_concern::level::k_available: - libmongoc::read_concern_set_level(_impl->read_concern_t, MONGOC_READ_CONCERN_LEVEL_AVAILABLE); - break; case read_concern::level::k_snapshot: - libmongoc::read_concern_set_level(_impl->read_concern_t, MONGOC_READ_CONCERN_LEVEL_SNAPSHOT); + _rc.acknowledge_level(static_cast(rc_level)); break; - case read_concern::level::k_unknown: default: - throw exception{error_code::k_unknown_read_concern}; - } -} - -void read_concern::acknowledge_string(bsoncxx::v_noabi::stdx::string_view rc_string) { - // libmongoc uses a NULL level to mean "use the server's default read_concern." - libmongoc::read_concern_set_level( - _impl->read_concern_t, rc_string.empty() ? nullptr : bsoncxx::v_noabi::string::to_string(rc_string).data()); -} - -read_concern::level read_concern::acknowledge_level() const { - auto level = libmongoc::read_concern_get_level(_impl->read_concern_t); - if (!level) { - return read_concern::level::k_server_default; - } - if (strcmp(MONGOC_READ_CONCERN_LEVEL_LOCAL, level) == 0) { - return read_concern::level::k_local; - } else if (strcmp(MONGOC_READ_CONCERN_LEVEL_MAJORITY, level) == 0) { - return read_concern::level::k_majority; - } else if (strcmp(MONGOC_READ_CONCERN_LEVEL_LINEARIZABLE, level) == 0) { - return read_concern::level::k_linearizable; - } else if (strcmp(MONGOC_READ_CONCERN_LEVEL_AVAILABLE, level) == 0) { - return read_concern::level::k_available; - } else if (strcmp(MONGOC_READ_CONCERN_LEVEL_SNAPSHOT, level) == 0) { - return read_concern::level::k_snapshot; - } else { - return read_concern::level::k_unknown; - } -} - -bsoncxx::v_noabi::stdx::string_view read_concern::acknowledge_string() const { - auto level = libmongoc::read_concern_get_level(_impl->read_concern_t); - if (!level) { - return ""; - } - return {bsoncxx::v_noabi::stdx::string_view{level}}; -} - -bsoncxx::v_noabi::document::value read_concern::to_document() const { - using bsoncxx::v_noabi::builder::basic::kvp; - - auto level = libmongoc::read_concern_get_level(_impl->read_concern_t); - - bsoncxx::v_noabi::builder::basic::document doc; - if (level) { - doc.append(kvp("level", level)); + case read_concern::level::k_unknown: + // Backward compatibility: k_unknown is an exception. + throw v_noabi::exception{error_code::k_unknown_read_concern}; } - - return doc.extract(); -} - -bool operator==(read_concern const& lhs, read_concern const& rhs) { - return lhs.acknowledge_level() == rhs.acknowledge_level(); } -bool operator!=(read_concern const& lhs, read_concern const& rhs) { - return !(lhs == rhs); +mongoc_read_concern_t const* read_concern::internal::as_mongoc(read_concern const& self) { + return v1::read_concern::internal::as_mongoc(self._rc); } } // namespace v_noabi diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.hh index a7199b6564..dcaf27436e 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.hh +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/read_concern.hh @@ -16,26 +16,16 @@ #include // IWYU pragma: export +// + #include namespace mongocxx { namespace v_noabi { -class read_concern::impl { +class read_concern::internal { public: - impl(::mongoc_read_concern_t* read_concern) : read_concern_t{read_concern} {} - - ~impl() { - libmongoc::read_concern_destroy(read_concern_t); - } - - impl(impl&&) = delete; - impl& operator=(impl&&) = delete; - - impl(impl const&) = delete; - impl& operator=(impl const&) = delete; - - ::mongoc_read_concern_t* read_concern_t; + static mongoc_read_concern_t const* as_mongoc(read_concern const& self); }; } // namespace v_noabi diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp index 92a5f98fb7..49e542123e 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include @@ -98,8 +99,8 @@ std::string uri::password() const { } mongocxx::v_noabi::read_concern uri::read_concern() const { - auto rc = libmongoc::uri_get_read_concern(_impl->uri_t); - return mongocxx::v_noabi::read_concern(bsoncxx::make_unique(libmongoc::read_concern_copy(rc))); + return v1::read_concern::internal::make( + libmongoc::read_concern_copy(libmongoc::uri_get_read_concern(_impl->uri_t))); } mongocxx::v_noabi::read_preference uri::read_preference() const { diff --git a/src/mongocxx/test/CMakeLists.txt b/src/mongocxx/test/CMakeLists.txt index 562904aa7c..d072dab439 100644 --- a/src/mongocxx/test/CMakeLists.txt +++ b/src/mongocxx/test/CMakeLists.txt @@ -104,6 +104,7 @@ set(mongocxx_test_sources_v1 v1/bsoncxx.cpp v1/exception.cpp v1/logger.cpp + v1/read_concern.cpp v1/read_preference.cpp ) diff --git a/src/mongocxx/test/v1/read_concern.cpp b/src/mongocxx/test/v1/read_concern.cpp new file mode 100644 index 0000000000..dcbb89ffa4 --- /dev/null +++ b/src/mongocxx/test/v1/read_concern.cpp @@ -0,0 +1,283 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +// + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +namespace mongocxx { +namespace v1 { + +TEST_CASE("ownership", "[mongocxx][v1][read_concern]") { + read_concern source; + read_concern target; + + source.acknowledge_string("source"); + target.acknowledge_string("target"); + + SECTION("move") { + auto move = std::move(source); + + // source is in an assign-or-move-only state. + + CHECK(move.acknowledge_string() == "source"); + + target = std::move(move); + + // source is in an assign-or-move-only state. + + CHECK(target.acknowledge_string() == "source"); + } + + SECTION("copy") { + auto copy = source; + + CHECK(source.acknowledge_string() == "source"); + CHECK(copy.acknowledge_string() == "source"); + + target = copy; + + CHECK(copy.acknowledge_string() == "source"); + CHECK(target.acknowledge_string() == "source"); + } +} + +TEST_CASE("default", "[mongocxx][v1][read_concern]") { + read_concern const rc; + + CHECK(rc.acknowledge_level() == read_concern::level::k_server_default); + CHECK(rc.acknowledge_string().empty()); +} + +TEST_CASE("acknowledge_level", "[mongocxx][v1][read_concern]") { + using level = read_concern::level; + + read_concern rc; + + SECTION("normal") { + level input = {}; + level expected{}; + + std::tie(input, expected) = GENERATE( + table({ + {level::k_local, level::k_local}, + {level::k_majority, level::k_majority}, + {level::k_linearizable, level::k_linearizable}, + {level::k_server_default, level::k_server_default}, + {level::k_unknown, level::k_server_default}, // Undocumented behavior. + {level::k_available, level::k_available}, + {level::k_snapshot, level::k_snapshot}, + {static_cast(-1), level::k_server_default}, // Undocumented behavior. + })); + + CAPTURE(input); + CHECK(rc.acknowledge_level(input).acknowledge_level() == expected); + } + + SECTION("acknowledge_string") { + level input = {}; + bsoncxx::v1::stdx::string_view expected; + + std::tie(input, expected) = GENERATE( + table({ + {level::k_local, "local"}, + {level::k_majority, "majority"}, + {level::k_linearizable, "linearizable"}, + {level::k_server_default, {}}, + {level::k_unknown, {}}, // Undocumented behavior. + {level::k_available, "available"}, + {level::k_snapshot, "snapshot"}, + {static_cast(-1), {}}, // Undocumented behavior. + })); + + CAPTURE(input); + CHECK(rc.acknowledge_level(input).acknowledge_string() == expected); + } +} + +TEST_CASE("acknowledge_string", "[mongocxx][v1][read_concern]") { + using level = read_concern::level; + + static constexpr bsoncxx::v1::stdx::string_view empty; + + read_concern rc; + + SECTION("normal") { + auto const v = GENERATE( + as{}, + // Normal. + "local", + "majority", + "linearizable", + empty, + "unknown", // Arbitrary string. + "available", + "snapshot", + + // Arbitrary. + "", + "abc", + "123", + "arbitrary", + "level"); + + CAPTURE(v); + CHECK(rc.acknowledge_string(v).acknowledge_string() == v); + } + + SECTION("acknowledge_level") { + bsoncxx::v1::stdx::string_view input; + level expected = {}; + + std::tie(input, expected) = GENERATE( + table({ + // Normal. + {"local", level::k_local}, + {"majority", level::k_majority}, + {"linearizable", level::k_linearizable}, + {empty, level::k_server_default}, + {"", level::k_server_default}, + {"available", level::k_available}, + {"snapshot", level::k_snapshot}, + + // Arbitrary. + {"abc", level::k_unknown}, + {"123", level::k_unknown}, + {"arbitrary", level::k_unknown}, + {"level", level::k_unknown}, + })); + + CAPTURE(input); + CHECK(rc.acknowledge_string(input).acknowledge_level() == expected); + } +} + +TEST_CASE("to_document", "[mongocxx][v1][read_concern]") { + using level = read_concern::level; + + SECTION("default") { + CHECK(read_concern{}.to_document() == bsoncxx::v1::document::view{}); + } + + SECTION("acknowledge_level") { + level v = {}; + scoped_bson expected; + + std::tie(v, expected) = GENERATE(map( + [](std::tuple i) { + return std::make_tuple(std::get<0>(i), scoped_bson{std::get<1>(i)}); + }, + table({ + // Normal. + {level::k_local, R"({"level": "local"})"}, + {level::k_majority, R"({"level": "majority"})"}, + {level::k_linearizable, R"({"level": "linearizable"})"}, + {level::k_server_default, R"({})"}, + {level::k_unknown, R"({})"}, // Undocumented behavior. + {level::k_available, R"({"level": "available"})"}, + {level::k_snapshot, R"({"level": "snapshot"})"}, + {static_cast(-1), R"({})"}, // Undocumented behavior. + }))); + + CAPTURE(v); + + auto const doc = read_concern{}.acknowledge_level(v).to_document(); + CHECK(doc.view() == expected.view()); + } + + SECTION("acknowledge_string") { + bsoncxx::v1::stdx::string_view str; + scoped_bson expected; + + std::tie(str, expected) = GENERATE(map( + [](std::tuple i) { + return std::make_tuple(std::get<0>(i), scoped_bson{std::get<1>(i)}); + }, + table({ + // Normal. + {"local", R"({"level": "local"})"}, + {"majority", R"({"level": "majority"})"}, + {"linearizable", R"({"level": "linearizable"})"}, + {bsoncxx::v1::stdx::string_view{}, R"({})"}, + {"", R"({})"}, + {"available", R"({"level": "available"})"}, + {"snapshot", R"({"level": "snapshot"})"}, + + // Arbitrary. + {"abc", R"({"level": "abc"})"}, + {"123", R"({"level": "123"})"}, + {"arbitrary", R"({"level": "arbitrary"})"}, + {"level", R"({"level": "level"})"}, + }))); + + CAPTURE(str); + + auto const doc = read_concern{}.acknowledge_string(str).to_document(); + CHECK(doc.view() == expected.view()); + } +} + +TEST_CASE("equality", "[mongocxx][v1][read_concern]") { + using level = read_concern::level; + + read_concern lhs; + read_concern rhs; + + SECTION("normal") { + lhs.acknowledge_level(level::k_majority); + rhs.acknowledge_level(level::k_local); + + CHECK(lhs == lhs); + CHECK(rhs == rhs); + CHECK(lhs != rhs); + + lhs.acknowledge_level(level::k_available); + rhs.acknowledge_level(level::k_server_default); + + CHECK(lhs == lhs); + CHECK(rhs == rhs); + CHECK(lhs != rhs); + } + + SECTION("unknown") { + lhs.acknowledge_level(level::k_majority); + rhs.acknowledge_level(level::k_unknown); + + CHECK(lhs == lhs); + CHECK(rhs == rhs); + CHECK(lhs != rhs); + + lhs.acknowledge_string("lhs"); + rhs.acknowledge_string("rhs"); + + CHECK(lhs == lhs); + CHECK(rhs == rhs); + CHECK(lhs != rhs); + } +} + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/test/v1/read_concern.hh b/src/mongocxx/test/v1/read_concern.hh new file mode 100644 index 0000000000..f3b9312924 --- /dev/null +++ b/src/mongocxx/test/v1/read_concern.hh @@ -0,0 +1,31 @@ +// Copyright 2009-present MongoDB, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include // IWYU pragma: export + +// + +#include + +CATCH_REGISTER_ENUM( + mongocxx::v1::read_concern::level, + mongocxx::v1::read_concern::level::k_local, + mongocxx::v1::read_concern::level::k_majority, + mongocxx::v1::read_concern::level::k_linearizable, + mongocxx::v1::read_concern::level::k_server_default, + mongocxx::v1::read_concern::level::k_unknown, + mongocxx::v1::read_concern::level::k_available, + mongocxx::v1::read_concern::level::k_snapshot)