Skip to content

Commit 83b838e

Browse files
authored
RUST-1512 Convert the remainder of Collection methods to use actions (#1046)
1 parent a3fe6c8 commit 83b838e

File tree

101 files changed

+2549
-3016
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+2549
-3016
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ in-use-encryption-unstable = ["mongocrypt", "rayon", "num_cpus"]
6969
tracing-unstable = ["tracing", "log"]
7070

7171
[dependencies]
72+
action_macro = { path = "action_macro" }
7273
async-trait = "0.1.42"
7374
base64 = "0.13.0"
7475
bitflags = "1.1.0"

action_macro/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target/

action_macro/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "action_macro"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "Apache-2.0"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
proc-macro2 = "1.0.78"
11+
quote = "1.0.35"
12+
syn = { version = "2.0.52", features = ["full", "parsing", "proc-macro"] }
13+
14+
[lib]
15+
proc-macro = true

action_macro/src/lib.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
extern crate proc_macro;
2+
3+
use quote::quote;
4+
use syn::{
5+
braced,
6+
parenthesized,
7+
parse::{Parse, ParseStream},
8+
parse_macro_input,
9+
parse_quote,
10+
parse_quote_spanned,
11+
spanned::Spanned,
12+
Block,
13+
Error,
14+
Generics,
15+
Ident,
16+
Lifetime,
17+
Token,
18+
Type,
19+
};
20+
21+
/// Generates:
22+
/// * an `IntoFuture` executing the given method body
23+
/// * an opaque wrapper type for the future in case we want to do something more fancy than
24+
/// BoxFuture.
25+
/// * a `run` method for sync execution, optionally with a wrapper function
26+
#[proc_macro]
27+
pub fn action_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
28+
let ActionImpl {
29+
generics,
30+
lifetime,
31+
action,
32+
future_name,
33+
exec_self_mut,
34+
exec_output,
35+
exec_body,
36+
sync_wrap,
37+
} = parse_macro_input!(input as ActionImpl);
38+
39+
let mut unbounded_generics = generics.clone();
40+
for lt in unbounded_generics.lifetimes_mut() {
41+
lt.bounds.clear();
42+
}
43+
for ty in unbounded_generics.type_params_mut() {
44+
ty.bounds.clear();
45+
}
46+
47+
let SyncWrap {
48+
sync_arg_mut,
49+
sync_arg,
50+
sync_output,
51+
sync_body,
52+
} = sync_wrap.unwrap_or_else(|| {
53+
parse_quote! { fn sync_wrap(out) -> #exec_output { out } }
54+
});
55+
56+
quote! {
57+
impl #generics crate::action::private::Sealed for #action { }
58+
59+
impl #generics crate::action::Action for #action { }
60+
61+
impl #generics std::future::IntoFuture for #action {
62+
type Output = #exec_output;
63+
type IntoFuture = #future_name #unbounded_generics;
64+
65+
fn into_future(#exec_self_mut self) -> Self::IntoFuture {
66+
#future_name (Box::pin(async move {
67+
#exec_body
68+
}))
69+
}
70+
}
71+
72+
pub struct #future_name #generics (crate::BoxFuture<#lifetime, #exec_output>);
73+
74+
impl #generics std::future::Future for #future_name #unbounded_generics {
75+
type Output = #exec_output;
76+
77+
fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
78+
self.0.as_mut().poll(cx)
79+
}
80+
}
81+
82+
#[cfg(feature = "sync")]
83+
impl #generics #action {
84+
/// Synchronously execute this action.
85+
pub fn run(self) -> #sync_output {
86+
let #sync_arg_mut #sync_arg = crate::sync::TOKIO_RUNTIME.block_on(std::future::IntoFuture::into_future(self));
87+
#sync_body
88+
}
89+
}
90+
}.into()
91+
}
92+
93+
// impl<generics> Action for ActionType {
94+
// type Future = FutureName;
95+
// async fn execute([mut] self) -> OutType { <exec body> }
96+
// [SyncWrap]
97+
// }
98+
struct ActionImpl {
99+
generics: Generics,
100+
lifetime: Lifetime,
101+
action: Type,
102+
future_name: Ident,
103+
exec_self_mut: Option<Token![mut]>,
104+
exec_output: Type,
105+
exec_body: Block,
106+
sync_wrap: Option<SyncWrap>,
107+
}
108+
109+
impl Parse for ActionImpl {
110+
fn parse(input: ParseStream) -> syn::Result<Self> {
111+
// impl<generics> Action for ActionType
112+
input.parse::<Token![impl]>()?;
113+
let generics: Generics = input.parse()?;
114+
let mut lifetime = None;
115+
for lt in generics.lifetimes() {
116+
if lifetime.is_some() {
117+
return Err(input.error("only one lifetime argument permitted"));
118+
}
119+
lifetime = Some(lt);
120+
}
121+
let lifetime = match lifetime {
122+
Some(lt) => lt.lifetime.clone(),
123+
None => parse_quote_spanned! { generics.span() => 'static },
124+
};
125+
parse_name(input, "Action")?;
126+
input.parse::<Token![for]>()?;
127+
let action = input.parse()?;
128+
129+
let impl_body;
130+
braced!(impl_body in input);
131+
132+
// type Future = FutureName;
133+
impl_body.parse::<Token![type]>()?;
134+
parse_name(&impl_body, "Future")?;
135+
impl_body.parse::<Token![=]>()?;
136+
let future_name = impl_body.parse()?;
137+
impl_body.parse::<Token![;]>()?;
138+
139+
// async fn execute([mut] self) -> OutType { <exec body> }
140+
impl_body.parse::<Token![async]>()?;
141+
impl_body.parse::<Token![fn]>()?;
142+
parse_name(&impl_body, "execute")?;
143+
let exec_args;
144+
parenthesized!(exec_args in impl_body);
145+
let exec_self_mut = exec_args.parse()?;
146+
exec_args.parse::<Token![self]>()?;
147+
if !exec_args.is_empty() {
148+
return Err(exec_args.error("unexpected token"));
149+
}
150+
impl_body.parse::<Token![->]>()?;
151+
let exec_output = impl_body.parse()?;
152+
let exec_body = impl_body.parse()?;
153+
154+
// Optional SyncWrap.
155+
let sync_wrap = if impl_body.peek(Token![fn]) {
156+
Some(impl_body.parse()?)
157+
} else {
158+
None
159+
};
160+
161+
if !impl_body.is_empty() {
162+
return Err(exec_args.error("unexpected token"));
163+
}
164+
165+
Ok(ActionImpl {
166+
generics,
167+
lifetime,
168+
action,
169+
future_name,
170+
exec_self_mut,
171+
exec_output,
172+
exec_body,
173+
sync_wrap,
174+
})
175+
}
176+
}
177+
178+
// fn sync_wrap([mut] out) -> OutType { <out body> }
179+
struct SyncWrap {
180+
sync_arg_mut: Option<Token![mut]>,
181+
sync_arg: Ident,
182+
sync_output: Type,
183+
sync_body: Block,
184+
}
185+
186+
impl Parse for SyncWrap {
187+
fn parse(input: ParseStream) -> syn::Result<Self> {
188+
input.parse::<Token![fn]>()?;
189+
parse_name(input, "sync_wrap")?;
190+
let args_input;
191+
parenthesized!(args_input in input);
192+
let sync_arg_mut = args_input.parse()?;
193+
let sync_arg = args_input.parse()?;
194+
if !args_input.is_empty() {
195+
return Err(args_input.error("unexpected token"));
196+
}
197+
input.parse::<Token![->]>()?;
198+
let sync_output = input.parse()?;
199+
let sync_body = input.parse()?;
200+
201+
Ok(SyncWrap {
202+
sync_arg_mut,
203+
sync_arg,
204+
sync_output,
205+
sync_body,
206+
})
207+
}
208+
}
209+
210+
/// Parse an identifier with a specific expected value.
211+
fn parse_name(input: ParseStream, name: &str) -> syn::Result<()> {
212+
let ident = input.parse::<Ident>()?;
213+
if ident.to_string() != name {
214+
return Err(Error::new(
215+
ident.span(),
216+
format!("expected '{}', got '{}'", name, ident),
217+
));
218+
}
219+
Ok(())
220+
}

manual/src/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ e.g.
2323
# let client = Client::with_uri_str("mongodb://example.com").await?;
2424
let collection = client.database("foo").collection("bar");
2525
let handle = tokio::task::spawn(async move {
26-
collection.insert_one(doc! { "x": 1 }, None).await
26+
collection.insert_one(doc! { "x": 1 }).await
2727
});
2828
2929
tokio::time::timeout(Duration::from_secs(5), handle).await???;

manual/src/encryption.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,16 @@ async fn main() -> Result<()> {
187187
// Clear old data.
188188
coll.drop().await?;
189189
190-
coll.insert_one(doc! { "encryptedField": "123456789" }, None)
190+
coll.insert_one(doc! { "encryptedField": "123456789" })
191191
.await?;
192-
println!("Decrypted document: {:?}", coll.find_one(None, None).await?);
192+
println!("Decrypted document: {:?}", coll.find_one(doc! {}).await?);
193193
let unencrypted_coll = Client::with_uri_str(URI)
194194
.await?
195195
.database(&encrypted_namespace.db)
196196
.collection::<Document>(&encrypted_namespace.coll);
197197
println!(
198198
"Encrypted document: {:?}",
199-
unencrypted_coll.find_one(None, None).await?
199+
unencrypted_coll.find_one(doc! {}).await?
200200
);
201201
202202
Ok(())
@@ -294,19 +294,19 @@ async fn main() -> Result<()> {
294294
.validator(doc! { "$jsonSchema": schema })
295295
.await?;
296296
297-
coll.insert_one(doc! { "encryptedField": "123456789" }, None)
297+
coll.insert_one(doc! { "encryptedField": "123456789" })
298298
.await?;
299-
println!("Decrypted document: {:?}", coll.find_one(None, None).await?);
299+
println!("Decrypted document: {:?}", coll.find_one(doc! {}).await?);
300300
let unencrypted_coll = Client::with_uri_str(URI)
301301
.await?
302302
.database(&encrypted_namespace.db)
303303
.collection::<Document>(&encrypted_namespace.coll);
304304
println!(
305305
"Encrypted document: {:?}",
306-
unencrypted_coll.find_one(None, None).await?
306+
unencrypted_coll.find_one(doc! {}).await?
307307
);
308308
// This would return a Write error with the message "Document failed validation".
309-
// unencrypted_coll.insert_one(doc! { "encryptedField": "123456789" }, None)
309+
// unencrypted_coll.insert_one(doc! { "encryptedField": "123456789" })
310310
// .await?;
311311
312312
Ok(())
@@ -407,11 +407,10 @@ async fn main() -> Result<()> {
407407
db.create_collection("encryptedCollection").await?;
408408
coll.insert_one(
409409
doc! { "_id": 1, "firstName": "Jane", "lastName": "Doe" },
410-
None,
411410
)
412411
.await?;
413412
let docs: Vec<_> = coll
414-
.find(doc! {"firstName": "Jane"}, None)
413+
.find(doc! {"firstName": "Jane"})
415414
.await?
416415
.try_collect()
417416
.await?;
@@ -540,7 +539,6 @@ async fn main() -> Result<()> {
540539
"encryptedIndexed": insert_payload_indexed,
541540
"encryptedUnindexed": insert_payload_unindexed,
542541
},
543-
None,
544542
)
545543
.await?;
546544
@@ -556,7 +554,7 @@ async fn main() -> Result<()> {
556554
// Find the document we inserted using the encrypted payload.
557555
// The returned document is automatically decrypted.
558556
let doc = coll
559-
.find_one(doc! { "encryptedIndexed": find_payload }, None)
557+
.find_one(doc! { "encryptedIndexed": find_payload })
560558
.await?;
561559
println!("Returned document: {:?}", doc);
562560
@@ -634,9 +632,9 @@ async fn main() -> Result<()> {
634632
Algorithm::AeadAes256CbcHmacSha512Deterministic,
635633
)
636634
.await?;
637-
coll.insert_one(doc! { "encryptedField": encrypted_field }, None)
635+
coll.insert_one(doc! { "encryptedField": encrypted_field })
638636
.await?;
639-
let mut doc = coll.find_one(None, None).await?.unwrap();
637+
let mut doc = coll.find_one(doc! {}).await?.unwrap();
640638
println!("Encrypted document: {:?}", doc);
641639
642640
// Explicitly decrypt the field:
@@ -735,18 +733,18 @@ async fn main() -> Result<()> {
735733
Algorithm::AeadAes256CbcHmacSha512Deterministic,
736734
)
737735
.await?;
738-
coll.insert_one(doc! { "encryptedField": encrypted_field }, None)
736+
coll.insert_one(doc! { "encryptedField": encrypted_field })
739737
.await?;
740738
// Automatically decrypts any encrypted fields.
741-
let doc = coll.find_one(None, None).await?.unwrap();
739+
let doc = coll.find_one(doc! {}).await?.unwrap();
742740
println!("Decrypted document: {:?}", doc);
743741
let unencrypted_coll = Client::with_uri_str(URI)
744742
.await?
745743
.database("test")
746744
.collection::<Document>("coll");
747745
println!(
748746
"Encrypted document: {:?}",
749-
unencrypted_coll.find_one(None, None).await?
747+
unencrypted_coll.find_one(doc! {}).await?
750748
);
751749
752750
Ok(())

manual/src/reading.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ let coll = client.database("items").collection::<Item>("in_stock");
5858
5959
for i in 0..5 {
6060
// Perform operations that work with directly our model.
61-
coll.insert_one(Item { id: i }, None).await;
61+
coll.insert_one(Item { id: i }).await;
6262
}
6363
#
6464
# Ok(())
@@ -89,9 +89,10 @@ use futures::stream::TryStreamExt;
8989
use mongodb::{bson::doc, options::FindOptions};
9090
9191
// Query the books in the collection with a filter and an option.
92-
let filter = doc! { "author": "George Orwell" };
93-
let find_options = FindOptions::builder().sort(doc! { "title": 1 }).build();
94-
let mut cursor = typed_collection.find(filter, find_options).await?;
92+
let mut cursor = typed_collection
93+
.find(doc! { "author": "George Orwell" })
94+
.sort(doc! { "title": 1 })
95+
.await?;
9596
9697
// Iterate over the results of the cursor.
9798
while let Some(book) = cursor.try_next().await? {

0 commit comments

Comments
 (0)