1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Contains all of the types needed to specify options to MongoDB operations.
//!
//! Most of the options structs in this module use the
//! [`typed-builder`](https://crates.io/crates/typed-builder) crate to derive a type-safe builder
//! API on them. For example, to create an instance of
//! [`FindOptions`](struct.FindOptions.html) with only `limit` and `batch_size` set, the builder
//! API can be used as follows:
//!
//! ```rust
//! use mongodb::options::FindOptions;
//!
//! let options = FindOptions::builder()
//!                   .limit(20)
//!                   .batch_size(5)
//!                   .build();
//! ```

pub use crate::{
    client::{auth::*, options::*},
    coll::options::*,
    collation::*,
    concern::*,
    db::options::*,
    index::options::*,
    selection_criteria::*,
};

/// Updates an options struct with the read preference/read concern/write concern of a
/// client/database/collection.
macro_rules! resolve_options {
    ($obj:expr, $opts:expr, [$( $field:ident ),+] ) => {
        $(
            if let Some(option) = $obj.$field() {
                if !$opts
                    .as_ref()
                    .map(|opts| opts.$field.is_some())
                    .unwrap_or(false)
                {
                    $opts.get_or_insert_with(Default::default).$field = Some(option.clone());
                }
            }
        )+
    };
}

/// Merges the options from src into dst.
macro_rules! merge_options {
    ($src:expr, $dst:expr, [$( $field:ident ),+] ) => {
        $(
            if let Some(ref option) = $src.$field {
                if !$dst.$field.is_some() {
                    $dst.$field = Some(option.clone());
                }
            }
        )+
    };
}

/// Updates the read concern of an options struct. If a transaction is starting or in progress,
/// return an error if a read concern was specified for the operation. Otherwise, inherit the read
/// concern from the collection/database.
macro_rules! resolve_read_concern_with_session {
    ($obj:expr, $opts:expr, $session:expr) => {{
        resolve_rw_concern_with_session!($obj, $opts, $session, read_concern, "read")
    }};
}

/// Updates the read concern of an options struct. If a transaction is starting or in progress,
/// return an error if a write concern was specified for the operation. Otherwise, inherit the write
/// concern from the collection/database.
macro_rules! resolve_write_concern_with_session {
    ($obj:expr, $opts:expr, $session:expr) => {{
        resolve_rw_concern_with_session!($obj, $opts, $session, write_concern, "write")
    }};
}

macro_rules! resolve_rw_concern_with_session {
    ($obj:expr, $opts:expr, $session:expr, $concern:ident, $name:expr) => {{
        if let Some(session) = $session {
            match session.transaction.state {
                TransactionState::Starting | TransactionState::InProgress => {
                    if $opts
                        .as_ref()
                        .map(|opts| opts.$concern.is_some())
                        .unwrap_or(false)
                    {
                        return Err(ErrorKind::InvalidArgument {
                            message: format!(
                                "Cannot set {} concern after starting a transaction",
                                $name
                            ),
                        }
                        .into());
                    }
                }
                _ => {
                    resolve_options!($obj, $opts, [$concern]);
                }
            }
        } else {
            resolve_options!($obj, $opts, [$concern]);
        }
        let result: Result<()> = Ok(());
        result
    }};
}

/// Updates the selection criteria of an options struct. If a transaction is starting or in progress
/// and the selection criteria is not configured directly on the operation, inherit the selection
/// criteria from the transaction options. Otherwise, use the selection criteria defined on the
/// operation or inherit it from the collection/database.
macro_rules! resolve_selection_criteria_with_session {
    ($obj:expr, $opts:expr, $session:expr) => {{
        if let Some(session) = $session {
            match session.transaction.state {
                TransactionState::Starting | TransactionState::InProgress => {
                    if let Some(ref options) = session.transaction.options {
                        if let Some(ref selection_criteria) = options.selection_criteria {
                            if $opts
                                .as_ref()
                                .map(|opts| opts.selection_criteria.is_none())
                                .unwrap_or(true)
                            {
                                $opts
                                    .get_or_insert_with(Default::default)
                                    .selection_criteria = Some(selection_criteria.clone());
                            }
                        }
                    }
                }
                _ => {
                    resolve_options!($obj, $opts, [selection_criteria]);
                }
            }
        } else {
            resolve_options!($obj, $opts, [selection_criteria]);
        }
        let result: Result<()> = Ok(());
        result
    }};
}