diff --git a/src/mongocxx/include/mongocxx/v1/uri.hpp b/src/mongocxx/include/mongocxx/v1/uri.hpp index 38e4677884..14d1e0e330 100644 --- a/src/mongocxx/include/mongocxx/v1/uri.hpp +++ b/src/mongocxx/include/mongocxx/v1/uri.hpp @@ -42,6 +42,7 @@ #include #include #include +#include #include namespace mongocxx { @@ -242,7 +243,7 @@ class uri { /// /// Return the mongoc "credentials" field containing "authMechanism" and related options. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v1::stdx::optional) credentials(); + MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v1::stdx::optional) credentials() const; /// /// Return the "srvMaxHosts" option. @@ -389,6 +390,16 @@ class uri { friend std::error_code make_error_code(errc v) { return {static_cast(v), error_category()}; } + + class internal; + + private: + // MSVC may incorrectly select the `void*` ctor given a `char const*` argument without `/Zc:strictStrings`. + struct void_ptr { + void* impl = nullptr; + }; + + explicit uri(void_ptr vp); }; BSONCXX_PRIVATE_INLINE_CXX17 constexpr char const* uri::k_default_uri; @@ -396,6 +407,13 @@ BSONCXX_PRIVATE_INLINE_CXX17 constexpr char const* uri::k_default_uri; } // namespace v1 } // namespace mongocxx +namespace std { + +template <> +struct is_error_code_enum : true_type {}; + +} // namespace std + #include /// diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri-fwd.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri-fwd.hpp index 6b873c90f2..619f6fe9cd 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri-fwd.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri-fwd.hpp @@ -14,6 +14,8 @@ #pragma once +#include // IWYU pragma: export + #include namespace mongocxx { @@ -26,7 +28,7 @@ class uri; namespace mongocxx { -using ::mongocxx::v_noabi::uri; +using v_noabi::uri; } // namespace mongocxx @@ -36,3 +38,6 @@ using ::mongocxx::v_noabi::uri; /// @file /// Declares @ref mongocxx::v_noabi::uri. /// +/// @par Includes +/// - @ref mongocxx/v1/uri-fwd.hpp +/// diff --git a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri.hpp b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri.hpp index ee7022a1dc..76da3c717c 100644 --- a/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri.hpp +++ b/src/mongocxx/include/mongocxx/v_noabi/mongocxx/uri.hpp @@ -14,16 +14,27 @@ #pragma once -#include +#include // IWYU pragma: export + +// + +#include + +#include // IWYU pragma: export + +#include +#include // IWYU pragma: keep: backward compatibility, to be removed. #include +#include #include #include -#include -#include -#include // IWYU pragma: export +#include // IWYU pragma: keep: backward compatibility, to be removed. +#include // IWYU pragma: keep: backward compatibility, to be removed. #include +#include +#include #include #include @@ -42,15 +53,11 @@ namespace v_noabi { /// - [Connection Strings (MongoDB Manual)](https://www.mongodb.com/docs/manual/reference/connection-string/) /// class uri { + private: + v1::uri _uri; + public: - /// - /// A host. - /// - struct host { - std::string name; ///< The host name. - std::uint16_t port; ///< The port number. - std::int32_t family; ///< The address family. - }; + using host = v1::uri::host; /// /// The default URI string: `"mongodb://localhost:27017"`. @@ -58,63 +65,107 @@ class uri { static MONGOCXX_ABI_EXPORT const std::string k_default_uri; /// - /// Constructs a uri from an optional MongoDB URI string. If no URI string is specified, - /// uses the default URI string: `"mongodb://localhost:27017"`. + /// Constructs a uri from the provided MongoDB URI string. /// /// @see /// - https://mongoc.org/libmongoc/current/mongoc_uri_t.html /// - /// @param uri_string - /// String representing a MongoDB connection string URI, defaults to k_default_uri. + /// @{ + /* explicit(false) */ MONGOCXX_ABI_EXPORT_CDECL() uri(bsoncxx::v_noabi::string::view_or_value uri_string); + + template < + typename T, + bsoncxx::detail::enable_if_t::value>* = nullptr> + /* explicit(false) */ uri(T t) : uri{bsoncxx::v_noabi::string::view_or_value{t}} { + // For backward compatibility: avoid ambiguity with the new v1::uri ctor. + } + /// @} + /// - MONGOCXX_ABI_EXPORT_CDECL() - uri(bsoncxx::v_noabi::string::view_or_value uri_string = k_default_uri); + /// Constructs a uri from the default MongoDB URI string: `"mongodb://localhost:27017"`. + /// + /// @see + /// - https://mongoc.org/libmongoc/current/mongoc_uri_t.html + /// + uri() : uri{bsoncxx::v_noabi::string::view_or_value{k_default_uri}} {} /// /// Move constructs a uri. /// - MONGOCXX_ABI_EXPORT_CDECL() uri(uri&&) noexcept; + uri(uri&& other) noexcept = default; /// /// Move assigns a uri. /// - MONGOCXX_ABI_EXPORT_CDECL(uri&) operator=(uri&&) noexcept; + uri& operator=(uri&& other) noexcept = default; /// /// Destroys a uri. /// - MONGOCXX_ABI_EXPORT_CDECL() ~uri(); + ~uri() = default; - uri(uri const&) = delete; - uri& operator=(uri const&) = delete; + uri(uri const& other) = delete; + uri& operator=(uri const& other) = delete; + + /// + /// Construct with the @ref mongocxx::v1 equivalent. + /// + /* explicit(false) */ uri(v1::uri uri) : _uri{std::move(uri)} {} + + /// + /// Convert to the @ref mongocxx::v1 equivalent. + /// + /// @par Postconditions: + /// - `*this` is in an assign-or-destroy-only state. + /// + /// @warning Invalidates all associated views. + /// + explicit operator v1::uri() && { + return std::move(_uri); + } + + /// + /// Convert to the @ref mongocxx::v1 equivalent. + /// + explicit operator v1::uri() const& { + return _uri; + } /// /// Returns the authentication mechanism from the uri. /// /// @return A string representing the authentication mechanism. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) auth_mechanism() const; + std::string auth_mechanism() const { + return std::string{_uri.auth_mechanism()}; + } /// /// Returns the authentication source from the uri. /// /// @return A string representing the authentication source. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) auth_source() const; + std::string auth_source() const { + return std::string{_uri.auth_source()}; + } /// /// Returns the hosts from the uri. /// /// @return A vector of hosts. /// - MONGOCXX_ABI_EXPORT_CDECL(std::vector) hosts() const; + std::vector hosts() const { + return _uri.hosts(); + } /// /// Returns the database from the uri. /// /// @return A string with the name of the database. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) database() const; + std::string database() const { + return std::string{_uri.database()}; + } /// /// Returns other uri options. @@ -125,35 +176,45 @@ class uri { /// /// @return A document view containing other options. /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::document::view) options() const; + bsoncxx::v_noabi::document::view options() const { + return _uri.options(); + } /// /// Returns the password from the uri. /// /// @return A string containing the supplied password. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) password() const; + std::string password() const { + return std::string{_uri.password()}; + } /// /// Returns the read concern from the uri. /// /// @return A read_concern that represents what was specified in the uri. /// - MONGOCXX_ABI_EXPORT_CDECL(mongocxx::v_noabi::read_concern) read_concern() const; + v_noabi::read_concern read_concern() const { + return _uri.read_concern(); + } /// /// Returns the read preference from the uri. /// /// @return A read_preference that represents what was specified in the uri. /// - MONGOCXX_ABI_EXPORT_CDECL(mongocxx::v_noabi::read_preference) read_preference() const; + v_noabi::read_preference read_preference() const { + return _uri.read_preference(); + } /// /// Returns the replica set specified in the uri. /// /// @return A string representing the supplied replica set name. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) replica_set() const; + std::string replica_set() const { + return std::string{_uri.replica_set()}; + } /// /// Returns the ssl parameter from the uri. @@ -162,66 +223,79 @@ class uri { /// /// @deprecated The tls() method should be used instead of this method. /// - MONGOCXX_DEPRECATED MONGOCXX_ABI_EXPORT_CDECL(bool) ssl() const; + bool ssl() const { + return _uri.tls(); + } /// /// Returns the tls parameter from the uri. /// /// @return Boolean that is @c true if tls is enabled and @c false if not. /// - MONGOCXX_ABI_EXPORT_CDECL(bool) tls() const; + bool tls() const { + return _uri.tls(); + } /// /// Returns the uri in a string format. /// /// @return A string with the uri. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) to_string() const; + std::string to_string() const { + return std::string{_uri.to_string()}; + } /// /// Returns the supplied username from the uri. /// /// @return A string with the username specified in the uri. /// - MONGOCXX_ABI_EXPORT_CDECL(std::string) username() const; + std::string username() const { + return std::string{_uri.username()}; + } /// /// Returns the write concern specified in the uri. /// /// @return A write_concern that represents what was specified in the uri. /// - MONGOCXX_ABI_EXPORT_CDECL(mongocxx::v_noabi::write_concern) write_concern() const; + v_noabi::write_concern write_concern() const { + return _uri.write_concern(); + } /// /// Returns the value of the option "appname" if present in the uri. /// /// @return An optional bsoncxx::v_noabi::stdx::string_view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - appname() const; + bsoncxx::v_noabi::stdx::optional appname() const { + return _uri.appname(); + } /// /// Returns the value of the option "authMechanismProperties" if present in the uri. /// /// @return An optional bsoncxx::v_noabi::document::view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - auth_mechanism_properties() const; + bsoncxx::v_noabi::stdx::optional auth_mechanism_properties() const { + return _uri.auth_mechanism_properties(); + } /// /// Returns the value of the option credentials if present in the uri. /// /// @return An optional bsoncxx::v_noabi::document::view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - credentials(); + MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) credentials(); /// /// Returns the value of the option "srvMaxHosts" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) srv_max_hosts() const; + bsoncxx::v_noabi::stdx::optional srv_max_hosts() const { + return _uri.srv_max_hosts(); + } /// /// Returns the list of compressors present in the uri or an empty list if "compressors" was not @@ -229,75 +303,90 @@ class uri { /// /// @return A std::vector of bsoncxx::v_noabi::stdx::string_view. /// - MONGOCXX_ABI_EXPORT_CDECL(std::vector) compressors() const; + std::vector compressors() const { + return _uri.compressors(); + } /// /// Returns the value of the option "connectTimeoutMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - connect_timeout_ms() const; + bsoncxx::v_noabi::stdx::optional connect_timeout_ms() const { + return _uri.connect_timeout_ms(); + } /// /// Returns the value of the option "directConnection" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) direct_connection() const; + bsoncxx::v_noabi::stdx::optional direct_connection() const { + return _uri.direct_connection(); + } /// /// Returns the value of the option "heartbeatFrequencyMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - heartbeat_frequency_ms() const; + bsoncxx::v_noabi::stdx::optional heartbeat_frequency_ms() const { + return _uri.heartbeat_frequency_ms(); + } /// /// Returns the value of the option "localThresholdMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - local_threshold_ms() const; + bsoncxx::v_noabi::stdx::optional local_threshold_ms() const { + return _uri.local_threshold_ms(); + } /// /// Returns the value of the option "maxPoolSize" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) max_pool_size() const; + bsoncxx::v_noabi::stdx::optional max_pool_size() const { + return _uri.max_pool_size(); + } /// /// Returns the value of the option "retryReads" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) retry_reads() const; + bsoncxx::v_noabi::stdx::optional retry_reads() const { + return _uri.retry_reads(); + } /// /// Returns the value of the option "retryWrites" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) retry_writes() const; + bsoncxx::v_noabi::stdx::optional retry_writes() const { + return _uri.retry_writes(); + } /// /// Returns the value of the option "serverSelectionTimeoutMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - server_selection_timeout_ms() const; + bsoncxx::v_noabi::stdx::optional server_selection_timeout_ms() const { + return _uri.server_selection_timeout_ms(); + } /// /// Returns the value of the option "serverSelectionTryOnce" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - server_selection_try_once() const; + bsoncxx::v_noabi::stdx::optional server_selection_try_once() const { + return _uri.server_selection_try_once(); + } /// /// Sets the value of the option "serverSelectionTryOnce" in the uri. @@ -306,56 +395,61 @@ class uri { /// /// @throws mongocxx::v_noabi::exception if there is an error setting the option. /// - MONGOCXX_ABI_EXPORT_CDECL(void) - server_selection_try_once(bool val); + MONGOCXX_ABI_EXPORT_CDECL(void) server_selection_try_once(bool val); /// /// Returns the value of the option "socketTimeoutMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - socket_timeout_ms() const; + bsoncxx::v_noabi::stdx::optional socket_timeout_ms() const { + return _uri.socket_timeout_ms(); + } /// /// Returns the value of the option "tlsAllowInvalidCertificates" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_allow_invalid_certificates() const; + bsoncxx::v_noabi::stdx::optional tls_allow_invalid_certificates() const { + return _uri.tls_allow_invalid_certificates(); + } /// /// Returns the value of the option "tlsAllowInvalidHostnames" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_allow_invalid_hostnames() const; + bsoncxx::v_noabi::stdx::optional tls_allow_invalid_hostnames() const { + return _uri.tls_allow_invalid_hostnames(); + } /// /// Returns the value of the option "tlsCAFile" if present in the uri. /// /// @return An optional bsoncxx::v_noabi::stdx::string_view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_ca_file() const; + bsoncxx::v_noabi::stdx::optional tls_ca_file() const { + return _uri.tls_ca_file(); + } /// /// Returns the value of the option "tlsCertificateKeyFile" if present in the uri. /// /// @return An optional bsoncxx::v_noabi::stdx::string_view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_certificate_key_file() const; + bsoncxx::v_noabi::stdx::optional tls_certificate_key_file() const { + return _uri.tls_certificate_key_file(); + } /// /// Returns the value of the option "tlsCertificateKeyFilePassword" if present in the uri. /// /// @return An optional bsoncxx::v_noabi::stdx::string_view /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_certificate_key_file_password() const; + bsoncxx::v_noabi::stdx::optional tls_certificate_key_file_password() const { + return _uri.tls_certificate_key_file_password(); + } /// /// Returns the value of the option "tlsDisableCertificateRevocationCheck" if present in the @@ -363,50 +457,62 @@ class uri { /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_disable_certificate_revocation_check() const; + bsoncxx::v_noabi::stdx::optional tls_disable_certificate_revocation_check() const { + return _uri.tls_disable_certificate_revocation_check(); + } /// /// Returns the value of the option "tlsDisableOCSPEndpointCheck" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - tls_disable_ocsp_endpoint_check() const; + bsoncxx::v_noabi::stdx::optional tls_disable_ocsp_endpoint_check() const { + return _uri.tls_disable_ocsp_endpoint_check(); + } /// /// Returns the value of the option "tlsInsecure" if present in the uri. /// /// @return An optional bool /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) tls_insecure() const; + bsoncxx::v_noabi::stdx::optional tls_insecure() const { + return _uri.tls_insecure(); + } /// /// Returns the value of the option "waitQueueTimeoutMS" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - wait_queue_timeout_ms() const; + bsoncxx::v_noabi::stdx::optional wait_queue_timeout_ms() const { + return _uri.wait_queue_timeout_ms(); + } /// /// Returns the value of the option "zlibCompressionLevel" if present in the uri. /// /// @return An optional std::int32_t /// - MONGOCXX_ABI_EXPORT_CDECL(bsoncxx::v_noabi::stdx::optional) - zlib_compression_level() const; + bsoncxx::v_noabi::stdx::optional zlib_compression_level() const { + return _uri.zlib_compression_level(); + } - private: - friend ::mongocxx::v_noabi::client; - friend ::mongocxx::v_noabi::pool; - - class impl; + class internal; +}; - uri(std::unique_ptr&& implementation); +/// +/// Convert to the @ref mongocxx::v_noabi equivalent of `v`. +/// +inline v_noabi::uri from_v1(v1::uri v) { + return {std::move(v)}; +} - std::unique_ptr _impl; -}; +/// +/// Convert to the @ref mongocxx::v1 equivalent of `v`. +/// +inline v1::uri to_v1(v_noabi::uri v) { + return v1::uri{std::move(v)}; +} } // namespace v_noabi } // namespace mongocxx @@ -417,3 +523,6 @@ class uri { /// @file /// Provides @ref mongocxx::v_noabi::uri. /// +/// @par Includes +/// - @ref mongocxx/v1/uri.hpp +/// diff --git a/src/mongocxx/lib/mongocxx/private/mongoc.hh b/src/mongocxx/lib/mongocxx/private/mongoc.hh index 7f7fe3c9fe..2437dc09bc 100644 --- a/src/mongocxx/lib/mongocxx/private/mongoc.hh +++ b/src/mongocxx/lib/mongocxx/private/mongoc.hh @@ -374,12 +374,14 @@ BSONCXX_PRIVATE_WARNINGS_POP(); X(transaction_opts_set_write_concern) \ X(uri_copy) \ X(uri_destroy) \ + X(uri_get_appname) \ X(uri_get_auth_mechanism) \ X(uri_get_auth_source) \ X(uri_get_compressors) \ X(uri_get_credentials) \ X(uri_get_database) \ X(uri_get_hosts) \ + X(uri_get_mechanism_properties) \ X(uri_get_option_as_utf8) \ X(uri_get_options) \ X(uri_get_password) \ @@ -390,6 +392,7 @@ BSONCXX_PRIVATE_WARNINGS_POP(); X(uri_get_tls) \ X(uri_get_username) \ X(uri_get_write_concern) \ + X(uri_set_option_as_bool) \ X(uri_new_with_error) \ X(write_concern_copy) \ X(write_concern_destroy) \ diff --git a/src/mongocxx/lib/mongocxx/v1/exception.cpp b/src/mongocxx/lib/mongocxx/v1/exception.cpp index 2bf1b669e1..fa835d00e9 100644 --- a/src/mongocxx/lib/mongocxx/v1/exception.cpp +++ b/src/mongocxx/lib/mongocxx/v1/exception.cpp @@ -300,10 +300,6 @@ v1::exception make_exception(bson_error_t const& error) { } // namespace -exception exception::internal::make(std::error_code ec) { - return {ec, bsoncxx::make_unique()}; -} - exception exception::internal::make(int code, std::error_category const& category, char const* message) { return {std::error_code{code, category}, message, bsoncxx::make_unique()}; } diff --git a/src/mongocxx/lib/mongocxx/v1/exception.hh b/src/mongocxx/lib/mongocxx/v1/exception.hh index b63d3896a9..186a022a85 100644 --- a/src/mongocxx/lib/mongocxx/v1/exception.hh +++ b/src/mongocxx/lib/mongocxx/v1/exception.hh @@ -16,10 +16,12 @@ // +#include #include #include #include +#include #include @@ -30,11 +32,28 @@ namespace v1 { class exception::internal { public: - static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(std::error_code ec); static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(int code, std::error_category const& category, char const* message); static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(int code, std::error_category const& category); + template < + typename Errc, + typename... Args, + bsoncxx::detail::enable_if_t::value>* = nullptr> + static exception make(Errc errc, Args&&... args) { + using std::make_error_code; + auto const ec = make_error_code(errc); + return make(ec.value(), ec.category(), std::forward(args)...); + } + + static exception make(std::error_code ec) { + return make(ec.value(), ec.category()); + } + + static exception make(std::error_code ec, char const* message) { + return make(ec.value(), ec.category(), message); + } + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) set_error_labels(exception& self, bsoncxx::v1::document::view v); }; diff --git a/src/mongocxx/lib/mongocxx/v1/read_concern.hh b/src/mongocxx/lib/mongocxx/v1/read_concern.hh index 0e88c24879..01218c9e0c 100644 --- a/src/mongocxx/lib/mongocxx/v1/read_concern.hh +++ b/src/mongocxx/lib/mongocxx/v1/read_concern.hh @@ -28,7 +28,7 @@ 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); + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(mongoc_read_concern_t const*) as_mongoc(read_concern const& self); }; } // namespace v1 diff --git a/src/mongocxx/lib/mongocxx/v1/uri.cpp b/src/mongocxx/lib/mongocxx/v1/uri.cpp index a5802ea934..a3a1ef63a5 100644 --- a/src/mongocxx/lib/mongocxx/v1/uri.cpp +++ b/src/mongocxx/lib/mongocxx/v1/uri.cpp @@ -12,4 +12,409 @@ // 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include + +namespace mongocxx { +namespace v1 { + +using code = uri::errc; + +namespace { + +mongoc_uri_t* to_mongoc(void* ptr) { + return static_cast(ptr); +} + +template +bsoncxx::v1::stdx::optional get_field(mongoc_uri_t const* uri, char const* field); + +template <> +bsoncxx::v1::stdx::optional get_field(mongoc_uri_t const* uri, char const* field) { + auto const options = libmongoc::uri_get_options(uri); // Never null. + + bson_iter_t iter = {}; + if (bson_iter_init_find_case(&iter, options, field) && bson_iter_type(&iter) == BSON_TYPE_INT32) { + return bson_iter_int32(&iter); + } + + return {}; +} + +template <> +bsoncxx::v1::stdx::optional get_field(mongoc_uri_t const* uri, char const* field) { + auto const options = libmongoc::uri_get_options(uri); // Never null. + + bson_iter_t iter = {}; + if (bson_iter_init_find_case(&iter, options, field) && bson_iter_type(&iter) == BSON_TYPE_BOOL) { + return bson_iter_bool(&iter); + } + + return {}; +} + +template <> +bsoncxx::v1::stdx::optional get_field(mongoc_uri_t const* uri, char const* field) { + auto const options = libmongoc::uri_get_options(uri); // Never null. + + bson_iter_t iter = {}; + if (bson_iter_init_find_case(&iter, options, field) && bson_iter_type(&iter) == BSON_TYPE_UTF8) { + std::uint32_t len = {}; + auto const data = bson_iter_utf8(&iter, &len); + return bsoncxx::v1::stdx::string_view{data, len}; + } + + return {}; +} + +bsoncxx::v1::stdx::optional get_credentials_field( + mongoc_uri_t const* uri, + char const* field) { + if (auto const creds = libmongoc::uri_get_credentials(uri)) { + bson_iter_t iter = {}; + if (bson_iter_init_find_case(&iter, creds, field) && bson_iter_type(&iter) == BSON_TYPE_DOCUMENT) { + std::uint8_t const* data = {}; + std::uint32_t len = {}; + bson_iter_document(&iter, &len, &data); + return bsoncxx::v1::document::view{data, len}; + } + } + + return {}; +} + +} // namespace + +uri::~uri() { + libmongoc::uri_destroy(to_mongoc(_impl)); +} + +uri::uri(uri&& other) noexcept : _impl{exchange(other._impl, nullptr)} {} + +uri& uri::operator=(uri&& other) noexcept { + if (this != &other) { + libmongoc::uri_destroy(to_mongoc(exchange(_impl, exchange(other._impl, nullptr)))); + } + + return *this; +} + +uri::uri(uri const& other) : _impl{libmongoc::uri_copy(to_mongoc(other._impl))} {} + +uri& uri::operator=(uri const& other) { + if (this != &other) { + libmongoc::uri_destroy(to_mongoc(exchange(_impl, libmongoc::uri_copy(to_mongoc(other._impl))))); + } + + return *this; +} + +uri::uri() : _impl{libmongoc::uri_new_with_error(k_default_uri, nullptr)} {} + +uri::uri(bsoncxx::v1::stdx::string_view v) + : _impl{[&]() -> void* { + bson_error_t error = {}; + if (mongoc_uri_t* ret = libmongoc::uri_new_with_error(std::string{v}.c_str(), &error)) { + return ret; + } + throw_exception(error); + }()} {} + +bsoncxx::v1::stdx::string_view uri::auth_mechanism() const { + if (auto const v = libmongoc::uri_get_auth_mechanism(to_mongoc(_impl))) { + return v; + } + return {}; +} + +bsoncxx::v1::stdx::string_view uri::auth_source() const { + if (auto const v = libmongoc::uri_get_auth_source(to_mongoc(_impl))) { + return v; + } + return {}; // Never null? +} + +std::vector uri::hosts() const { + std::vector ret; + + for (auto iter = libmongoc::uri_get_hosts(to_mongoc(_impl)); iter != nullptr; iter = iter->next) { + ret.push_back(host{iter->host, iter->port, iter->family}); + } + + return ret; +} + +bsoncxx::v1::stdx::string_view uri::database() const { + if (auto const v = libmongoc::uri_get_database(to_mongoc(_impl))) { + return v; + } + return {}; +} + +bsoncxx::v1::document::view uri::options() const { + return scoped_bson_view{libmongoc::uri_get_options(to_mongoc(_impl))}.view(); +} + +bsoncxx::v1::stdx::string_view uri::password() const { + if (auto const v = libmongoc::uri_get_password(to_mongoc(_impl))) { + return v; + } + return {}; +} + +v1::read_concern uri::read_concern() const { + return v1::read_concern::internal::make( + libmongoc::read_concern_copy(libmongoc::uri_get_read_concern(to_mongoc(_impl)))); +} + +v1::read_preference uri::read_preference() const { + return v1::read_preference::internal::make( + libmongoc::read_prefs_copy(libmongoc::uri_get_read_prefs_t(to_mongoc(_impl)))); +} + +bsoncxx::v1::stdx::string_view uri::replica_set() const { + if (auto const v = libmongoc::uri_get_replica_set(to_mongoc(_impl))) { + return v; + } + return {}; +} + +bool uri::tls() const { + return libmongoc::uri_get_tls(to_mongoc(_impl)); +} + +bsoncxx::v1::stdx::string_view uri::to_string() const { + return libmongoc::uri_get_string(to_mongoc(_impl)); +} + +bsoncxx::v1::stdx::string_view uri::username() const { + if (auto const v = libmongoc::uri_get_username(to_mongoc(_impl))) { + return v; + } + return {}; +} + +v1::write_concern uri::write_concern() const { + return v1::write_concern::internal::make( + libmongoc::write_concern_copy(libmongoc::uri_get_write_concern(to_mongoc(_impl)))); +} + +bsoncxx::v1::stdx::optional uri::appname() const { + if (auto const v = libmongoc::uri_get_appname(to_mongoc(_impl))) { + return v; + } + return {}; +} + +bsoncxx::v1::stdx::optional uri::auth_mechanism_properties() const { + return get_credentials_field(to_mongoc(_impl), MONGOC_URI_AUTHMECHANISMPROPERTIES); +} + +bsoncxx::v1::stdx::optional uri::credentials() const { + if (auto const v = libmongoc::uri_get_credentials(to_mongoc(_impl))) { + return scoped_bson_view{v}.view(); + } + return {}; // Never null? +} + +bsoncxx::v1::stdx::optional uri::srv_max_hosts() const { + return get_field(to_mongoc(_impl), MONGOC_URI_SRVMAXHOSTS); +} + +std::vector uri::compressors() const { + if (auto const doc = scoped_bson_view{libmongoc::uri_get_compressors(to_mongoc(_impl))}.view()) { + std::vector ret; + + for (auto const e : doc) { + ret.push_back(e.key()); + } + + return ret; + } + + return {}; +} + +bsoncxx::v1::stdx::optional uri::connect_timeout_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_CONNECTTIMEOUTMS); +} + +bsoncxx::v1::stdx::optional uri::direct_connection() const { + return get_field(to_mongoc(_impl), MONGOC_URI_DIRECTCONNECTION); +} + +bsoncxx::v1::stdx::optional uri::heartbeat_frequency_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_HEARTBEATFREQUENCYMS); +} + +bsoncxx::v1::stdx::optional uri::local_threshold_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_LOCALTHRESHOLDMS); +} + +bsoncxx::v1::stdx::optional uri::max_pool_size() const { + return get_field(to_mongoc(_impl), MONGOC_URI_MAXPOOLSIZE); +} + +bsoncxx::v1::stdx::optional uri::retry_reads() const { + return get_field(to_mongoc(_impl), MONGOC_URI_RETRYREADS); +} + +bsoncxx::v1::stdx::optional uri::retry_writes() const { + return get_field(to_mongoc(_impl), MONGOC_URI_RETRYWRITES); +} + +bsoncxx::v1::stdx::optional uri::server_selection_timeout_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_SERVERSELECTIONTIMEOUTMS); +} + +bsoncxx::v1::stdx::optional uri::server_selection_try_once() const { + return get_field(to_mongoc(_impl), MONGOC_URI_SERVERSELECTIONTRYONCE); +} + +uri& uri::server_selection_try_once(bool val) { + if (!libmongoc::uri_set_option_as_bool(to_mongoc(_impl), MONGOC_URI_SERVERSELECTIONTRYONCE, val)) { + throw v1::exception::internal::make(code::set_failure, MONGOC_URI_SERVERSELECTIONTRYONCE); + } + return *this; +} + +bsoncxx::v1::stdx::optional uri::socket_timeout_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_SOCKETTIMEOUTMS); +} + +bsoncxx::v1::stdx::optional uri::tls_allow_invalid_certificates() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSALLOWINVALIDCERTIFICATES); +} + +bsoncxx::v1::stdx::optional uri::tls_allow_invalid_hostnames() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSALLOWINVALIDHOSTNAMES); +} + +bsoncxx::v1::stdx::optional uri::tls_ca_file() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSCAFILE); +} + +bsoncxx::v1::stdx::optional uri::tls_certificate_key_file() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSCERTIFICATEKEYFILE); +} + +bsoncxx::v1::stdx::optional uri::tls_certificate_key_file_password() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSCERTIFICATEKEYFILEPASSWORD); +} + +bsoncxx::v1::stdx::optional uri::tls_disable_certificate_revocation_check() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSDISABLECERTIFICATEREVOCATIONCHECK); +} + +bsoncxx::v1::stdx::optional uri::tls_disable_ocsp_endpoint_check() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSDISABLEOCSPENDPOINTCHECK); +} + +bsoncxx::v1::stdx::optional uri::tls_insecure() const { + return get_field(to_mongoc(_impl), MONGOC_URI_TLSINSECURE); +} + +bsoncxx::v1::stdx::optional uri::wait_queue_timeout_ms() const { + return get_field(to_mongoc(_impl), MONGOC_URI_WAITQUEUETIMEOUTMS); +} + +bsoncxx::v1::stdx::optional uri::zlib_compression_level() const { + return get_field(to_mongoc(_impl), MONGOC_URI_ZLIBCOMPRESSIONLEVEL); +} + +std::error_category const& uri::error_category() { + class type final : public std::error_category { + char const* name() const noexcept override { + return "mongocxx::v1::uri"; + } + + std::string message(int v) const noexcept override { + switch (static_cast(v)) { + case code::zero: + return "zero"; + case code::set_failure: + return "could not set the requested URI option"; + default: + return std::string(this->name()) + ':' + std::to_string(v); + } + } + + bool equivalent(int v, std::error_condition const& ec) const noexcept override { + if (ec.category() == v1::source_error_category()) { + using condition = v1::source_errc; + + auto const source = static_cast(ec.value()); + + switch (static_cast(v)) { + case code::set_failure: + return source == condition::mongocxx; + + case code::zero: + default: + return false; + } + } + + if (ec.category() == v1::type_error_category()) { + using condition = v1::type_errc; + + auto const type = static_cast(ec.value()); + + switch (static_cast(v)) { + case code::set_failure: + return type == condition::invalid_argument; + + case code::zero: + default: + return false; + } + } + + return false; + } + }; + + static bsoncxx::immortal const instance; + + return instance.value(); +} + +uri::uri(void_ptr vp) : _impl{vp.impl} {} + +uri uri::internal::make(mongoc_uri_t* uri) { + v1::uri::void_ptr vp; + vp.impl = uri; + return v1::uri{vp}; +} + +mongoc_uri_t const* uri::internal::as_mongoc(uri const& self) { + return to_mongoc(self._impl); +} + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/lib/mongocxx/v1/uri.hh b/src/mongocxx/lib/mongocxx/v1/uri.hh new file mode 100644 index 0000000000..6939cd7531 --- /dev/null +++ b/src/mongocxx/lib/mongocxx/v1/uri.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 uri::internal { + public: + static uri make(mongoc_uri_t* uri); + + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(mongoc_uri_t const*) as_mongoc(uri const& self); +}; + +} // namespace v1 +} // namespace mongocxx diff --git a/src/mongocxx/lib/mongocxx/v1/write_concern.hh b/src/mongocxx/lib/mongocxx/v1/write_concern.hh index 93b69a820c..e4e6c05e55 100644 --- a/src/mongocxx/lib/mongocxx/v1/write_concern.hh +++ b/src/mongocxx/lib/mongocxx/v1/write_concern.hh @@ -28,7 +28,7 @@ class write_concern::internal { public: static MONGOCXX_ABI_EXPORT_CDECL_TESTING(write_concern) make(mongoc_write_concern_t* rc); - static mongoc_write_concern_t const* as_mongoc(write_concern const& self); + static MONGOCXX_ABI_EXPORT_CDECL_TESTING(mongoc_write_concern_t const*) as_mongoc(write_concern const& self); }; } // namespace v1 diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp index bf881ebda4..f7f0b95d88 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/client.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -88,7 +89,7 @@ client::client(mongocxx::v_noabi::uri const& uri, options::client const& options throw exception{error_code::k_ssl_not_supported}; } #endif - auto new_client = libmongoc::client_new_from_uri(uri._impl->uri_t); + auto new_client = libmongoc::client_new_from_uri(v_noabi::uri::internal::as_mongoc(uri)); if (!new_client) { // Shouldn't happen after checks above, but future libmongoc's may change behavior. throw exception{error_code::k_invalid_parameter, "could not construct client from URI"}; @@ -179,9 +180,7 @@ mongocxx::v_noabi::read_preference client::read_preference() const { } mongocxx::v_noabi::uri client::uri() const { - mongocxx::v_noabi::uri connection_string( - bsoncxx::make_unique(libmongoc::uri_copy(libmongoc::client_get_uri(_get_impl().client_t)))); - return connection_string; + return v1::uri::internal::make(libmongoc::uri_copy(libmongoc::client_get_uri(_get_impl().client_t))); } void client::write_concern_deprecated(mongocxx::v_noabi::write_concern wc) { diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/pool.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/pool.cpp index ee5f406f52..eb2485453d 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/pool.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/pool.cpp @@ -36,7 +36,7 @@ namespace mongocxx { namespace v_noabi { // Attempts to create a new client pool using the uri. Throws an exception upon error. -static mongoc_client_pool_t* construct_client_pool(mongoc_uri_t* uri) { +static mongoc_client_pool_t* construct_client_pool(mongoc_uri_t const* uri) { bson_error_t error; auto pool = libmongoc::client_pool_new_with_error(uri, &error); if (!pool) { @@ -57,7 +57,7 @@ void pool::_release(client* client) { pool::~pool() = default; pool::pool(uri const& uri, options::pool const& options) - : _impl{bsoncxx::make_unique(construct_client_pool(uri._impl->uri_t))} { + : _impl{bsoncxx::make_unique(construct_client_pool(v_noabi::uri::internal::as_mongoc(uri)))} { #if MONGOCXX_SSL_IS_ENABLED() if (options.client_opts().tls_opts()) { if (!uri.tls()) diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp index deab0c0392..7d34ba946f 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.cpp @@ -12,294 +12,43 @@ // 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 -#include +#include + +#include + +#include -#include +#include +#include +#include #include namespace mongocxx { namespace v_noabi { -namespace { - -// Some of the 'uri_get_*' string accessors may return nullptr. Check for this case and convert to -// the empty string. -std::string to_string_null_safe(char const* str) { - if (str == nullptr) { - return std::string{}; - } - return str; -} - -} // namespace - // NOLINTNEXTLINE(cert-err58-cpp): v_noabi backward compatibility. std::string const uri::k_default_uri = "mongodb://localhost:27017"; -uri::uri(std::unique_ptr&& implementation) : _impl{std::move(implementation)} {} - -uri::uri(bsoncxx::v_noabi::string::view_or_value uri_string) { - bson_error_t error; - - _impl = bsoncxx::make_unique(libmongoc::uri_new_with_error(uri_string.terminated().data(), &error)); - - if (_impl->uri_t == nullptr) { - throw logic_error{error_code::k_invalid_uri, error.message}; - } -} - -uri::uri(uri&&) noexcept = default; -uri& uri::operator=(uri&&) noexcept = default; - -uri::~uri() = default; - -std::string uri::auth_mechanism() const { - return to_string_null_safe(libmongoc::uri_get_auth_mechanism(_impl->uri_t)); -} - -std::string uri::auth_source() const { - return libmongoc::uri_get_auth_source(_impl->uri_t); -} - -std::string uri::database() const { - return to_string_null_safe(libmongoc::uri_get_database(_impl->uri_t)); -} - -std::vector uri::hosts() const { - std::vector result; - - for (auto host_list = libmongoc::uri_get_hosts(_impl->uri_t); host_list; host_list = host_list->next) { - result.push_back(host{host_list->host, host_list->port, host_list->family}); - } - - return result; -} - -bsoncxx::v_noabi::document::view uri::options() const { - bson_t const* const opts_bson = libmongoc::uri_get_options(_impl->uri_t); - return bsoncxx::v_noabi::document::view{::bson_get_data(opts_bson), opts_bson->len}; -} - -std::string uri::password() const { - return to_string_null_safe(libmongoc::uri_get_password(_impl->uri_t)); -} - -mongocxx::v_noabi::read_concern uri::read_concern() const { - 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 { - return v1::read_preference::internal::make( - libmongoc::read_prefs_copy(libmongoc::uri_get_read_prefs_t(_impl->uri_t))); -} - -std::string uri::replica_set() const { - return to_string_null_safe(libmongoc::uri_get_replica_set(_impl->uri_t)); -} - -std::string uri::to_string() const { - return libmongoc::uri_get_string(_impl->uri_t); -} - -bool uri::ssl() const { - return libmongoc::uri_get_tls(_impl->uri_t); -} - -bool uri::tls() const { - return libmongoc::uri_get_tls(_impl->uri_t); -} - -std::string uri::username() const { - return to_string_null_safe(libmongoc::uri_get_username(_impl->uri_t)); -} - -mongocxx::v_noabi::write_concern uri::write_concern() const { - return v1::write_concern::internal::make( - libmongoc::write_concern_copy(libmongoc::uri_get_write_concern(_impl->uri_t))); -} - -static bsoncxx::v_noabi::stdx::optional _string_option( - mongoc_uri_t* uri, - std::string opt_name) { - if (auto const value = libmongoc::uri_get_option_as_utf8(uri, opt_name.c_str(), nullptr)) { - return value; - } - return {}; -} - -static bsoncxx::v_noabi::stdx::optional _int32_option(mongoc_uri_t* uri, std::string opt_name) { - bson_iter_t iter; - bson_t const* options_bson = libmongoc::uri_get_options(uri); - - if (!bson_iter_init_find_case(&iter, options_bson, opt_name.c_str()) || !BSON_ITER_HOLDS_INT32(&iter)) { - return bsoncxx::v_noabi::stdx::nullopt; - } - return bson_iter_int32(&iter); -} - -static bsoncxx::v_noabi::stdx::optional _bool_option(mongoc_uri_t* uri, std::string opt_name) { - bson_iter_t iter; - bson_t const* options_bson = libmongoc::uri_get_options(uri); - - if (!bson_iter_init_find_case(&iter, options_bson, opt_name.c_str()) || !BSON_ITER_HOLDS_BOOL(&iter)) { - return bsoncxx::v_noabi::stdx::nullopt; - } - return bson_iter_bool(&iter); -} - -bsoncxx::v_noabi::stdx::optional uri::credentials() { - bson_t const* options_bson = libmongoc::uri_get_credentials(_impl->uri_t); - uint8_t const* data = bson_get_data(options_bson); - - return bsoncxx::v_noabi::document::view(data, options_bson->len); -} - -bsoncxx::v_noabi::stdx::optional uri::srv_max_hosts() const { - return _int32_option(_impl->uri_t, MONGOC_URI_SRVMAXHOSTS); -} - -static bsoncxx::v_noabi::stdx::optional _credential_document_option( - mongoc_uri_t* uri, - std::string opt_name) { - auto const options_bson = libmongoc::uri_get_credentials(uri); - - bson_iter_t iter = {}; - if (!bson_iter_init_find_case(&iter, options_bson, opt_name.c_str()) || !BSON_ITER_HOLDS_DOCUMENT(&iter)) { - return bsoncxx::v_noabi::stdx::nullopt; - } - - uint8_t const* data = {}; - uint32_t len = {}; - bson_iter_document(&iter, &len, &data); - return bsoncxx::v_noabi::document::view(data, len); -} - -bsoncxx::v_noabi::stdx::optional uri::appname() const { - return _string_option(_impl->uri_t, "appname"); -} - -// Special case. authMechanismProperties are stored as part of libmongoc's credentials. -bsoncxx::v_noabi::stdx::optional uri::auth_mechanism_properties() const { - return _credential_document_option(_impl->uri_t, "authMechanismProperties"); -} - -std::vector uri::compressors() const { - auto const compressors = libmongoc::uri_get_compressors(_impl->uri_t); - if (!compressors) { - // Should not happen. libmongoc will return an empty document even if there were no - // compressors present in the URI. - return {}; - } - - bson_iter_t iter = {}; - bson_iter_init(&iter, compressors); - - std::vector result; - while (bson_iter_next(&iter)) { - result.push_back(bsoncxx::v_noabi::stdx::string_view{bson_iter_key(&iter), bson_iter_key_len(&iter)}); - } - return result; -} - -bsoncxx::v_noabi::stdx::optional uri::connect_timeout_ms() const { - return _int32_option(_impl->uri_t, "connectTimeoutMS"); -} - -bsoncxx::v_noabi::stdx::optional uri::direct_connection() const { - return _bool_option(_impl->uri_t, "directConnection"); -} - -bsoncxx::v_noabi::stdx::optional uri::heartbeat_frequency_ms() const { - return _int32_option(_impl->uri_t, "heartbeatFrequencyMS"); -} - -bsoncxx::v_noabi::stdx::optional uri::local_threshold_ms() const { - return _int32_option(_impl->uri_t, "localThresholdMS"); -} - -bsoncxx::v_noabi::stdx::optional uri::max_pool_size() const { - return _int32_option(_impl->uri_t, "maxPoolSize"); -} - -bsoncxx::v_noabi::stdx::optional uri::retry_reads() const { - return _bool_option(_impl->uri_t, "retryReads"); -} - -bsoncxx::v_noabi::stdx::optional uri::retry_writes() const { - return _bool_option(_impl->uri_t, "retryWrites"); -} - -bsoncxx::v_noabi::stdx::optional uri::server_selection_timeout_ms() const { - return _int32_option(_impl->uri_t, "serverSelectionTimeoutMS"); -} - -bsoncxx::v_noabi::stdx::optional uri::server_selection_try_once() const { - return _bool_option(_impl->uri_t, "serverSelectionTryOnce"); -} - -void uri::server_selection_try_once(bool val) { - if (!mongoc_uri_set_option_as_bool(_impl->uri_t, "serverSelectionTryOnce", val)) { - throw exception{error_code::k_invalid_uri, "failed to set 'serverSelectionTryOnce' option"}; - } -} - -bsoncxx::v_noabi::stdx::optional uri::socket_timeout_ms() const { - return _int32_option(_impl->uri_t, "socketTimeoutMS"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_allow_invalid_certificates() const { - return _bool_option(_impl->uri_t, "tlsAllowInvalidCertificates"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_allow_invalid_hostnames() const { - return _bool_option(_impl->uri_t, "tlsAllowInvalidHostnames"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_ca_file() const { - return _string_option(_impl->uri_t, "tlsCAFile"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_certificate_key_file() const { - return _string_option(_impl->uri_t, "tlsCertificateKeyFile"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_certificate_key_file_password() const { - return _string_option(_impl->uri_t, "tlsCertificateKeyFilePassword"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_disable_certificate_revocation_check() const { - return _bool_option(_impl->uri_t, "tlsDisableCertificateRevocationCheck"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_disable_ocsp_endpoint_check() const { - return _bool_option(_impl->uri_t, "tlsDisableOCSPEndpointCheck"); -} - -bsoncxx::v_noabi::stdx::optional uri::tls_insecure() const { - return _bool_option(_impl->uri_t, "tlsInsecure"); +uri::uri(bsoncxx::v_noabi::string::view_or_value uri_string) try : _uri{uri_string.view()} { +} catch (v1::exception const& ex) { + throw v_noabi::logic_error{v_noabi::error_code::k_invalid_uri, ex.what()}; } -bsoncxx::v_noabi::stdx::optional uri::wait_queue_timeout_ms() const { - return _int32_option(_impl->uri_t, "waitQueueTimeoutMS"); +void uri::server_selection_try_once(bool val) try { + _uri.server_selection_try_once(val); +} catch (v1::exception const&) { + throw v_noabi::exception{v_noabi::error_code::k_invalid_uri, "failed to set 'serverSelectionTryOnce' option"}; } -bsoncxx::v_noabi::stdx::optional uri::zlib_compression_level() const { - return _int32_option(_impl->uri_t, "zlibCompressionLevel"); +mongoc_uri_t const* uri::internal::as_mongoc(uri const& self) { + return v1::uri::internal::as_mongoc(self._uri); } } // namespace v_noabi diff --git a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.hh b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.hh index 39b4d3d8ec..10d1220832 100644 --- a/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.hh +++ b/src/mongocxx/lib/mongocxx/v_noabi/mongocxx/uri.hh @@ -16,22 +16,16 @@ #include // IWYU pragma: export +// + #include namespace mongocxx { namespace v_noabi { -class uri::impl { +class uri::internal { public: - impl(mongoc_uri_t* uri) : uri_t(uri) {} - ~impl() { - libmongoc::uri_destroy(uri_t); - } - impl(impl&&) = delete; - impl& operator=(impl&&) = delete; - impl(impl const&) = delete; - impl& operator=(impl const&) = delete; - mongoc_uri_t* uri_t; + static mongoc_uri_t const* as_mongoc(uri const& self); }; } // namespace v_noabi diff --git a/src/mongocxx/test/CMakeLists.txt b/src/mongocxx/test/CMakeLists.txt index e566e4ba61..d1c8d6d36f 100644 --- a/src/mongocxx/test/CMakeLists.txt +++ b/src/mongocxx/test/CMakeLists.txt @@ -138,6 +138,7 @@ set(mongocxx_test_sources_v1 v1/transaction_options.cpp v1/update_many_options.cpp v1/update_one_options.cpp + v1/uri.cpp v1/write_concern.cpp ) diff --git a/src/mongocxx/test/v1/uri.cpp b/src/mongocxx/test/v1/uri.cpp new file mode 100644 index 0000000000..e4f40e1d65 --- /dev/null +++ b/src/mongocxx/test/v1/uri.cpp @@ -0,0 +1,725 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +namespace mongocxx { +namespace v1 { + +using code = uri::errc; + +namespace { + +template +void test_string(MemFn mem_fn, Mock& mock) { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto fn = mock.create_instance(); + + SECTION("null") { + fn->interpose([&](mongoc_uri_t const* ptr) -> char const* { + CHECK(ptr == identity); + return nullptr; + }); + CHECK((opts.*mem_fn)().data() == nullptr); + } + + SECTION("value") { + char const str = '\0'; + + fn->interpose([&](mongoc_uri_t const* ptr) -> char const* { + CHECK(ptr == identity); + return &str; + }); + CHECK((opts.*mem_fn)().data() == static_cast(&str)); + } +} + +template +void test_string_opt(MemFn mem_fn, Mock& mock) { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto fn = mock.create_instance(); + + SECTION("empty") { + fn->interpose([&](mongoc_uri_t const* ptr) -> char const* { + CHECK(ptr == identity); + return nullptr; + }); + + CHECK_FALSE((opts.*mem_fn)().has_value()); + } + + SECTION("value") { + char const str = '\0'; + + fn->interpose([&](mongoc_uri_t const* ptr) -> char const* { + CHECK(ptr == identity); + return &str; + }); + + auto const opt = (opts.*mem_fn)(); + REQUIRE(opt.has_value()); + CHECK(opt->data() == static_cast(&str)); + } +} + +template +void test_bool(MemFn mem_fn, Mock& mock) { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto const v = GENERATE(false, true); + + auto fn = mock.create_instance(); + fn->interpose([&](mongoc_uri_t const* ptr) -> bool { + CHECK(ptr == identity); + return v; + }); + CHECK((opts.*mem_fn)() == v); +} + +template +void test_doc(MemFn mem_fn, Mock& mock) { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + scoped_bson const doc{R"({"x": 1})"}; + + auto fn = mock.create_instance(); + fn->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return doc.bson(); + }); + CHECK((opts.*mem_fn)().data() == doc.view().data()); +} + +template +void test_doc_opt(MemFn mem_fn, Mock& mock) { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto fn = mock.create_instance(); + + SECTION("empty") { + fn->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return nullptr; + }); + + CHECK_FALSE((opts.*mem_fn)().has_value()); + } + + SECTION("value") { + scoped_bson const doc{R"({"x": 1})"}; + + fn->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return doc.bson(); + }); + + auto const opt = (opts.*mem_fn)(); + REQUIRE(opt.has_value()); + CHECK(opt->data() == doc.view().data()); + } +} + +template +void test_credentials_option(MemFn mem_fn, bsoncxx::v1::document::view doc, T const& expected) { + CAPTURE(doc); + CAPTURE(expected); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto credentials = libmongoc::uri_get_credentials.create_instance(); + credentials->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return scoped_bson_view{doc}.bson(); // Never null. + }); + + CHECK((opts.*mem_fn)() == expected); +} + +template +void test_option(MemFn mem_fn, bsoncxx::v1::document::view doc, T const& expected) { + CAPTURE(doc); + CAPTURE(expected); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto options = libmongoc::uri_get_options.create_instance(); + options->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return scoped_bson_view{doc}.bson(); // Never null. + }); + + CHECK((opts.*mem_fn)() == expected); +} + +void test_option_string( + bsoncxx::v1::stdx::optional (uri::*mem_fn)() const, + char const* field) { + auto const v = GENERATE(values({ + "x", + "abc", + })); + + scoped_bson const options{BCON_NEW(field, BCON_UTF8(v))}; + + test_option(mem_fn, options.view(), v); +} + +void test_option_int32(bsoncxx::v1::stdx::optional (uri::*mem_fn)() const, char const* field) { + using T = std::int32_t; + + auto const v = GENERATE(values({ + T{INT32_MIN}, + T{-1}, + T{0}, + T{1}, + T{INT32_MAX}, + })); + + scoped_bson const options{BCON_NEW(field, BCON_INT32(v))}; + + test_option(mem_fn, options.view(), v); +} + +void test_option_bool(bsoncxx::v1::stdx::optional (uri::*mem_fn)() const, char const* field) { + auto const v = GENERATE(false, true); + + scoped_bson const options{BCON_NEW(field, BCON_BOOL(v))}; + + test_option(mem_fn, options.view(), v); +} + +} // namespace + +TEST_CASE("error code", "[mongocxx][v1][uri][error]") { + using mongocxx::v1::source_errc; + using mongocxx::v1::type_errc; + + auto const& category = mongocxx::v1::uri::error_category(); + CHECK_THAT(category.name(), Catch::Matchers::Equals("mongocxx::v1::uri")); + + auto const zero_errc = make_error_condition(static_cast(0)); + + SECTION("unknown") { + std::error_code const ec = static_cast(-1); + + CHECK(ec.category() == category); + CHECK(ec.value() == -1); + CHECK(ec); + CHECK(ec.message() == std::string(category.name()) + ":-1"); + } + + SECTION("zero") { + std::error_code const ec = code::zero; + + CHECK(ec.category() == category); + CHECK(ec.value() == 0); + CHECK_FALSE(ec); + CHECK(ec.message() == "zero"); + + CHECK(ec != zero_errc); + CHECK(ec != source_errc::zero); + CHECK(ec != type_errc::zero); + } + + SECTION("non-zero") { + std::error_code const ec = code::set_failure; + + CHECK(ec.category() == category); + CHECK(ec.value() != static_cast(code::zero)); + CHECK(ec); + CHECK(ec.message() != "zero"); + + CHECK(ec != zero_errc); + CHECK(ec != source_errc::zero); + CHECK(ec != type_errc::zero); + } + + SECTION("source") { + CHECK(make_error_code(code::set_failure) == source_errc::mongocxx); + } + + SECTION("type") { + CHECK(make_error_code(code::set_failure) == type_errc::invalid_argument); + } +} + +TEST_CASE("exceptions", "[mongocxx][v1][uri]") { + SECTION("initialization") { + try { + (void)uri{"invalid"}; + FAIL("should not reach this point"); + } catch (v1::exception const& ex) { + CHECK(ex.code() == v1::source_errc::mongoc); + CHECK(ex.code().value() == MONGOC_ERROR_COMMAND_INVALID_ARG); + } + } + + SECTION("set_failure") { + auto const v = GENERATE(false, true); + + uri opts; + + auto const ptr = uri::internal::as_mongoc(opts); + + auto set_option_as_bool = libmongoc::uri_set_option_as_bool.create_instance(); + + set_option_as_bool->interpose([&](mongoc_uri_t* uri, char const* option_orig, bool value) -> bool { + CHECK(uri == ptr); + CHECK_THAT(option_orig, Catch::Matchers::Equals(MONGOC_URI_SERVERSELECTIONTRYONCE)); + CHECK(value == v); + + return false; + }); + + try { + opts.server_selection_try_once(v); + FAIL("should not reach this point"); + } catch (v1::exception const& ex) { + CHECK(ex.code() == code::set_failure); + CHECK_THAT( + ex.what(), + Catch::Matchers::ContainsSubstring("could not set the requested URI option") && + Catch::Matchers::ContainsSubstring(MONGOC_URI_SERVERSELECTIONTRYONCE)); + } + } +} + +TEST_CASE("ownership", "[mongocxx][v1][uri]") { + uri source{"mongodb://localhost:27017/source"}; + uri target{"mongodb://localhost:27017/target"}; + + REQUIRE(source.database() == "source"); + REQUIRE(target.database() == "target"); + + auto const source_value = source.database(); + + SECTION("move") { + auto move = std::move(source); + + // source is in an assign-or-move-only state. + + CHECK(move.database() == source_value); + + target = std::move(move); + + // source is in an assign-or-move-only state. + + CHECK(target.database() == source_value); + } + + SECTION("copy") { + auto copy = source; + + CHECK(source.database() == source_value); + CHECK(copy.database() == source_value); + + target = copy; + + CHECK(copy.database() == source_value); + CHECK(target.database() == source_value); + } +} + +TEST_CASE("default", "[mongocxx][v1][uri]") { + uri const opts; + + CHECK(opts.auth_mechanism().empty()); + CHECK(opts.auth_source() == "admin"); + CHECK(opts.hosts().size() == 1u); // "localhost:27017" + CHECK(opts.database().empty()); + CHECK(opts.options().empty()); + CHECK(opts.password().empty()); + CHECK(opts.read_concern() == v1::read_concern{}); + CHECK(opts.read_preference() == v1::read_preference{}); + CHECK(opts.replica_set().empty()); + CHECK_FALSE(opts.tls()); + CHECK(opts.to_string() == uri::k_default_uri); + CHECK(opts.username().empty()); + CHECK(opts.write_concern() == v1::write_concern{}); + CHECK_FALSE(opts.appname().has_value()); + CHECK_FALSE(opts.auth_mechanism_properties().has_value()); + CHECK_FALSE(opts.credentials() == scoped_bson{R"({"authMechanism": {}})"}.view()); // Never empty. + CHECK_FALSE(opts.srv_max_hosts().has_value()); + CHECK(opts.compressors().empty()); + CHECK_FALSE(opts.connect_timeout_ms().has_value()); + CHECK_FALSE(opts.direct_connection().has_value()); + CHECK_FALSE(opts.heartbeat_frequency_ms().has_value()); + CHECK_FALSE(opts.local_threshold_ms().has_value()); + CHECK_FALSE(opts.max_pool_size().has_value()); + CHECK_FALSE(opts.retry_reads().has_value()); + CHECK_FALSE(opts.retry_writes().has_value()); + CHECK_FALSE(opts.server_selection_timeout_ms().has_value()); + CHECK_FALSE(opts.server_selection_try_once().has_value()); + CHECK_FALSE(opts.socket_timeout_ms().has_value()); + CHECK_FALSE(opts.tls_allow_invalid_certificates().has_value()); + CHECK_FALSE(opts.tls_allow_invalid_hostnames().has_value()); + CHECK_FALSE(opts.tls_ca_file().has_value()); + CHECK_FALSE(opts.tls_certificate_key_file().has_value()); + CHECK_FALSE(opts.tls_certificate_key_file_password().has_value()); + CHECK_FALSE(opts.tls_disable_certificate_revocation_check().has_value()); + CHECK_FALSE(opts.tls_disable_ocsp_endpoint_check().has_value()); + CHECK_FALSE(opts.tls_insecure().has_value()); + CHECK_FALSE(opts.wait_queue_timeout_ms().has_value()); + CHECK_FALSE(opts.zlib_compression_level().has_value()); +} + +TEST_CASE("auth_mechanism", "[mongocxx][v1][uri]") { + test_string(&uri::auth_mechanism, libmongoc::uri_get_auth_mechanism); +} + +TEST_CASE("auth_source", "[mongocxx][v1][uri]") { + test_string(&uri::auth_source, libmongoc::uri_get_auth_source); +} + +TEST_CASE("hosts", "[mongocxx][v1][uri]") { + std::array v = {{ + { + nullptr, + "mongodb://localhost", + "mongodb://localhost:27017", + 27017u, + {}, // family + {}, // padding + }, + { + nullptr, + "mongodb://localhost", + "mongodb://localhost:27018", + 27018u, + {}, // family + {}, // padding + }, + { + nullptr, + "mongodb://localhost", + "mongodb://localhost:27019", + 27019u, + {}, // family + {}, // padding + }, + }}; + + auto const len = GENERATE(as{}, 0u, 1u, 2u, 3u); + REQUIRE(len <= v.size()); + + if (len > 1) { + v[0].next = &v[1]; + } + + if (len > 2) { + v[1].next = &v[2]; + } + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto hosts = libmongoc::uri_get_hosts.create_instance(); + hosts->interpose([&](mongoc_uri_t const* ptr) -> mongoc_host_list_t const* { + CHECK(ptr == identity); + return len == 0 ? nullptr : v.data(); + }); + + auto const list = opts.hosts(); + REQUIRE(list.size() == len); + for (std::size_t i = 0u; i < len; ++i) { + auto const& actual = list[i]; + auto const& expected = v[i]; + + CHECK(actual.port == expected.port); + } +} + +TEST_CASE("database", "[mongocxx][v1][uri]") { + test_string(&uri::database, libmongoc::uri_get_database); +} + +TEST_CASE("options", "[mongocxx][v1][uri]") { + test_doc(&uri::options, libmongoc::uri_get_options); +} + +TEST_CASE("password", "[mongocxx][v1][uri]") { + test_string(&uri::password, libmongoc::uri_get_password); +} + +TEST_CASE("read_concern", "[mongocxx][v1][uri]") { + using T = v1::read_concern; + + auto const v = GENERATE(values({ + T{}, + T{}.acknowledge_level(T::level::k_majority), + })); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto read_concern = libmongoc::uri_get_read_concern.create_instance(); + read_concern->interpose([&](mongoc_uri_t const* ptr) -> mongoc_read_concern_t const* { + CHECK(ptr == identity); + return v1::read_concern::internal::as_mongoc(v); + }); + + CHECK(opts.read_concern() == v); +} + +TEST_CASE("read_preference", "[mongocxx][v1][uri]") { + using T = mongocxx::v1::read_preference; + + auto const v = GENERATE(values({ + T{}, + T{}.mode(T::read_mode::k_secondary), + T{}.tags(scoped_bson{R"([1, 2.0, "3"])"}.array_view()), + })); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto read_preference = libmongoc::uri_get_read_prefs_t.create_instance(); + read_preference->interpose([&](mongoc_uri_t const* ptr) -> mongoc_read_prefs_t const* { + CHECK(ptr == identity); + return v1::read_preference::internal::as_mongoc(v); + }); + + CHECK(opts.read_preference() == v); +} + +TEST_CASE("replica_set", "[mongocxx][v1][uri]") { + test_string(&uri::replica_set, libmongoc::uri_get_replica_set); +} + +TEST_CASE("tls", "[mongocxx][v1][uri]") { + test_bool(&uri::tls, libmongoc::uri_get_tls); +} + +TEST_CASE("to_string", "[mongocxx][v1][uri]") { + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + char const str = '\0'; + + auto fn = libmongoc::uri_get_string.create_instance(); + fn->interpose([&](mongoc_uri_t const* ptr) -> char const* { + CHECK(ptr == identity); + return &str; // Never null. + }); + CHECK(opts.to_string().data() == static_cast(&str)); +} + +TEST_CASE("username", "[mongocxx][v1][uri]") { + test_string(&uri::username, libmongoc::uri_get_username); +} + +TEST_CASE("write_concern", "[mongocxx][v1][uri]") { + using T = mongocxx::v1::write_concern; + + auto const v = GENERATE(values({ + T{}, + T{}.acknowledge_level(T::level::k_majority), + T{}.tag("abc"), + })); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto write_concern = libmongoc::uri_get_write_concern.create_instance(); + write_concern->interpose([&](mongoc_uri_t const* ptr) -> mongoc_write_concern_t const* { + CHECK(ptr == identity); + return v1::write_concern::internal::as_mongoc(v); + }); + + CHECK(opts.write_concern() == v); +} + +TEST_CASE("appname", "[mongocxx][v1][uri]") { + test_string_opt(&uri::appname, libmongoc::uri_get_appname); +} + +TEST_CASE("auth_mechanism_properties", "[mongocxx][v1][uri]") { + auto const v = GENERATE(R"({"x": 1})", R"({"y": 2})"); + + scoped_bson const creds{BCON_NEW(MONGOC_URI_AUTHMECHANISMPROPERTIES, BCON_DOCUMENT(scoped_bson{v}.bson()))}; + scoped_bson const expected{v}; + + test_credentials_option(&uri::auth_mechanism_properties, creds.view(), expected.view()); +} + +TEST_CASE("credentials", "[mongocxx][v1][uri]") { + test_doc_opt(&uri::credentials, libmongoc::uri_get_credentials); +} + +TEST_CASE("srv_max_hosts", "[mongocxx][v1][uri]") { + test_option_int32(&uri::srv_max_hosts, MONGOC_URI_SRVMAXHOSTS); +} + +TEST_CASE("compressors", "[mongocxx][v1][uri]") { + scoped_bson v; + std::size_t len = {}; + + std::tie(v, len) = GENERATE( + table({ + {scoped_bson{}, 0u}, + {scoped_bson{R"({"x": 1})"}, 1u}, + {scoped_bson{R"({"a": 1, "b": 2.0, "c": "three"})"}, 3u}, + })); + + uri const opts; + auto const identity = uri::internal::as_mongoc(opts); + + auto compressors = libmongoc::uri_get_compressors.create_instance(); + compressors->interpose([&](mongoc_uri_t const* ptr) -> bson_t const* { + CHECK(ptr == identity); + return len == 0u ? nullptr : v.bson(); + }); + + auto const list = opts.compressors(); + REQUIRE(list.size() == len); + + auto iter = v.view().begin(); + for (auto const e : list) { + CHECK(static_cast(e.data()) == static_cast(iter->key().data())); + ++iter; + } +} + +TEST_CASE("connect_timeout_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::connect_timeout_ms, MONGOC_URI_CONNECTTIMEOUTMS); +} + +TEST_CASE("direct_connection", "[mongocxx][v1][uri]") { + test_option_bool(&uri::direct_connection, MONGOC_URI_DIRECTCONNECTION); +} + +TEST_CASE("heartbeat_frequency_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::heartbeat_frequency_ms, MONGOC_URI_HEARTBEATFREQUENCYMS); +} + +TEST_CASE("local_threshold_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::local_threshold_ms, MONGOC_URI_LOCALTHRESHOLDMS); +} + +TEST_CASE("max_pool_size", "[mongocxx][v1][uri]") { + test_option_int32(&uri::max_pool_size, MONGOC_URI_MAXPOOLSIZE); +} + +TEST_CASE("retry_reads", "[mongocxx][v1][uri]") { + test_option_bool(&uri::retry_reads, MONGOC_URI_RETRYREADS); +} + +TEST_CASE("retry_writes", "[mongocxx][v1][uri]") { + test_option_bool(&uri::retry_writes, MONGOC_URI_RETRYWRITES); +} + +TEST_CASE("server_selection_timeout_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::server_selection_timeout_ms, MONGOC_URI_SERVERSELECTIONTIMEOUTMS); +} + +TEST_CASE("server_selection_try_once", "[mongocxx][v1][uri]") { + SECTION("get") { + test_option_bool(&uri::server_selection_try_once, MONGOC_URI_SERVERSELECTIONTRYONCE); + } + + SECTION("set") { + auto const v = GENERATE(false, true); + + CHECK(uri{}.server_selection_try_once(v).server_selection_try_once() == v); + } +} + +TEST_CASE("socket_timeout_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::socket_timeout_ms, MONGOC_URI_SOCKETTIMEOUTMS); +} + +TEST_CASE("tls_allow_invalid_certificates", "[mongocxx][v1][uri]") { + test_option_bool(&uri::tls_allow_invalid_certificates, MONGOC_URI_TLSALLOWINVALIDCERTIFICATES); +} + +TEST_CASE("tls_allow_invalid_hostnames", "[mongocxx][v1][uri]") { + test_option_bool(&uri::tls_allow_invalid_hostnames, MONGOC_URI_TLSALLOWINVALIDHOSTNAMES); +} + +TEST_CASE("tls_ca_file", "[mongocxx][v1][uri]") { + test_option_string(&uri::tls_ca_file, MONGOC_URI_TLSCAFILE); +} + +TEST_CASE("tls_certificate_key_file", "[mongocxx][v1][uri]") { + test_option_string(&uri::tls_certificate_key_file, MONGOC_URI_TLSCERTIFICATEKEYFILE); +} + +TEST_CASE("tls_certificate_key_file_password", "[mongocxx][v1][uri]") { + test_option_string(&uri::tls_certificate_key_file_password, MONGOC_URI_TLSCERTIFICATEKEYFILEPASSWORD); +} + +TEST_CASE("tls_disable_certificate_revocation_check", "[mongocxx][v1][uri]") { + test_option_bool(&uri::tls_disable_certificate_revocation_check, MONGOC_URI_TLSDISABLECERTIFICATEREVOCATIONCHECK); +} + +TEST_CASE("tls_disable_ocsp_endpoint_check", "[mongocxx][v1][uri]") { + test_option_bool(&uri::tls_disable_ocsp_endpoint_check, MONGOC_URI_TLSDISABLEOCSPENDPOINTCHECK); +} + +TEST_CASE("tls_insecure", "[mongocxx][v1][uri]") { + test_option_bool(&uri::tls_insecure, MONGOC_URI_TLSINSECURE); +} + +TEST_CASE("wait_queue_timeout_ms", "[mongocxx][v1][uri]") { + test_option_int32(&uri::wait_queue_timeout_ms, MONGOC_URI_WAITQUEUETIMEOUTMS); +} + +TEST_CASE("zlib_compression_level", "[mongocxx][v1][uri]") { + test_option_int32(&uri::zlib_compression_level, MONGOC_URI_ZLIBCOMPRESSIONLEVEL); +} + +} // namespace v1 +} // namespace mongocxx