mongodb/operation/
find_and_modify.rs1pub(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}