Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create async Send alternative #56

Merged
merged 5 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions ouroboros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@
/// }
///
/// fn main() {
/// // The builder is created by the #[self_referencing] macro
/// // The builder is created by the #[self_referencing] macro
/// // and is used to create the struct
/// let mut my_value = MyStructBuilder {
/// int_data: 42,
/// float_data: 3.14,
///
/// // Note that the name of the field in the builder
/// // is the name of the field in the struct + `_builder`
/// // Note that the name of the field in the builder
/// // is the name of the field in the struct + `_builder`
/// // ie: {field_name}_builder
/// // the closure that assigns the value for the field will be passed
/// // the closure that assigns the value for the field will be passed
/// // a reference to the field(s) defined in the #[borrows] macro
///
///
/// int_reference_builder: |int_data: &i32| int_data,
/// float_reference_builder: |float_data: &mut f32| float_data,
/// }.build();
Expand Down Expand Up @@ -206,6 +206,52 @@
/// }
/// ```
///
/// # Async Send
/// When Send trait is needed, the Send variant of async methods and builders is available.
///
/// Here is the same example as above in its async send version:
///
/// ```ignore
/// use ouroboros::self_referencing;
///
/// #[self_referencing]
/// struct MyStruct {
/// int_data: i32,
/// float_data: f32,
/// #[borrows(int_data)]
/// int_reference: &'this i32,
/// #[borrows(mut float_data)]
/// float_reference: &'this mut f32,
/// }
///
/// #[tokio::main]
/// async fn main() {
/// let mut my_value = MyStructAsyncSendBuilder {
/// int_data: 42,
/// float_data: 3.14,
/// int_reference_builder: |int_data: &i32| Box::pin(async move { int_data }),
/// float_reference_builder: |float_data: &mut f32| Box::pin(async move { float_data }),
/// }.build().await;
///
/// // Prints 42
/// println!("{:?}", my_value.borrow_int_data());
/// // Prints 3.14
/// println!("{:?}", my_value.borrow_float_reference());
/// // Sets the value of float_data to 84.0
/// my_value.with_mut(|fields| {
/// **fields.float_reference = (**fields.int_reference as f32) * 2.0;
/// });
///
/// // We can hold on to this reference...
/// let int_ref = *my_value.borrow_int_reference();
/// println!("{:?}", *int_ref);
/// // As long as the struct is still alive.
/// drop(my_value);
/// // This will cause an error!
/// // println!("{:?}", *int_ref);
/// }
/// ```
///
/// # What does the macro generate?
/// The `#[self_referencing]` struct will replace your definition with an unsafe self-referencing
/// struct with a safe public interface. Many functions will be generated depending on your original
Expand Down Expand Up @@ -238,6 +284,9 @@
/// A basic async constructor. It works identically to the sync constructor differing only in the
/// type of closures it expects. Whenever a closure is required it is expected to return a Pinned
/// and Boxed Future that Outputs the same type as the synchronous version.
/// ### `MyStruct::new_async_send(fields...) -> MyStruct`
/// An async send constructor. It works identically to the ssync constructor differing only in the
/// Send trait being specified in the return type.
/// ### `MyStructBuilder`
/// This is the preferred way to create a new instance of your struct. It is similar to using the
/// `MyStruct { a, b, c, d }` syntax instead of `MyStruct::new(a, b, c, d)`. It contains one field
Expand All @@ -251,6 +300,8 @@
/// identically to the synchronous builder differing only in the type of closures it expects.
/// Whenever a closure is required it is expected to return a Pinned and Boxed Future that Outputs
/// the same type as the synchronous version.
/// ### `MyStructAsyncSendBuilder`
/// Same as MyStructAsyncBuilder, but with Send trait specified in the return type.
/// ### `MyStruct::try_new<E>(fields...) -> Result<MyStruct, E>`
/// Similar to the regular `new()` function, except the functions wich create values for all
/// **self-referencing fields** can return `Result<>`s. If any of those are `Err`s, that error will be
Expand All @@ -261,10 +312,14 @@
/// **self-referencing fields** can return `Result<>`s. If any of those are `Err`s, that error will be
/// returned instead of an instance of `MyStruct`. The preferred way to use this function is through
/// `MyStructAsyncTryBuilder` and its `try_build()` function.
/// ### `MyStruct::try_new_async_send<E>(fields...) -> Result<MyStruct, E>`
/// Same as `new_async()` function, but with Send trait specified in the return type.
/// ### `MyStruct::try_new_or_recover_async<E>(fields...) -> Result<MyStruct, (E, Heads)>`
/// Similar to the `try_new_async()` function, except that all the **head fields** are returned along side
/// the original error in case of an error. The preferred way to use this function is through
/// `MyStructAsyncTryBuilder` and its `try_build_or_recover()` function.
/// ### `MyStruct::try_new_or_recover_async_send<E>(fields...) -> Result<MyStruct, (E, Heads)>`
/// Same as `try_new_or_recover_async()` function, but with Send trait specified in the return type.
/// ### `MyStruct::with_FIELD<R>(&self, user: FnOnce(field: &FieldType) -> R) -> R`
/// This function is generated for every **tail and immutably-borrowed field** in your struct. It
/// allows safely accessing
Expand Down
42 changes: 23 additions & 19 deletions ouroboros_macro/src/generate/constructor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
info_structures::{ArgType, FieldType, Options, StructInfo},
info_structures::{ArgType, BuilderType, FieldType, Options, StructInfo},
utils::to_class_case,
};
use proc_macro2::{Ident, TokenStream};
Expand All @@ -9,7 +9,7 @@ use syn::Error;
pub fn create_builder_and_constructor(
info: &StructInfo,
options: Options,
make_async: bool,
builder_type: BuilderType,
) -> Result<(Ident, TokenStream, TokenStream), Error> {
let struct_name = info.ident.clone();
let generic_args = info.generic_arguments();
Expand All @@ -19,10 +19,10 @@ pub fn create_builder_and_constructor(
} else {
syn::parse_quote! { pub(super) }
};
let builder_struct_name = if make_async {
format_ident!("{}AsyncBuilder", info.ident)
} else {
format_ident!("{}Builder", info.ident)
let builder_struct_name = match builder_type {
BuilderType::AsyncSend => format_ident!("{}AsyncSendBuilder", info.ident),
BuilderType::Async => format_ident!("{}AsyncBuilder", info.ident),
BuilderType::Sync => format_ident!("{}Builder", info.ident),
};
let documentation = format!(
concat!(
Expand Down Expand Up @@ -67,7 +67,7 @@ pub fn create_builder_and_constructor(
for field in &info.fields {
let field_name = &field.name;

let arg_type = field.make_constructor_arg_type(&info, make_async)?;
let arg_type = field.make_constructor_arg_type(&info, builder_type)?;
if let ArgType::Plain(plain_type) = arg_type {
// No fancy builder function, we can just move the value directly into the struct.
params.push(quote! { #field_name: #plain_type });
Expand Down Expand Up @@ -101,7 +101,7 @@ pub fn create_builder_and_constructor(
}
}
doc_table += &format!(") -> {}: _` | \n", field_name.to_string());
if make_async {
if builder_type.is_async() {
code.push(quote! { let #field_name = #builder_name (#(#builder_args),*).await; });
} else {
code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); });
Expand Down Expand Up @@ -148,10 +148,10 @@ pub fn create_builder_and_constructor(
quote! { #[doc(hidden)] }
};

let constructor_fn = if make_async {
quote! { async fn new_async }
} else {
quote! { fn new }
let constructor_fn = match builder_type {
BuilderType::AsyncSend => quote! { async fn new_async_send },
BuilderType::Async => quote! { async fn new_async },
BuilderType::Sync => quote! { fn new },
};
let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect();
let constructor_def = quote! {
Expand All @@ -164,23 +164,27 @@ pub fn create_builder_and_constructor(
}
};
let generic_where = &info.generics.where_clause;
let builder_fn = if make_async {
let builder_fn = if builder_type.is_async() {
quote! { async fn build }
} else {
quote! { fn build }
};
let builder_code = if make_async {
quote! {
let builder_code = match builder_type {
BuilderType::AsyncSend => quote! {
#struct_name::new_async_send(
#(self.#builder_struct_field_names),*
).await
},
BuilderType::Async => quote! {
#struct_name::new_async(
#(self.#builder_struct_field_names),*
).await
}
} else {
quote! {
},
BuilderType::Sync => quote! {
#struct_name::new(
#(self.#builder_struct_field_names),*
)
}
},
};
let builder_def = quote! {
#builder_documentation
Expand Down
4 changes: 2 additions & 2 deletions ouroboros_macro/src/generate/summon_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::Error;

use crate::info_structures::{ArgType, StructInfo};
use crate::info_structures::{ArgType, BuilderType, StructInfo};

pub fn generate_checker_summoner(info: &StructInfo) -> Result<TokenStream, Error> {
let mut code: Vec<TokenStream> = Vec::new();
Expand All @@ -12,7 +12,7 @@ pub fn generate_checker_summoner(info: &StructInfo) -> Result<TokenStream, Error
for field in &info.fields {
let field_name = &field.name;

let arg_type = field.make_constructor_arg_type(&info, false)?;
let arg_type = field.make_constructor_arg_type(&info, BuilderType::Sync)?;
if let ArgType::Plain(plain_type) = arg_type {
// No fancy builder function, we can just move the value directly into the struct.
params.push(quote! { #field_name: #plain_type });
Expand Down
72 changes: 40 additions & 32 deletions ouroboros_macro/src/generate/try_constructor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
info_structures::{ArgType, FieldType, Options, StructInfo},
info_structures::{ArgType, BuilderType, FieldType, Options, StructInfo},
utils::to_class_case,
};
use proc_macro2::{Ident, TokenStream};
Expand All @@ -9,7 +9,7 @@ use syn::Error;
pub fn create_try_builder_and_constructor(
info: &StructInfo,
options: Options,
make_async: bool,
builder_type: BuilderType,
) -> Result<(Ident, TokenStream, TokenStream), Error> {
let struct_name = info.ident.clone();
let generic_args = info.generic_arguments();
Expand All @@ -31,10 +31,10 @@ pub fn create_try_builder_and_constructor(
}
let mut current_head_index = 0;

let builder_struct_name = if make_async {
format_ident!("{}AsyncTryBuilder", info.ident)
} else {
format_ident!("{}TryBuilder", info.ident)
let builder_struct_name = match builder_type {
BuilderType::AsyncSend => format_ident!("{}AsyncSendTryBuilder", info.ident),
BuilderType::Async => format_ident!("{}AsyncTryBuilder", info.ident),
BuilderType::Sync => format_ident!("{}TryBuilder", info.ident),
};
let documentation = format!(
concat!(
Expand Down Expand Up @@ -96,7 +96,7 @@ pub fn create_try_builder_and_constructor(
for field in &info.fields {
let field_name = &field.name;

let arg_type = field.make_try_constructor_arg_type(info, make_async)?;
let arg_type = field.make_try_constructor_arg_type(info, builder_type)?;
if let ArgType::Plain(plain_type) = arg_type {
// No fancy builder function, we can just move the value directly into the struct.
params.push(quote! { #field_name: #plain_type });
Expand Down Expand Up @@ -143,7 +143,7 @@ pub fn create_try_builder_and_constructor(
}
}
doc_table += &format!(") -> Result<{}: _, Error_>` | \n", field_name.to_string());
let builder_value = if make_async {
let builder_value = if builder_type.is_async() {
quote! { #builder_name (#(#builder_args),*).await }
} else {
quote! { #builder_name (#(#builder_args),*) }
Expand Down Expand Up @@ -202,22 +202,22 @@ pub fn create_try_builder_and_constructor(
} else {
quote! { #[doc(hidden)] }
};
let or_recover_ident = if make_async {
quote! { try_new_or_recover_async }
} else {
quote! { try_new_or_recover }
let or_recover_ident = match builder_type {
BuilderType::AsyncSend => quote! { try_new_or_recover_async_send },
BuilderType::Async => quote! { try_new_or_recover_async },
BuilderType::Sync => quote! { try_new_or_recover },
};
let or_recover_constructor_fn = if make_async {
let or_recover_constructor_fn = if builder_type.is_async() {
quote! { async fn #or_recover_ident }
} else {
quote! { fn #or_recover_ident }
};
let constructor_fn = if make_async {
quote! { async fn try_new_async }
} else {
quote! { fn try_new }
let constructor_fn = match builder_type {
BuilderType::AsyncSend => quote! { async fn try_new_async_send },
BuilderType::Async => quote! { async fn try_new_async },
BuilderType::Sync => quote! { fn try_new },
};
let constructor_code = if make_async {
let constructor_code = if builder_type.is_async() {
quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).await.map_err(|(error, _heads)| error) }
} else {
quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).map_err(|(error, _heads)| error) }
Expand All @@ -237,41 +237,49 @@ pub fn create_try_builder_and_constructor(
builder_struct_generic_producers.push(quote! { Error_ });
builder_struct_generic_consumers.push(quote! { Error_ });
let generic_where = &info.generics.where_clause;
let builder_fn = if make_async {
let builder_fn = if builder_type.is_async() {
quote! { async fn try_build }
} else {
quote! { fn try_build }
};
let or_recover_builder_fn = if make_async {
let or_recover_builder_fn = if builder_type.is_async() {
quote! { async fn try_build_or_recover }
} else {
quote! { fn try_build_or_recover }
};
let builder_code = if make_async {
quote! {
let builder_code = match builder_type {
BuilderType::AsyncSend => quote! {
#struct_name::try_new_async_send(
#(self.#builder_struct_field_names),*
).await
},
BuilderType::Async => quote! {
#struct_name::try_new_async(
#(self.#builder_struct_field_names),*
).await
}
} else {
quote! {
},
BuilderType::Sync => quote! {
#struct_name::try_new(
#(self.#builder_struct_field_names),*
)
}
},
};
let or_recover_builder_code = if make_async {
quote! {
let or_recover_builder_code = match builder_type {
BuilderType::AsyncSend => quote! {
#struct_name::try_new_or_recover_async_send(
#(self.#builder_struct_field_names),*
).await
},
BuilderType::Async => quote! {
#struct_name::try_new_or_recover_async(
#(self.#builder_struct_field_names),*
).await
}
} else {
quote! {
},
BuilderType::Sync => quote! {
#struct_name::try_new_or_recover(
#(self.#builder_struct_field_names),*
)
}
},
};
let builder_def = quote! {
#builder_documentation
Expand Down
Loading