#include "mongoc-opts-helpers-private.h"
#include "mongoc-opts-private.h"
#include "mongoc-error.h"
#include "mongoc-util-private.h"
#include "mongoc-client-private.h"

/**************************************************
 *
 * Generated by build/generate-opts.py.
 *
 * DO NOT EDIT THIS FILE.
 *
 *************************************************/
/* clang-format off */


bool
_mongoc_insert_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_insert_one_opts_t *mongoc_insert_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_insert_one_opts->crud.writeConcern = NULL;
   mongoc_insert_one_opts->crud.write_concern_owned = false;
   mongoc_insert_one_opts->crud.client_session = NULL;
   mongoc_insert_one_opts->crud.validate = _mongoc_default_insert_vflags;
   memset (&mongoc_insert_one_opts->crud.comment, 0, sizeof (bson_value_t));
   mongoc_insert_one_opts->bypass = false;
   bson_init (&mongoc_insert_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_insert_one_opts->crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_insert_one_opts->crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_insert_one_opts->crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_insert_one_opts->crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_insert_one_opts->crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_insert_one_opts->bypass,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_insert_one_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_insert_one_opts_cleanup (mongoc_insert_one_opts_t *mongoc_insert_one_opts)
{
   if (mongoc_insert_one_opts->crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_insert_one_opts->crud.writeConcern);
   }
   bson_value_destroy (&mongoc_insert_one_opts->crud.comment);
   bson_destroy (&mongoc_insert_one_opts->extra);
}

bool
_mongoc_insert_many_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_insert_many_opts_t *mongoc_insert_many_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_insert_many_opts->crud.writeConcern = NULL;
   mongoc_insert_many_opts->crud.write_concern_owned = false;
   mongoc_insert_many_opts->crud.client_session = NULL;
   mongoc_insert_many_opts->crud.validate = _mongoc_default_insert_vflags;
   memset (&mongoc_insert_many_opts->crud.comment, 0, sizeof (bson_value_t));
   mongoc_insert_many_opts->ordered = true;
   mongoc_insert_many_opts->bypass = false;
   bson_init (&mongoc_insert_many_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_insert_many_opts->crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_insert_many_opts->crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_insert_many_opts->crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_insert_many_opts->crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_insert_many_opts->crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "ordered")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_insert_many_opts->ordered,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_insert_many_opts->bypass,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_insert_many_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_insert_many_opts_cleanup (mongoc_insert_many_opts_t *mongoc_insert_many_opts)
{
   if (mongoc_insert_many_opts->crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_insert_many_opts->crud.writeConcern);
   }
   bson_value_destroy (&mongoc_insert_many_opts->crud.comment);
   bson_destroy (&mongoc_insert_many_opts->extra);
}

bool
_mongoc_delete_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_delete_one_opts_t *mongoc_delete_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_delete_one_opts->delete.crud.writeConcern = NULL;
   mongoc_delete_one_opts->delete.crud.write_concern_owned = false;
   mongoc_delete_one_opts->delete.crud.client_session = NULL;
   mongoc_delete_one_opts->delete.crud.validate = BSON_VALIDATE_NONE;
   memset (&mongoc_delete_one_opts->delete.crud.comment, 0, sizeof (bson_value_t));
   bson_init (&mongoc_delete_one_opts->delete.collation);
   memset (&mongoc_delete_one_opts->delete.hint, 0, sizeof (bson_value_t));
   bson_init (&mongoc_delete_one_opts->delete.let);
   bson_init (&mongoc_delete_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_delete_one_opts->delete.crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_delete_one_opts->delete.let,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_delete_one_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_delete_one_opts_cleanup (mongoc_delete_one_opts_t *mongoc_delete_one_opts)
{
   if (mongoc_delete_one_opts->delete.crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_delete_one_opts->delete.crud.writeConcern);
   }
   bson_value_destroy (&mongoc_delete_one_opts->delete.crud.comment);
   bson_destroy (&mongoc_delete_one_opts->delete.collation);
   bson_value_destroy (&mongoc_delete_one_opts->delete.hint);
   bson_destroy (&mongoc_delete_one_opts->delete.let);
   bson_destroy (&mongoc_delete_one_opts->extra);
}

bool
_mongoc_delete_many_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_delete_many_opts_t *mongoc_delete_many_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_delete_many_opts->delete.crud.writeConcern = NULL;
   mongoc_delete_many_opts->delete.crud.write_concern_owned = false;
   mongoc_delete_many_opts->delete.crud.client_session = NULL;
   mongoc_delete_many_opts->delete.crud.validate = BSON_VALIDATE_NONE;
   memset (&mongoc_delete_many_opts->delete.crud.comment, 0, sizeof (bson_value_t));
   bson_init (&mongoc_delete_many_opts->delete.collation);
   memset (&mongoc_delete_many_opts->delete.hint, 0, sizeof (bson_value_t));
   bson_init (&mongoc_delete_many_opts->delete.let);
   bson_init (&mongoc_delete_many_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_delete_many_opts->delete.crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_delete_many_opts->delete.let,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_delete_many_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_delete_many_opts_cleanup (mongoc_delete_many_opts_t *mongoc_delete_many_opts)
{
   if (mongoc_delete_many_opts->delete.crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_delete_many_opts->delete.crud.writeConcern);
   }
   bson_value_destroy (&mongoc_delete_many_opts->delete.crud.comment);
   bson_destroy (&mongoc_delete_many_opts->delete.collation);
   bson_value_destroy (&mongoc_delete_many_opts->delete.hint);
   bson_destroy (&mongoc_delete_many_opts->delete.let);
   bson_destroy (&mongoc_delete_many_opts->extra);
}

bool
_mongoc_update_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_update_one_opts_t *mongoc_update_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_update_one_opts->update.crud.writeConcern = NULL;
   mongoc_update_one_opts->update.crud.write_concern_owned = false;
   mongoc_update_one_opts->update.crud.client_session = NULL;
   mongoc_update_one_opts->update.crud.validate = _mongoc_default_update_vflags;
   memset (&mongoc_update_one_opts->update.crud.comment, 0, sizeof (bson_value_t));
   mongoc_update_one_opts->update.bypass = false;
   bson_init (&mongoc_update_one_opts->update.collation);
   memset (&mongoc_update_one_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_update_one_opts->update.upsert = false;
   bson_init (&mongoc_update_one_opts->update.let);
   bson_init (&mongoc_update_one_opts->arrayFilters);
   bson_init (&mongoc_update_one_opts->sort);
   bson_init (&mongoc_update_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_update_one_opts->update.crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_update_one_opts->update.crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_update_one_opts->update.crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_update_one_opts->update.crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_update_one_opts->update.crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_update_one_opts->update.bypass,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_update_one_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_update_one_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_update_one_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_update_one_opts->update.let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "arrayFilters")) {
         if (!_mongoc_convert_array (
               client,
               &iter,
               &mongoc_update_one_opts->arrayFilters,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sort")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_update_one_opts->sort,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_update_one_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_update_one_opts_cleanup (mongoc_update_one_opts_t *mongoc_update_one_opts)
{
   if (mongoc_update_one_opts->update.crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_update_one_opts->update.crud.writeConcern);
   }
   bson_value_destroy (&mongoc_update_one_opts->update.crud.comment);
   bson_destroy (&mongoc_update_one_opts->update.collation);
   bson_value_destroy (&mongoc_update_one_opts->update.hint);
   bson_destroy (&mongoc_update_one_opts->update.let);
   bson_destroy (&mongoc_update_one_opts->arrayFilters);
   bson_destroy (&mongoc_update_one_opts->sort);
   bson_destroy (&mongoc_update_one_opts->extra);
}

bool
_mongoc_update_many_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_update_many_opts_t *mongoc_update_many_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_update_many_opts->update.crud.writeConcern = NULL;
   mongoc_update_many_opts->update.crud.write_concern_owned = false;
   mongoc_update_many_opts->update.crud.client_session = NULL;
   mongoc_update_many_opts->update.crud.validate = _mongoc_default_update_vflags;
   memset (&mongoc_update_many_opts->update.crud.comment, 0, sizeof (bson_value_t));
   mongoc_update_many_opts->update.bypass = false;
   bson_init (&mongoc_update_many_opts->update.collation);
   memset (&mongoc_update_many_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_update_many_opts->update.upsert = false;
   bson_init (&mongoc_update_many_opts->update.let);
   bson_init (&mongoc_update_many_opts->arrayFilters);
   bson_init (&mongoc_update_many_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_update_many_opts->update.crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_update_many_opts->update.crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_update_many_opts->update.crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_update_many_opts->update.crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_update_many_opts->update.crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_update_many_opts->update.bypass,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_update_many_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_update_many_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_update_many_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_update_many_opts->update.let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "arrayFilters")) {
         if (!_mongoc_convert_array (
               client,
               &iter,
               &mongoc_update_many_opts->arrayFilters,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_update_many_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_update_many_opts_cleanup (mongoc_update_many_opts_t *mongoc_update_many_opts)
{
   if (mongoc_update_many_opts->update.crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_update_many_opts->update.crud.writeConcern);
   }
   bson_value_destroy (&mongoc_update_many_opts->update.crud.comment);
   bson_destroy (&mongoc_update_many_opts->update.collation);
   bson_value_destroy (&mongoc_update_many_opts->update.hint);
   bson_destroy (&mongoc_update_many_opts->update.let);
   bson_destroy (&mongoc_update_many_opts->arrayFilters);
   bson_destroy (&mongoc_update_many_opts->extra);
}

bool
_mongoc_replace_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_replace_one_opts_t *mongoc_replace_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_replace_one_opts->update.crud.writeConcern = NULL;
   mongoc_replace_one_opts->update.crud.write_concern_owned = false;
   mongoc_replace_one_opts->update.crud.client_session = NULL;
   mongoc_replace_one_opts->update.crud.validate = _mongoc_default_replace_vflags;
   memset (&mongoc_replace_one_opts->update.crud.comment, 0, sizeof (bson_value_t));
   mongoc_replace_one_opts->update.bypass = false;
   bson_init (&mongoc_replace_one_opts->update.collation);
   memset (&mongoc_replace_one_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_replace_one_opts->update.upsert = false;
   bson_init (&mongoc_replace_one_opts->update.let);
   bson_init (&mongoc_replace_one_opts->sort);
   bson_init (&mongoc_replace_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_replace_one_opts->update.crud.writeConcern,
               error)) {
            return false;
         }

         mongoc_replace_one_opts->update.crud.write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_replace_one_opts->update.crud.client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_replace_one_opts->update.crud.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_replace_one_opts->update.crud.comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_replace_one_opts->update.bypass,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_replace_one_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_replace_one_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_replace_one_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_replace_one_opts->update.let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sort")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_replace_one_opts->sort,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_replace_one_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_replace_one_opts_cleanup (mongoc_replace_one_opts_t *mongoc_replace_one_opts)
{
   if (mongoc_replace_one_opts->update.crud.write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_replace_one_opts->update.crud.writeConcern);
   }
   bson_value_destroy (&mongoc_replace_one_opts->update.crud.comment);
   bson_destroy (&mongoc_replace_one_opts->update.collation);
   bson_value_destroy (&mongoc_replace_one_opts->update.hint);
   bson_destroy (&mongoc_replace_one_opts->update.let);
   bson_destroy (&mongoc_replace_one_opts->sort);
   bson_destroy (&mongoc_replace_one_opts->extra);
}

bool
_mongoc_bulk_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_opts_t *mongoc_bulk_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_bulk_opts->writeConcern = NULL;
   mongoc_bulk_opts->write_concern_owned = false;
   mongoc_bulk_opts->ordered = true;
   mongoc_bulk_opts->client_session = NULL;
   bson_init (&mongoc_bulk_opts->let);
   memset (&mongoc_bulk_opts->comment, 0, sizeof (bson_value_t));
   bson_init (&mongoc_bulk_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_bulk_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_bulk_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "ordered")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_opts->ordered,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_bulk_opts->client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_opts->let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_bulk_opts->comment,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_opts_cleanup (mongoc_bulk_opts_t *mongoc_bulk_opts)
{
   if (mongoc_bulk_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_bulk_opts->writeConcern);
   }
   bson_destroy (&mongoc_bulk_opts->let);
   bson_value_destroy (&mongoc_bulk_opts->comment);
   bson_destroy (&mongoc_bulk_opts->extra);
}

bool
_mongoc_bulk_insert_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_insert_opts_t *mongoc_bulk_insert_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_bulk_insert_opts->validate = _mongoc_default_insert_vflags;
   bson_init (&mongoc_bulk_insert_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_bulk_insert_opts->validate,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_insert_opts_cleanup (mongoc_bulk_insert_opts_t *mongoc_bulk_insert_opts)
{
   bson_destroy (&mongoc_bulk_insert_opts->extra);
}

bool
_mongoc_bulk_update_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_update_one_opts_t *mongoc_bulk_update_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_bulk_update_one_opts->update.validate = _mongoc_default_update_vflags;
   bson_init (&mongoc_bulk_update_one_opts->update.collation);
   memset (&mongoc_bulk_update_one_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_bulk_update_one_opts->update.upsert = false;
   mongoc_bulk_update_one_opts->update.multi = false;
   bson_init (&mongoc_bulk_update_one_opts->sort);
   bson_init (&mongoc_bulk_update_one_opts->arrayFilters);
   bson_init (&mongoc_bulk_update_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->update.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "multi")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->update.multi,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sort")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->sort,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "arrayFilters")) {
         if (!_mongoc_convert_array (
               client,
               &iter,
               &mongoc_bulk_update_one_opts->arrayFilters,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_update_one_opts_cleanup (mongoc_bulk_update_one_opts_t *mongoc_bulk_update_one_opts)
{
   bson_destroy (&mongoc_bulk_update_one_opts->update.collation);
   bson_value_destroy (&mongoc_bulk_update_one_opts->update.hint);
   bson_destroy (&mongoc_bulk_update_one_opts->sort);
   bson_destroy (&mongoc_bulk_update_one_opts->arrayFilters);
   bson_destroy (&mongoc_bulk_update_one_opts->extra);
}

bool
_mongoc_bulk_update_many_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_update_many_opts_t *mongoc_bulk_update_many_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_bulk_update_many_opts->update.validate = _mongoc_default_update_vflags;
   bson_init (&mongoc_bulk_update_many_opts->update.collation);
   memset (&mongoc_bulk_update_many_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_bulk_update_many_opts->update.upsert = false;
   mongoc_bulk_update_many_opts->update.multi = true;
   bson_init (&mongoc_bulk_update_many_opts->arrayFilters);
   bson_init (&mongoc_bulk_update_many_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->update.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "multi")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->update.multi,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "arrayFilters")) {
         if (!_mongoc_convert_array (
               client,
               &iter,
               &mongoc_bulk_update_many_opts->arrayFilters,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_update_many_opts_cleanup (mongoc_bulk_update_many_opts_t *mongoc_bulk_update_many_opts)
{
   bson_destroy (&mongoc_bulk_update_many_opts->update.collation);
   bson_value_destroy (&mongoc_bulk_update_many_opts->update.hint);
   bson_destroy (&mongoc_bulk_update_many_opts->arrayFilters);
   bson_destroy (&mongoc_bulk_update_many_opts->extra);
}

bool
_mongoc_bulk_replace_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_replace_one_opts_t *mongoc_bulk_replace_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_bulk_replace_one_opts->update.validate = _mongoc_default_replace_vflags;
   bson_init (&mongoc_bulk_replace_one_opts->update.collation);
   memset (&mongoc_bulk_replace_one_opts->update.hint, 0, sizeof (bson_value_t));
   mongoc_bulk_replace_one_opts->update.upsert = false;
   mongoc_bulk_replace_one_opts->update.multi = false;
   bson_init (&mongoc_bulk_replace_one_opts->sort);
   bson_init (&mongoc_bulk_replace_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "validate")) {
         if (!_mongoc_convert_validate_flags (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->update.validate,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->update.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->update.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "upsert")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->update.upsert,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "multi")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->update.multi,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sort")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_replace_one_opts->sort,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_replace_one_opts_cleanup (mongoc_bulk_replace_one_opts_t *mongoc_bulk_replace_one_opts)
{
   bson_destroy (&mongoc_bulk_replace_one_opts->update.collation);
   bson_value_destroy (&mongoc_bulk_replace_one_opts->update.hint);
   bson_destroy (&mongoc_bulk_replace_one_opts->sort);
   bson_destroy (&mongoc_bulk_replace_one_opts->extra);
}

bool
_mongoc_bulk_remove_one_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_remove_one_opts_t *mongoc_bulk_remove_one_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   bson_init (&mongoc_bulk_remove_one_opts->remove.collation);
   memset (&mongoc_bulk_remove_one_opts->remove.hint, 0, sizeof (bson_value_t));
   mongoc_bulk_remove_one_opts->remove.limit = 1;
   bson_init (&mongoc_bulk_remove_one_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_remove_one_opts->remove.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_bulk_remove_one_opts->remove.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "limit")) {
         if (!_mongoc_convert_int32_t (
               client,
               &iter,
               &mongoc_bulk_remove_one_opts->remove.limit,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_remove_one_opts_cleanup (mongoc_bulk_remove_one_opts_t *mongoc_bulk_remove_one_opts)
{
   bson_destroy (&mongoc_bulk_remove_one_opts->remove.collation);
   bson_value_destroy (&mongoc_bulk_remove_one_opts->remove.hint);
   bson_destroy (&mongoc_bulk_remove_one_opts->extra);
}

bool
_mongoc_bulk_remove_many_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_bulk_remove_many_opts_t *mongoc_bulk_remove_many_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   bson_init (&mongoc_bulk_remove_many_opts->remove.collation);
   memset (&mongoc_bulk_remove_many_opts->remove.hint, 0, sizeof (bson_value_t));
   mongoc_bulk_remove_many_opts->remove.limit = 0;
   bson_init (&mongoc_bulk_remove_many_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_bulk_remove_many_opts->remove.collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_bulk_remove_many_opts->remove.hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "limit")) {
         if (!_mongoc_convert_int32_t (
               client,
               &iter,
               &mongoc_bulk_remove_many_opts->remove.limit,
               error)) {
            return false;
         }
      }
      else {
         bson_set_error (error,
                         MONGOC_ERROR_COMMAND,
                         MONGOC_ERROR_COMMAND_INVALID_ARG,
                         "Invalid option '%s'",
                         bson_iter_key (&iter));
         return false;
      }
   }

   return true;
}

void
_mongoc_bulk_remove_many_opts_cleanup (mongoc_bulk_remove_many_opts_t *mongoc_bulk_remove_many_opts)
{
   bson_destroy (&mongoc_bulk_remove_many_opts->remove.collation);
   bson_value_destroy (&mongoc_bulk_remove_many_opts->remove.hint);
   bson_destroy (&mongoc_bulk_remove_many_opts->extra);
}

bool
_mongoc_change_stream_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_change_stream_opts_t *mongoc_change_stream_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_change_stream_opts->batchSize = 0;
   bson_init (&mongoc_change_stream_opts->resumeAfter);
   bson_init (&mongoc_change_stream_opts->startAfter);
   memset (&mongoc_change_stream_opts->startAtOperationTime, 0, sizeof (mongoc_timestamp_t));
   mongoc_change_stream_opts->maxAwaitTimeMS = 0;
   mongoc_change_stream_opts->fullDocument = NULL;
   mongoc_change_stream_opts->fullDocumentBeforeChange = NULL;
   mongoc_change_stream_opts->showExpandedEvents = false;
   memset (&mongoc_change_stream_opts->comment, 0, sizeof (bson_value_t));
   bson_init (&mongoc_change_stream_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "batchSize")) {
         if (!_mongoc_convert_int32_t (
               client,
               &iter,
               &mongoc_change_stream_opts->batchSize,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "resumeAfter")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_change_stream_opts->resumeAfter,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "startAfter")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_change_stream_opts->startAfter,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "startAtOperationTime")) {
         if (!_mongoc_convert_timestamp (
               client,
               &iter,
               &mongoc_change_stream_opts->startAtOperationTime,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "maxAwaitTimeMS")) {
         if (!_mongoc_convert_int64_positive (
               client,
               &iter,
               &mongoc_change_stream_opts->maxAwaitTimeMS,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "fullDocument")) {
         if (!_mongoc_convert_utf8 (
               client,
               &iter,
               &mongoc_change_stream_opts->fullDocument,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "fullDocumentBeforeChange")) {
         if (!_mongoc_convert_utf8 (
               client,
               &iter,
               &mongoc_change_stream_opts->fullDocumentBeforeChange,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "showExpandedEvents")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_change_stream_opts->showExpandedEvents,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_change_stream_opts->comment,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_change_stream_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_change_stream_opts_cleanup (mongoc_change_stream_opts_t *mongoc_change_stream_opts)
{
   bson_destroy (&mongoc_change_stream_opts->resumeAfter);
   bson_destroy (&mongoc_change_stream_opts->startAfter);
   bson_value_destroy (&mongoc_change_stream_opts->comment);
   bson_destroy (&mongoc_change_stream_opts->extra);
}

bool
_mongoc_create_index_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_create_index_opts_t *mongoc_create_index_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_create_index_opts->writeConcern = NULL;
   mongoc_create_index_opts->write_concern_owned = false;
   mongoc_create_index_opts->client_session = NULL;
   bson_init (&mongoc_create_index_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_create_index_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_create_index_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_create_index_opts->client_session,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_create_index_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_create_index_opts_cleanup (mongoc_create_index_opts_t *mongoc_create_index_opts)
{
   if (mongoc_create_index_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_create_index_opts->writeConcern);
   }
   bson_destroy (&mongoc_create_index_opts->extra);
}

bool
_mongoc_read_write_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_read_write_opts_t *mongoc_read_write_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   bson_init (&mongoc_read_write_opts->readConcern);
   mongoc_read_write_opts->writeConcern = NULL;
   mongoc_read_write_opts->write_concern_owned = false;
   mongoc_read_write_opts->client_session = NULL;
   bson_init (&mongoc_read_write_opts->collation);
   mongoc_read_write_opts->serverId = 0;
   bson_init (&mongoc_read_write_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "readConcern")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_read_write_opts->readConcern,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_read_write_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_read_write_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_read_write_opts->client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_read_write_opts->collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "serverId")) {
         if (!_mongoc_convert_server_id (
               client,
               &iter,
               &mongoc_read_write_opts->serverId,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_read_write_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_read_write_opts_cleanup (mongoc_read_write_opts_t *mongoc_read_write_opts)
{
   bson_destroy (&mongoc_read_write_opts->readConcern);
   if (mongoc_read_write_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_read_write_opts->writeConcern);
   }
   bson_destroy (&mongoc_read_write_opts->collation);
   bson_destroy (&mongoc_read_write_opts->extra);
}

bool
_mongoc_gridfs_bucket_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_gridfs_bucket_opts_t *mongoc_gridfs_bucket_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_gridfs_bucket_opts->bucketName = "fs";
   mongoc_gridfs_bucket_opts->chunkSizeBytes = 261120;
   mongoc_gridfs_bucket_opts->writeConcern = NULL;
   mongoc_gridfs_bucket_opts->write_concern_owned = false;
   mongoc_gridfs_bucket_opts->readConcern = NULL;
   bson_init (&mongoc_gridfs_bucket_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "bucketName")) {
         if (!_mongoc_convert_utf8 (
               client,
               &iter,
               &mongoc_gridfs_bucket_opts->bucketName,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "chunkSizeBytes")) {
         if (!_mongoc_convert_int32_positive (
               client,
               &iter,
               &mongoc_gridfs_bucket_opts->chunkSizeBytes,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_gridfs_bucket_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_gridfs_bucket_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "readConcern")) {
         if (!_mongoc_convert_read_concern (
               client,
               &iter,
               &mongoc_gridfs_bucket_opts->readConcern,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_gridfs_bucket_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_gridfs_bucket_opts_cleanup (mongoc_gridfs_bucket_opts_t *mongoc_gridfs_bucket_opts)
{
   if (mongoc_gridfs_bucket_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_gridfs_bucket_opts->writeConcern);
   }
   mongoc_read_concern_destroy (mongoc_gridfs_bucket_opts->readConcern);
   bson_destroy (&mongoc_gridfs_bucket_opts->extra);
}

bool
_mongoc_gridfs_bucket_upload_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_gridfs_bucket_upload_opts_t *mongoc_gridfs_bucket_upload_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_gridfs_bucket_upload_opts->chunkSizeBytes = 0;
   bson_init (&mongoc_gridfs_bucket_upload_opts->metadata);
   bson_init (&mongoc_gridfs_bucket_upload_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "chunkSizeBytes")) {
         if (!_mongoc_convert_int32_positive (
               client,
               &iter,
               &mongoc_gridfs_bucket_upload_opts->chunkSizeBytes,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "metadata")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_gridfs_bucket_upload_opts->metadata,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_gridfs_bucket_upload_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_gridfs_bucket_upload_opts_cleanup (mongoc_gridfs_bucket_upload_opts_t *mongoc_gridfs_bucket_upload_opts)
{
   bson_destroy (&mongoc_gridfs_bucket_upload_opts->metadata);
   bson_destroy (&mongoc_gridfs_bucket_upload_opts->extra);
}

bool
_mongoc_aggregate_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_aggregate_opts_t *mongoc_aggregate_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_aggregate_opts->readConcern = NULL;
   mongoc_aggregate_opts->writeConcern = NULL;
   mongoc_aggregate_opts->write_concern_owned = false;
   mongoc_aggregate_opts->client_session = NULL;
   mongoc_aggregate_opts->bypass = false;
   bson_init (&mongoc_aggregate_opts->collation);
   mongoc_aggregate_opts->serverId = 0;
   mongoc_aggregate_opts->batchSize = 0;
   mongoc_aggregate_opts->batchSize_is_set = false;
   bson_init (&mongoc_aggregate_opts->let);
   memset (&mongoc_aggregate_opts->comment, 0, sizeof (bson_value_t));
   memset (&mongoc_aggregate_opts->hint, 0, sizeof (bson_value_t));
   bson_init (&mongoc_aggregate_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "readConcern")) {
         if (!_mongoc_convert_read_concern (
               client,
               &iter,
               &mongoc_aggregate_opts->readConcern,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_aggregate_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_aggregate_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_aggregate_opts->client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "bypassDocumentValidation")) {
         if (!_mongoc_convert_bool (
               client,
               &iter,
               &mongoc_aggregate_opts->bypass,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_aggregate_opts->collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "serverId")) {
         if (!_mongoc_convert_server_id (
               client,
               &iter,
               &mongoc_aggregate_opts->serverId,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "batchSize")) {
         if (!_mongoc_convert_int32_t (
               client,
               &iter,
               &mongoc_aggregate_opts->batchSize,
               error)) {
            return false;
         }

         mongoc_aggregate_opts->batchSize_is_set = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_aggregate_opts->let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_aggregate_opts->comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_aggregate_opts->hint,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_aggregate_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_aggregate_opts_cleanup (mongoc_aggregate_opts_t *mongoc_aggregate_opts)
{
   mongoc_read_concern_destroy (mongoc_aggregate_opts->readConcern);
   if (mongoc_aggregate_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_aggregate_opts->writeConcern);
   }
   bson_destroy (&mongoc_aggregate_opts->collation);
   bson_destroy (&mongoc_aggregate_opts->let);
   bson_value_destroy (&mongoc_aggregate_opts->comment);
   bson_value_destroy (&mongoc_aggregate_opts->hint);
   bson_destroy (&mongoc_aggregate_opts->extra);
}

bool
_mongoc_find_and_modify_appended_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_find_and_modify_appended_opts_t *mongoc_find_and_modify_appended_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   mongoc_find_and_modify_appended_opts->writeConcern = NULL;
   mongoc_find_and_modify_appended_opts->write_concern_owned = false;
   mongoc_find_and_modify_appended_opts->client_session = NULL;
   memset (&mongoc_find_and_modify_appended_opts->hint, 0, sizeof (bson_value_t));
   bson_init (&mongoc_find_and_modify_appended_opts->let);
   memset (&mongoc_find_and_modify_appended_opts->comment, 0, sizeof (bson_value_t));
   bson_init (&mongoc_find_and_modify_appended_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "writeConcern")) {
         if (!_mongoc_convert_write_concern (
               client,
               &iter,
               &mongoc_find_and_modify_appended_opts->writeConcern,
               error)) {
            return false;
         }

         mongoc_find_and_modify_appended_opts->write_concern_owned = true;
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_find_and_modify_appended_opts->client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_find_and_modify_appended_opts->hint,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "let")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_find_and_modify_appended_opts->let,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_find_and_modify_appended_opts->comment,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_find_and_modify_appended_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_find_and_modify_appended_opts_cleanup (mongoc_find_and_modify_appended_opts_t *mongoc_find_and_modify_appended_opts)
{
   if (mongoc_find_and_modify_appended_opts->write_concern_owned) {
      mongoc_write_concern_destroy (mongoc_find_and_modify_appended_opts->writeConcern);
   }
   bson_value_destroy (&mongoc_find_and_modify_appended_opts->hint);
   bson_destroy (&mongoc_find_and_modify_appended_opts->let);
   bson_value_destroy (&mongoc_find_and_modify_appended_opts->comment);
   bson_destroy (&mongoc_find_and_modify_appended_opts->extra);
}

bool
_mongoc_count_document_opts_parse (
   mongoc_client_t *client,
   const bson_t *opts,
   mongoc_count_document_opts_t *mongoc_count_document_opts,
   bson_error_t *error)
{
   bson_iter_t iter;

   BSON_ASSERT (client || true); // client may be NULL.

   bson_init (&mongoc_count_document_opts->readConcern);
   mongoc_count_document_opts->client_session = NULL;
   bson_init (&mongoc_count_document_opts->collation);
   mongoc_count_document_opts->serverId = 0;
   memset (&mongoc_count_document_opts->skip, 0, sizeof (bson_value_t));
   memset (&mongoc_count_document_opts->limit, 0, sizeof (bson_value_t));
   memset (&mongoc_count_document_opts->comment, 0, sizeof (bson_value_t));
   memset (&mongoc_count_document_opts->hint, 0, sizeof (bson_value_t));
   bson_init (&mongoc_count_document_opts->extra);

   if (!opts) {
      return true;
   }

   if (!bson_iter_init (&iter, opts)) {
      bson_set_error (error,
                      MONGOC_ERROR_BSON,
                      MONGOC_ERROR_BSON_INVALID,
                      "Invalid 'opts' parameter.");
      return false;
   }

   while (bson_iter_next (&iter)) {
      if (!strcmp (bson_iter_key (&iter), "readConcern")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_count_document_opts->readConcern,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "sessionId")) {
         if (!_mongoc_convert_session_id (
               client,
               &iter,
               &mongoc_count_document_opts->client_session,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "collation")) {
         if (!_mongoc_convert_document (
               client,
               &iter,
               &mongoc_count_document_opts->collation,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "serverId")) {
         if (!_mongoc_convert_server_id (
               client,
               &iter,
               &mongoc_count_document_opts->serverId,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "skip")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_count_document_opts->skip,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "limit")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_count_document_opts->limit,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "comment")) {
         if (!_mongoc_convert_bson_value_t (
               client,
               &iter,
               &mongoc_count_document_opts->comment,
               error)) {
            return false;
         }
      }
      else if (!strcmp (bson_iter_key (&iter), "hint")) {
         if (!_mongoc_convert_hint (
               client,
               &iter,
               &mongoc_count_document_opts->hint,
               error)) {
            return false;
         }
      }
      else {
         /* unrecognized values are copied to "extra" */
         if (!BSON_APPEND_VALUE (
               &mongoc_count_document_opts->extra,
               bson_iter_key (&iter),
               bson_iter_value (&iter))) {
            bson_set_error (error,
                            MONGOC_ERROR_BSON,
                            MONGOC_ERROR_BSON_INVALID,
                            "Invalid 'opts' parameter.");
            return false;
         }
      }
   }

   return true;
}

void
_mongoc_count_document_opts_cleanup (mongoc_count_document_opts_t *mongoc_count_document_opts)
{
   bson_destroy (&mongoc_count_document_opts->readConcern);
   bson_destroy (&mongoc_count_document_opts->collation);
   bson_value_destroy (&mongoc_count_document_opts->skip);
   bson_value_destroy (&mongoc_count_document_opts->limit);
   bson_value_destroy (&mongoc_count_document_opts->comment);
   bson_value_destroy (&mongoc_count_document_opts->hint);
   bson_destroy (&mongoc_count_document_opts->extra);
}
