Skip to content

Commit

Permalink
Merge pull request #56 from nappa85/main
Browse files Browse the repository at this point in the history
Create async Send alternative
  • Loading branch information
someguynamedjosh authored Mar 17, 2022
2 parents 6bbf159 + 8ed9b58 commit a20705a
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 72 deletions.
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

0 comments on commit a20705a

Please sign in to comment.