mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 11:41:02 +00:00
Create a more rigid overseer builder pattern that fails at compile time (#4753)
Introduces `Missing<Field>` and `Init<Field>` states, that are used in place of builder generics, and make this possible.
This commit is contained in:
@@ -15,41 +15,15 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use quote::{format_ident, quote};
|
||||
use syn::Ident;
|
||||
use syn::{parse_quote, Path, PathSegment};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Returns all combinations for a single replacement:
|
||||
/// 1. generic args with `NEW` in place
|
||||
/// 2. subsystem type to be replaced
|
||||
/// 3. the subsystem name to be replaced by a new type and value
|
||||
/// 4. all other subsystems that are supposed to be kept
|
||||
fn derive_replacable_generic_lists(
|
||||
info: &OverseerInfo,
|
||||
) -> Vec<(TokenStream, Ident, Ident, Vec<Ident>)> {
|
||||
// subsystem generic types
|
||||
let builder_generic_ty = info.builder_generic_types();
|
||||
|
||||
let to_be_replaced_name = info.subsystem_names_without_wip();
|
||||
let baggage_generic_ty = &info.baggage_generic_types();
|
||||
|
||||
builder_generic_ty
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, to_be_replaced_ty)| {
|
||||
let mut to_keep_name = to_be_replaced_name.clone();
|
||||
let to_be_replaced_name: Ident = to_keep_name.remove(idx);
|
||||
|
||||
let mut builder_generic_ty = builder_generic_ty.clone();
|
||||
builder_generic_ty[idx] = format_ident!("NEW");
|
||||
|
||||
let generics_ts = quote! {
|
||||
<S, #( #baggage_generic_ty, )* #( #builder_generic_ty, )* >
|
||||
};
|
||||
|
||||
(generics_ts, to_be_replaced_ty.clone(), to_be_replaced_name, to_keep_name)
|
||||
})
|
||||
.collect::<Vec<(_, _, _, _)>>()
|
||||
fn recollect_without_idx<T: Clone>(x: &[T], idx: usize) -> Vec<T> {
|
||||
let mut v = Vec::<T>::with_capacity(x.len().saturating_sub(1));
|
||||
v.extend(x.iter().take(idx).cloned());
|
||||
v.extend(x.iter().skip(idx + 1).cloned());
|
||||
v
|
||||
}
|
||||
|
||||
/// Implement a builder pattern for the `Overseer`-type,
|
||||
@@ -58,24 +32,15 @@ fn derive_replacable_generic_lists(
|
||||
/// Elements tagged with `wip` are not covered here.
|
||||
pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let overseer_name = info.overseer_name.clone();
|
||||
let builder = Ident::new(&(overseer_name.to_string() + "Builder"), overseer_name.span());
|
||||
let handle = Ident::new(&(overseer_name.to_string() + "Handle"), overseer_name.span());
|
||||
let connector = Ident::new(&(overseer_name.to_string() + "Connector"), overseer_name.span());
|
||||
let builder = format_ident!("{}Builder", overseer_name);
|
||||
let handle = format_ident!("{}Handle", overseer_name);
|
||||
let connector = format_ident!("{}Connector", overseer_name);
|
||||
let subsystem_ctx_name = format_ident!("{}SubsystemContext", overseer_name);
|
||||
|
||||
let subsystem_name = &info.subsystem_names_without_wip();
|
||||
let subsystem_name_init_with = &info
|
||||
.subsystem_names_without_wip()
|
||||
.iter()
|
||||
.map(|subsystem_name| format_ident!("{}_with", subsystem_name))
|
||||
.collect::<Vec<_>>();
|
||||
let subsystem_name_replace_with = &info
|
||||
.subsystem_names_without_wip()
|
||||
.iter()
|
||||
.map(|subsystem_name| format_ident!("replace_{}", subsystem_name))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let builder_generic_ty = &info.builder_generic_types();
|
||||
let subsystem_generics = &info.subsystem_generic_types();
|
||||
|
||||
let consumes = &info.consumes_without_wip();
|
||||
let channel_name = &info.channel_names_without_wip("");
|
||||
let channel_name_unbounded = &info.channel_names_without_wip("_unbounded");
|
||||
|
||||
@@ -85,11 +50,20 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let channel_name_rx = &info.channel_names_without_wip("_rx");
|
||||
let channel_name_unbounded_rx = &info.channel_names_without_wip("_unbounded_rx");
|
||||
|
||||
let baggage_generic_ty = &info.baggage_generic_types();
|
||||
let baggage_name = &info.baggage_names();
|
||||
let baggage_ty = &info.baggage_types();
|
||||
let baggage_generic_ty = &info.baggage_generic_types();
|
||||
|
||||
let subsystem_ctx_name = format_ident!("{}SubsystemContext", overseer_name);
|
||||
// State generics that are used to encode each field's status (Init/Missing)
|
||||
let baggage_passthrough_state_generics = baggage_name
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, _)| format_ident!("InitStateBaggage{}", idx))
|
||||
.collect::<Vec<_>>();
|
||||
let subsystem_passthrough_state_generics = subsystem_name
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, _)| format_ident!("InitStateSubsystem{}", idx))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let error_ty = &info.extern_error_ty;
|
||||
|
||||
@@ -107,49 +81,256 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let generics = quote! {
|
||||
< S, #( #baggage_generic_ty, )* >
|
||||
};
|
||||
let where_clause = quote! {
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
// Helpers to use within quote! macros
|
||||
let spawner_where_clause: syn::TypeParam = parse_quote! {
|
||||
S: #support_crate ::SpawnNamed + Send
|
||||
};
|
||||
|
||||
let builder_generics = quote! {
|
||||
<S, #( #baggage_generic_ty, )* #( #builder_generic_ty, )* >
|
||||
};
|
||||
// Field names and real types
|
||||
let field_name = subsystem_name.iter().chain(baggage_name.iter()).collect::<Vec<_>>();
|
||||
let field_type = subsystem_generics
|
||||
.iter()
|
||||
.map(|ident| Path::from(PathSegment::from(ident.clone())))
|
||||
.chain(info.baggage().iter().map(|bag| bag.field_ty.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// all subsystems must have the same context
|
||||
// even if the overseer does not impose such a limit.
|
||||
let builder_additional_generics = quote! {
|
||||
<#( #builder_generic_ty, )* >
|
||||
};
|
||||
// Setters logic
|
||||
|
||||
let consumes = &info.consumes();
|
||||
// For each setter we need to leave the remaining fields untouched and
|
||||
// remove the field that we are fixing in this setter
|
||||
// For subsystems `*_with` and `replace_*` setters are needed.
|
||||
let subsystem_specific_setters =
|
||||
info.subsystems().iter().filter(|ssf| !ssf.wip).enumerate().map(|(idx, ssf)| {
|
||||
let field_name = &ssf.name;
|
||||
let field_type = &ssf.generic;
|
||||
let subsystem_consumes = &ssf.consumes;
|
||||
// Remove state generic for the item to be replaced. It sufficient to know `field_type` for
|
||||
// that since we always move from `Init<#field_type>` to `Init<NEW>`.
|
||||
let impl_subsystem_state_generics = recollect_without_idx(&subsystem_passthrough_state_generics[..], idx);
|
||||
|
||||
let subsyste_ctx_name =
|
||||
Ident::new(&(overseer_name.to_string() + "SubsystemContext"), overseer_name.span());
|
||||
let field_name_with = format_ident!("{}_with", field_name);
|
||||
let field_name_replace = format_ident!("replace_{}", field_name);
|
||||
|
||||
let builder_where_clause = quote! {
|
||||
where
|
||||
S: #support_crate ::SpawnNamed,
|
||||
#(
|
||||
#builder_generic_ty : Subsystem<#subsyste_ctx_name< #consumes >, #error_ty>,
|
||||
)*
|
||||
};
|
||||
// In a setter we replace `Uninit<T>` with `Init<T>` leaving all other
|
||||
// types as they are, as such they will be free generics.
|
||||
let mut current_state_generics = subsystem_passthrough_state_generics
|
||||
.iter()
|
||||
.map(|subsystem_state_generic_ty| parse_quote!(#subsystem_state_generic_ty))
|
||||
.collect::<Vec<syn::GenericArgument>>();
|
||||
current_state_generics[idx] = parse_quote! { Missing<#field_type> };
|
||||
|
||||
// Generics that will be present after initializing a specific `Missing<_>` field.
|
||||
let mut post_setter_state_generics = current_state_generics.clone();
|
||||
post_setter_state_generics[idx] = parse_quote! { Init<#field_type> };
|
||||
|
||||
let mut post_replace_state_generics = current_state_generics.clone();
|
||||
post_replace_state_generics[idx] = parse_quote! { Init<NEW> };
|
||||
|
||||
// All fields except the one we update with the new argument
|
||||
// see the loop below.
|
||||
let to_keep_subsystem_name = recollect_without_idx(&subsystem_name[..], idx);
|
||||
|
||||
// Create the field init `fn`
|
||||
quote! {
|
||||
impl <InitStateSpawner, #field_type, #( #impl_subsystem_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
#builder <InitStateSpawner, #( #current_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
where
|
||||
#field_type : Subsystem<#subsystem_ctx_name<#subsystem_consumes>, #error_ty>,
|
||||
{
|
||||
/// Specify the subsystem in the builder directly
|
||||
pub fn #field_name (self, var: #field_type ) ->
|
||||
#builder <InitStateSpawner, #( #post_setter_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
{
|
||||
#builder {
|
||||
#field_name: Init::<#field_type>::Value(var),
|
||||
#(
|
||||
#to_keep_subsystem_name: self. #to_keep_subsystem_name,
|
||||
)*
|
||||
#(
|
||||
#baggage_name: self. #baggage_name,
|
||||
)*
|
||||
spawner: self.spawner,
|
||||
}
|
||||
}
|
||||
/// Specify the the initialization function for a subsystem
|
||||
pub fn #field_name_with<'a, F>(self, subsystem_init_fn: F ) ->
|
||||
#builder <InitStateSpawner, #( #post_setter_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
where
|
||||
F: 'static + FnOnce(#handle) ->
|
||||
::std::result::Result<#field_type, #error_ty>,
|
||||
{
|
||||
let boxed_func = Init::<#field_type>::Fn(
|
||||
Box::new(subsystem_init_fn) as SubsystemInitFn<#field_type>
|
||||
);
|
||||
#builder {
|
||||
#field_name: boxed_func,
|
||||
#(
|
||||
#to_keep_subsystem_name: self. #to_keep_subsystem_name,
|
||||
)*
|
||||
#(
|
||||
#baggage_name: self. #baggage_name,
|
||||
)*
|
||||
spawner: self.spawner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <InitStateSpawner, #field_type, #( #impl_subsystem_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
#builder <InitStateSpawner, #( #post_setter_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
where
|
||||
#field_type : Subsystem<#subsystem_ctx_name<#subsystem_consumes>, #error_ty>,
|
||||
{
|
||||
/// Replace a subsystem by another implementation for the
|
||||
/// consumable message type.
|
||||
pub fn #field_name_replace<NEW, F>(self, gen_replacement_fn: F)
|
||||
-> #builder <InitStateSpawner, #( #post_replace_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
where
|
||||
#field_type: 'static,
|
||||
F: 'static + FnOnce(#field_type) -> NEW,
|
||||
NEW: #support_crate ::Subsystem<#subsystem_ctx_name< #subsystem_consumes >, #error_ty>,
|
||||
{
|
||||
let replacement: Init<NEW> = match self.#field_name {
|
||||
Init::Fn(fx) =>
|
||||
Init::<NEW>::Fn(Box::new(move |handle: #handle| {
|
||||
let orig = fx(handle)?;
|
||||
Ok(gen_replacement_fn(orig))
|
||||
})),
|
||||
Init::Value(val) =>
|
||||
Init::Value(gen_replacement_fn(val)),
|
||||
};
|
||||
#builder {
|
||||
#field_name: replacement,
|
||||
#(
|
||||
#to_keep_subsystem_name: self. #to_keep_subsystem_name,
|
||||
)*
|
||||
#(
|
||||
#baggage_name: self. #baggage_name,
|
||||
)*
|
||||
spawner: self.spawner,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Produce setters for all baggage fields as well
|
||||
let baggage_specific_setters = info.baggage().iter().enumerate().map(|(idx, bag_field)| {
|
||||
// Baggage fields follow subsystems
|
||||
let fname = &bag_field.field_name;
|
||||
let field_type = &bag_field.field_ty;
|
||||
let impl_baggage_state_generics = recollect_without_idx(&baggage_passthrough_state_generics[..], idx);
|
||||
let to_keep_baggage_name = recollect_without_idx(&baggage_name[..], idx);
|
||||
|
||||
let mut pre_setter_generics = baggage_passthrough_state_generics
|
||||
.iter()
|
||||
.map(|gen_ty| parse_quote!(#gen_ty))
|
||||
.collect::<Vec<syn::GenericArgument>>();
|
||||
pre_setter_generics[idx] = parse_quote! { Missing<#field_type> };
|
||||
|
||||
let mut post_setter_generics = pre_setter_generics.clone();
|
||||
post_setter_generics[idx] = parse_quote! { Init<#field_type> };
|
||||
|
||||
// Baggage can also be generic, so we need to include that to a signature
|
||||
let preserved_baggage_generic = if bag_field.generic {
|
||||
quote! {#field_type,}
|
||||
} else {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl <InitStateSpawner, #preserved_baggage_generic #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
|
||||
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #pre_setter_generics, )* >
|
||||
{
|
||||
/// Specify the baggage in the builder when it was not initialized before
|
||||
pub fn #fname (self, var: #field_type ) ->
|
||||
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #post_setter_generics, )* >
|
||||
{
|
||||
#builder {
|
||||
#fname: Init::<#field_type>::Value(var),
|
||||
#(
|
||||
#subsystem_name: self. #subsystem_name,
|
||||
)*
|
||||
#(
|
||||
#to_keep_baggage_name: self. #to_keep_baggage_name,
|
||||
)*
|
||||
spawner: self.spawner,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl <InitStateSpawner, #preserved_baggage_generic #( #subsystem_passthrough_state_generics, )* #( #impl_baggage_state_generics, )* >
|
||||
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #post_setter_generics, )* > {
|
||||
/// Specify the baggage in the builder when it has been previously initialized
|
||||
pub fn #fname (self, var: #field_type ) ->
|
||||
#builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #post_setter_generics, )* >
|
||||
{
|
||||
#builder {
|
||||
#fname: Init::<#field_type>::Value(var),
|
||||
#(
|
||||
#subsystem_name: self. #subsystem_name,
|
||||
)*
|
||||
#(
|
||||
#to_keep_baggage_name: self. #to_keep_baggage_name,
|
||||
)*
|
||||
spawner: self.spawner,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let event = &info.extern_event_ty;
|
||||
let initialized_builder = format_ident!("Initialized{}", builder);
|
||||
// The direct generics as expected by the `Overseer<_,_,..>`, without states
|
||||
let initialized_builder_generics = quote! {
|
||||
S, #( #baggage_generic_ty, )* #( #subsystem_generics, )*
|
||||
};
|
||||
|
||||
let mut ts = quote! {
|
||||
impl #generics #overseer_name #generics #where_clause {
|
||||
/// Create a new overseer utilizing the builder.
|
||||
pub fn builder #builder_additional_generics () -> #builder #builder_generics
|
||||
#builder_where_clause
|
||||
{
|
||||
#builder :: default()
|
||||
/// Convenience alias.
|
||||
type SubsystemInitFn<T> = Box<dyn FnOnce(#handle) -> ::std::result::Result<T, #error_ty> >;
|
||||
|
||||
/// Type for the initialized field of the overseer builder
|
||||
pub enum Init<T> {
|
||||
/// Defer initialization to a point where the `handle` is available.
|
||||
Fn(SubsystemInitFn<T>),
|
||||
/// Directly initialize the subsystem with the given subsystem type `T`.
|
||||
/// Also used for baggage fields
|
||||
Value(T),
|
||||
}
|
||||
/// Type marker for the uninitialized field of the overseer builder.
|
||||
/// `PhantomData` is used for type hinting when creating uninitialized
|
||||
/// builder, e.g. to avoid specifying the generics when instantiating
|
||||
/// the `FooBuilder` when calling `Foo::builder()`
|
||||
#[derive(Debug)]
|
||||
pub struct Missing<T>(::core::marker::PhantomData<T>);
|
||||
|
||||
/// Trait used to mark fields status in a builder
|
||||
trait OverseerFieldState<T> {}
|
||||
|
||||
impl<T> OverseerFieldState<T> for Init<T> {}
|
||||
impl<T> OverseerFieldState<T> for Missing<T> {}
|
||||
|
||||
impl<T> ::std::default::Default for Missing<T> {
|
||||
fn default() -> Self {
|
||||
Missing::<T>(::core::marker::PhantomData::<T>::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, #( #baggage_generic_ty, )*> #overseer_name <S, #( #baggage_generic_ty, )*> where #spawner_where_clause {
|
||||
/// Create a new overseer utilizing the builder.
|
||||
pub fn builder< #( #subsystem_generics),* >() ->
|
||||
#builder<Missing<S> #(, Missing<#field_type> )* >
|
||||
where
|
||||
#(
|
||||
#subsystem_generics : Subsystem<#subsystem_ctx_name< #consumes >, #error_ty>,
|
||||
)*
|
||||
{
|
||||
#builder :: new()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ts.extend(quote! {
|
||||
/// Handle for an overseer.
|
||||
pub type #handle = #support_crate ::metered::MeteredSender< #event >;
|
||||
|
||||
@@ -192,39 +373,26 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/// Convenience alias.
|
||||
type SubsystemInitFn<T> = Box<dyn FnOnce(#handle) -> ::std::result::Result<T, #error_ty> >;
|
||||
|
||||
/// Initialization type to be used for a field of the overseer.
|
||||
enum FieldInitMethod<T> {
|
||||
/// Defer initialization to a point where the `handle` is available.
|
||||
Fn(SubsystemInitFn<T>),
|
||||
/// Directly initialize the subsystem with the given subsystem type `T`.
|
||||
Value(T),
|
||||
/// Subsystem field does not have a value just yet.
|
||||
Uninitialized
|
||||
}
|
||||
|
||||
impl<T> ::std::default::Default for FieldInitMethod<T> {
|
||||
fn default() -> Self {
|
||||
Self::Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub struct #builder #builder_generics {
|
||||
ts.extend(quote!{
|
||||
/// Builder pattern to create compile time safe construction path.
|
||||
pub struct #builder <InitStateSpawner, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*> {
|
||||
#(
|
||||
#subsystem_name : FieldInitMethod< #builder_generic_ty >,
|
||||
#subsystem_name: #subsystem_passthrough_state_generics,
|
||||
)*
|
||||
#(
|
||||
#baggage_name : ::std::option::Option< #baggage_ty >,
|
||||
#baggage_name: #baggage_passthrough_state_generics,
|
||||
)*
|
||||
spawner: ::std::option::Option< S >,
|
||||
spawner: InitStateSpawner,
|
||||
}
|
||||
});
|
||||
|
||||
impl #builder_generics Default for #builder #builder_generics {
|
||||
fn default() -> Self {
|
||||
ts.extend(quote! {
|
||||
impl<#initialized_builder_generics> #builder<Missing<S>, #( Missing<#field_type>, )*>
|
||||
{
|
||||
/// Create a new builder pattern, with all fields being uninitialized.
|
||||
fn new() -> Self {
|
||||
// explicitly assure the required traits are implemented
|
||||
fn trait_from_must_be_implemented<E>()
|
||||
where
|
||||
@@ -234,62 +402,57 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
trait_from_must_be_implemented::< #error_ty >();
|
||||
|
||||
Self {
|
||||
#(
|
||||
#subsystem_name: Default::default(),
|
||||
)*
|
||||
#(
|
||||
#baggage_name: None,
|
||||
)*
|
||||
spawner: None,
|
||||
#(
|
||||
#field_name: Missing::<#field_type>::default(),
|
||||
)*
|
||||
spawner: Missing::<S>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl #builder_generics #builder #builder_generics #builder_where_clause {
|
||||
/// The spawner to use for spawning tasks.
|
||||
pub fn spawner(mut self, spawner: S) -> Self
|
||||
where
|
||||
S: #support_crate ::SpawnNamed + Send
|
||||
// Spawner setter
|
||||
ts.extend(quote!{
|
||||
impl<S, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
#builder<Missing<S>, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
where
|
||||
#spawner_where_clause
|
||||
{
|
||||
/// The `spawner` to use for spawning tasks.
|
||||
pub fn spawner(self, spawner: S) -> #builder<Init<S>, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*>
|
||||
{
|
||||
self.spawner = Some(spawner);
|
||||
self
|
||||
#builder {
|
||||
#(
|
||||
#field_name: self. #field_name,
|
||||
)*
|
||||
spawner: Init::<S>::Value(spawner),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ts.extend(quote! {
|
||||
/// Type used to represent a builder where all fields are initialized and the overseer could be constructed.
|
||||
pub type #initialized_builder<#initialized_builder_generics> = #builder<Init<S>, #( Init<#field_type>, )*>;
|
||||
|
||||
// A builder specialization where all fields are set
|
||||
impl<#initialized_builder_generics> #initialized_builder<#initialized_builder_generics>
|
||||
where
|
||||
#spawner_where_clause,
|
||||
#(
|
||||
/// Specify the particular subsystem implementation.
|
||||
pub fn #subsystem_name (mut self, subsystem: #builder_generic_ty ) -> Self {
|
||||
self. #subsystem_name = FieldInitMethod::Value( subsystem );
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the particular subsystem by giving a init function.
|
||||
pub fn #subsystem_name_init_with <'a, F> (mut self, subsystem_init_fn: F ) -> Self
|
||||
where
|
||||
F: 'static + FnOnce(#handle) -> ::std::result::Result<#builder_generic_ty, #error_ty>,
|
||||
{
|
||||
self. #subsystem_name = FieldInitMethod::Fn(
|
||||
Box::new(subsystem_init_fn) as SubsystemInitFn<#builder_generic_ty>
|
||||
);
|
||||
self
|
||||
}
|
||||
#subsystem_generics : Subsystem<#subsystem_ctx_name< #consumes >, #error_ty>,
|
||||
)*
|
||||
|
||||
#(
|
||||
/// Attach the user defined addendum type.
|
||||
pub fn #baggage_name (mut self, baggage: #baggage_ty ) -> Self {
|
||||
self. #baggage_name = Some( baggage );
|
||||
self
|
||||
}
|
||||
)*
|
||||
|
||||
{
|
||||
/// Complete the construction and create the overseer type.
|
||||
pub fn build(self) -> ::std::result::Result<(#overseer_name #generics, #handle), #error_ty> {
|
||||
pub fn build(self)
|
||||
-> ::std::result::Result<(#overseer_name<S, #( #baggage_generic_ty, )*>, #handle), #error_ty> {
|
||||
let connector = #connector ::default();
|
||||
self.build_with_connector(connector)
|
||||
}
|
||||
|
||||
/// Complete the construction and create the overseer type based on an existing `connector`.
|
||||
pub fn build_with_connector(self, connector: #connector) -> ::std::result::Result<(#overseer_name #generics, #handle), #error_ty>
|
||||
pub fn build_with_connector(self, connector: #connector)
|
||||
-> ::std::result::Result<(#overseer_name<S, #( #baggage_generic_ty, )*>, #handle), #error_ty>
|
||||
{
|
||||
let #connector {
|
||||
handle: events_tx,
|
||||
@@ -327,20 +490,19 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
)*
|
||||
};
|
||||
|
||||
let mut spawner = self.spawner.expect("Spawner is set. qed");
|
||||
let mut spawner = match self.spawner {
|
||||
Init::Value(value) => value,
|
||||
_ => unreachable!("Only ever init spawner as value. qed"),
|
||||
};
|
||||
|
||||
let mut running_subsystems = #support_crate ::FuturesUnordered::<
|
||||
BoxFuture<'static, ::std::result::Result<(), #error_ty > >
|
||||
>::new();
|
||||
|
||||
#(
|
||||
// TODO generate a builder pattern that ensures this
|
||||
// TODO https://github.com/paritytech/polkadot/issues/3427
|
||||
let #subsystem_name = match self. #subsystem_name {
|
||||
FieldInitMethod::Fn(func) => func(handle.clone())?,
|
||||
FieldInitMethod::Value(val) => val,
|
||||
FieldInitMethod::Uninitialized =>
|
||||
panic!("All subsystems must exist with the builder pattern."),
|
||||
Init::Fn(func) => func(handle.clone())?,
|
||||
Init::Value(val) => val,
|
||||
};
|
||||
|
||||
let unbounded_meter = #channel_name_unbounded_rx.meter().clone();
|
||||
@@ -351,11 +513,11 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
let (signal_tx, signal_rx) = #support_crate ::metered::channel(SIGNAL_CHANNEL_CAPACITY);
|
||||
|
||||
// Generate subsystem name based on overseer field name.
|
||||
let mut subsystem_string = String::from(stringify!(#subsystem_name));
|
||||
let subsystem_string = String::from(stringify!(#subsystem_name));
|
||||
// Convert owned `snake case` string to a `kebab case` static str.
|
||||
let subsystem_static_str = Box::leak(subsystem_string.replace("_", "-").into_boxed_str());
|
||||
|
||||
let ctx = #subsyste_ctx_name::< #consumes >::new(
|
||||
let ctx = #subsystem_ctx_name::< #consumes >::new(
|
||||
signal_rx,
|
||||
message_rx,
|
||||
channels_out.clone(),
|
||||
@@ -376,15 +538,6 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
)?;
|
||||
)*
|
||||
|
||||
#(
|
||||
let #baggage_name = self. #baggage_name .expect(
|
||||
&format!("Baggage variable `{0}` of `{1}` must be set by the user!",
|
||||
stringify!(#baggage_name),
|
||||
stringify!(#overseer_name)
|
||||
)
|
||||
);
|
||||
)*
|
||||
|
||||
use #support_crate ::StreamExt;
|
||||
|
||||
let to_overseer_rx = to_overseer_rx.fuse();
|
||||
@@ -394,7 +547,10 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
)*
|
||||
|
||||
#(
|
||||
#baggage_name,
|
||||
#baggage_name: match self. #baggage_name {
|
||||
Init::Value(val) => val,
|
||||
_ => panic!("unexpected baggage initialization, must be value"),
|
||||
},
|
||||
)*
|
||||
|
||||
spawner,
|
||||
@@ -406,84 +562,10 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream {
|
||||
Ok((overseer, handle))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut acc = TokenStream::new();
|
||||
|
||||
for (
|
||||
(
|
||||
(
|
||||
ref modified_generics,
|
||||
ref to_be_replaced_ty,
|
||||
ref to_be_replaced_name,
|
||||
ref to_keep_name,
|
||||
),
|
||||
subsystem_name_replace_with,
|
||||
),
|
||||
consumes,
|
||||
) in derive_replacable_generic_lists(info)
|
||||
.into_iter()
|
||||
.zip(subsystem_name_replace_with.iter())
|
||||
.zip(consumes.iter())
|
||||
{
|
||||
let replace1 = quote! {
|
||||
/// Replace a subsystem by another implementation for the
|
||||
/// consumable message type.
|
||||
pub fn #subsystem_name_replace_with < NEW, F >
|
||||
(self, gen_replacement_fn: F) -> #builder #modified_generics
|
||||
where
|
||||
#to_be_replaced_ty: 'static,
|
||||
F: 'static + FnOnce(#to_be_replaced_ty) -> NEW,
|
||||
NEW: #support_crate ::Subsystem<#subsystem_ctx_name< #consumes >, #error_ty>,
|
||||
{
|
||||
|
||||
let Self {
|
||||
#to_be_replaced_name,
|
||||
#(
|
||||
#to_keep_name,
|
||||
)*
|
||||
#(
|
||||
#baggage_name,
|
||||
)*
|
||||
spawner,
|
||||
} = self;
|
||||
|
||||
// Some cases require that parts of the original are copied
|
||||
// over, since they include a one time initialization.
|
||||
let replacement: FieldInitMethod<NEW> = match #to_be_replaced_name {
|
||||
FieldInitMethod::Fn(fx) => FieldInitMethod::Fn(
|
||||
Box::new(move |handle: #handle| {
|
||||
let orig = fx(handle)?;
|
||||
Ok(gen_replacement_fn(orig))
|
||||
})
|
||||
),
|
||||
FieldInitMethod::Value(val) => FieldInitMethod::Value(gen_replacement_fn(val)),
|
||||
FieldInitMethod::Uninitialized => panic!("Must have a value before it can be replaced. qed"),
|
||||
};
|
||||
|
||||
#builder :: #modified_generics {
|
||||
#to_be_replaced_name: replacement,
|
||||
#(
|
||||
#to_keep_name,
|
||||
)*
|
||||
#(
|
||||
#baggage_name,
|
||||
)*
|
||||
spawner,
|
||||
}
|
||||
}
|
||||
};
|
||||
acc.extend(replace1);
|
||||
}
|
||||
|
||||
ts.extend(quote! {
|
||||
impl #builder_generics #builder #builder_generics
|
||||
#builder_where_clause
|
||||
{
|
||||
#acc
|
||||
}
|
||||
});
|
||||
|
||||
ts.extend(baggage_specific_setters);
|
||||
ts.extend(subsystem_specific_setters);
|
||||
ts.extend(impl_task_kind(info));
|
||||
ts
|
||||
}
|
||||
|
||||
@@ -264,12 +264,21 @@ impl OverseerInfo {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn subsystem_generic_types(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|sff| sff.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn baggage(&self) -> &[BaggageField] {
|
||||
self.baggage.as_slice()
|
||||
}
|
||||
|
||||
pub(crate) fn baggage_names(&self) -> Vec<Ident> {
|
||||
self.baggage.iter().map(|bag| bag.field_name.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
pub(crate) fn baggage_types(&self) -> Vec<Path> {
|
||||
self.baggage.iter().map(|bag| bag.field_ty.clone()).collect::<Vec<_>>()
|
||||
}
|
||||
pub(crate) fn baggage_decl(&self) -> Vec<TokenStream> {
|
||||
self.baggage
|
||||
.iter()
|
||||
@@ -280,15 +289,6 @@ impl OverseerInfo {
|
||||
.collect::<Vec<TokenStream>>()
|
||||
}
|
||||
|
||||
/// Generic types per subsystem, as defined by the user.
|
||||
pub(crate) fn builder_generic_types(&self) -> Vec<Ident> {
|
||||
self.subsystems
|
||||
.iter()
|
||||
.filter(|ssf| !ssf.wip)
|
||||
.map(|sff| sff.generic.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub(crate) fn baggage_generic_types(&self) -> Vec<Ident> {
|
||||
self.baggage
|
||||
.iter()
|
||||
|
||||
Reference in New Issue
Block a user