mongodb/operation/
find_and_modify.rs

1pub(crate) mod options;
2
3use std::{fmt::Debug, marker::PhantomData};
4
5use serde::{de::DeserializeOwned, Deserialize};
6
7use self::options::FindAndModifyOptions;
8use crate::{
9    bson::{doc, from_slice, rawdoc, Document, RawBson, RawDocumentBuf},
10    bson_util,
11    cmap::{Command, RawCommandResponse, StreamDescription},
12    coll::{options::UpdateModifications, Namespace},
13    error::{ErrorKind, Result},
14    operation::{
15        append_options_to_raw_document,
16        find_and_modify::options::Modification,
17        remove_empty_write_concern,
18        OperationWithDefaults,
19        Retryability,
20    },
21    options::WriteConcern,
22};
23
24use super::{ExecutionContext, UpdateOrReplace};
25
26pub(crate) struct FindAndModify<T: DeserializeOwned> {
27    ns: Namespace,
28    query: Document,
29    modification: Modification,
30    options: Option<FindAndModifyOptions>,
31    _phantom: PhantomData<fn() -> T>,
32}
33
34impl<T: DeserializeOwned> FindAndModify<T> {
35    pub(crate) fn with_modification(
36        ns: Namespace,
37        query: Document,
38        modification: Modification,
39        options: Option<FindAndModifyOptions>,
40    ) -> Result<Self> {
41        if let Modification::Update(UpdateOrReplace::UpdateModifications(
42            UpdateModifications::Document(d),
43        )) = &modification
44        {
45            bson_util::update_document_check(d)?;
46        };
47        Ok(Self {
48            ns,
49            query,
50            modification,
51            options,
52            _phantom: PhantomData,
53        })
54    }
55}
56
57impl<T: DeserializeOwned> OperationWithDefaults for FindAndModify<T> {
58    type O = Option<T>;
59    const NAME: &'static str = "findAndModify";
60
61    fn build(&mut self, description: &StreamDescription) -> Result<Command> {
62        if let Some(ref options) = self.options {
63            if options.hint.is_some() && description.max_wire_version.unwrap_or(0) < 8 {
64                return Err(ErrorKind::InvalidArgument {
65                    message: "Specifying a hint to find_one_and_x is not supported on server \
66                              versions < 4.4"
67                        .to_string(),
68                }
69                .into());
70            }
71        }
72
73        let mut body = rawdoc! {
74            Self::NAME: self.ns.coll.clone(),
75            "query": RawDocumentBuf::from_document(&self.query)?,
76        };
77
78        match &self.modification {
79            Modification::Delete => body.append("remove", true),
80            Modification::Update(update_or_replace) => {
81                update_or_replace.append_to_rawdoc(&mut body, "update")?
82            }
83        }
84
85        if let Some(ref mut options) = self.options {
86            remove_empty_write_concern!(Some(options));
87        }
88        append_options_to_raw_document(&mut body, self.options.as_ref())?;
89
90        Ok(Command::new(
91            Self::NAME.to_string(),
92            self.ns.db.clone(),
93            body,
94        ))
95    }
96
97    fn handle_response<'a>(
98        &'a self,
99        response: RawCommandResponse,
100        _context: ExecutionContext<'a>,
101    ) -> Result<Self::O> {
102        #[derive(Debug, Deserialize)]
103        pub(crate) struct Response {
104            value: RawBson,
105        }
106        let response: Response = response.body()?;
107
108        match response.value {
109            RawBson::Document(doc) => Ok(Some(from_slice(doc.as_bytes())?)),
110            RawBson::Null => Ok(None),
111            other => Err(ErrorKind::InvalidResponse {
112                message: format!(
113                    "expected document for value field of findAndModify response, but instead got \
114                     {:?}",
115                    other
116                ),
117            }
118            .into()),
119        }
120    }
121
122    fn write_concern(&self) -> Option<&WriteConcern> {
123        self.options.as_ref().and_then(|o| o.write_concern.as_ref())
124    }
125
126    fn retryability(&self) -> Retryability {
127        Retryability::Write
128    }
129}