diff --git a/polkadot/bridges/bin/rialto/node/src/overseer.rs b/polkadot/bridges/bin/rialto/node/src/overseer.rs index 517f83f206..bd38ceca65 100644 --- a/polkadot/bridges/bin/rialto/node/src/overseer.rs +++ b/polkadot/bridges/bin/rialto/node/src/overseer.rs @@ -33,7 +33,7 @@ use polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig; use polkadot_node_core_provisioner::ProvisionerConfig; use polkadot_node_network_protocol::request_response::{v1 as request_v1, IncomingRequestReceiver}; use polkadot_overseer::{ - metrics::Metrics as OverseerMetrics, BlockInfo, MetricsTrait, Overseer, OverseerBuilder, + metrics::Metrics as OverseerMetrics, BlockInfo, MetricsTrait, Overseer, InitializedOverseerBuilder, OverseerConnector, OverseerHandle, }; use polkadot_primitives::v1::ParachainHost; @@ -139,7 +139,7 @@ pub fn prepared_overseer_builder( disputes_enabled, }: OverseerGenArgs<'_, Spawner, RuntimeClient>, ) -> Result< - OverseerBuilder< + InitializedOverseerBuilder< Spawner, Arc, CandidateValidationSubsystem, diff --git a/polkadot/node/overseer/overseer-gen/README.md b/polkadot/node/overseer/overseer-gen/README.md new file mode 100644 index 0000000000..1a68882b4b --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/README.md @@ -0,0 +1,78 @@ +# overseer pattern + +The overseer pattern is a partial actor pattern + +## proc-macro + +The proc macro provides a convenience generator with a builder pattern, +where at it's core it creates and spawns a set of subsystems, which are purely +declarative. + +```rust + #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] + pub struct Overseer { + #[subsystem(MsgA)] + sub_a: AwesomeSubSysA, + + #[subsystem(MsgB)] + sub_b: AwesomeSubSysB, + } +``` + +* Each subsystem is annotated with `#[subsystem(_)]` where `MsgA` respectively `MsgB` are the messages +being consumed by that particular subsystem. Each of those subsystems is required to implement the subsystem +trait. +* `error=` tells the overseer to use the user provided +error type, if not provided a builtin one is used. Note that this is the one error type used throughout all calls, so make sure it does impl `From` for all other error types `E` that are relevant to your application. +* `event=` declares an external event type, that injects certain events +into the overseer, without participating in the subsystem pattern. +* `signal=` defines a signal type to be used for the overseer. This is a shared "clock" for all subsystems. +* `gen=` defines a wrapping `enum` type that is used to wrap all messages that can be consumed by _any_ subsystem. + +```rust + /// Execution context, always requred. + pub struct DummyCtx; + + /// Task spawner, always required. + pub struct DummySpawner; + + fn main() { + let _overseer = Overseer::builder() + .sub_a(AwesomeSubSysA::default()) + .sub_b(AwesomeSubSysB::default()) + .spawner(DummySpawner) + .build(); + } +``` + +In the shown `main`, the overseer is created by means of a generated, compile time erroring +builder pattern. + +The builder requires all subsystems, baggage fields (additional struct data) and spawner to be +set via the according setter method before `build` method could even be called. Failure to do +such an initialization will lead to a compile error. This is implemented by encoding each +builder field in a set of so called `state generics`, meaning that each field can be either +`Init` or `Missing`, so each setter translates a state from `Missing` to `Init` state +for the specific struct field. Therefore, if you see a compile time error that blames about +`Missing` where `Init` is expected it usually means that some subsystems or baggage fields were +not set prior to the `build` call. + +To exclude subsystems from such a check, one can set `wip` attribute on some subsystem that +is not ready to be included in the Overseer: + +```rust + #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] + pub struct Overseer { + #[subsystem(MsgA)] + sub_a: AwesomeSubSysA, + + #[subsystem(MsgB), wip] + sub_b: AwesomeSubSysB, // This subsystem will not be required nor allowed to be set + } +``` + +Baggage fields can be initialized more than one time, however, it is not true for subsystems: +subsystems must be initialized only once (another compile time check) or be _replaced_ by +a special setter like method `replace_`. + +A task spawner and subsystem context are required to be defined with `SpawnNamed` and respectively `SubsystemContext` implemented. diff --git a/polkadot/node/overseer/overseer-gen/examples/dummy.rs b/polkadot/node/overseer/overseer-gen/examples/dummy.rs index a6ed1dd2af..d263d9fca8 100644 --- a/polkadot/node/overseer/overseer-gen/examples/dummy.rs +++ b/polkadot/node/overseer/overseer-gen/examples/dummy.rs @@ -2,6 +2,7 @@ use polkadot_node_network_protocol::WrongVariant; use polkadot_overseer_gen::*; +use std::collections::HashMap; /// Concrete subsystem implementation for `MsgStrukt` msg type. #[derive(Default)] @@ -88,7 +89,7 @@ impl NetworkMsg { } #[overlord(signal=SigSigSig, event=EvX, error=Yikes, network=NetworkMsg, gen=AllMessages)] -struct Xxx { +struct Xxx { #[subsystem(MsgStrukt)] sub0: AwesomeSubSys, @@ -96,6 +97,8 @@ struct Xxx { plinkos: GoblinTower, i_like_pi: f64, + i_like_generic: T, + i_like_hash: HashMap, } #[derive(Debug, Clone)] @@ -125,12 +128,16 @@ impl SpawnNamed for DummySpawner { struct DummyCtx; fn main() { - let (overseer, _handle): (Xxx<_>, _) = Xxx::builder() + let (overseer, _handle): (Xxx<_, f64>, _) = Xxx::builder() .sub0(AwesomeSubSys::default()) .plinkos(GoblinTower::default()) .i_like_pi(::std::f64::consts::PI) + .i_like_generic(42.0) + .i_like_hash(HashMap::new()) .spawner(DummySpawner) .build() .unwrap(); assert_eq!(overseer.i_like_pi.floor() as i8, 3); + assert_eq!(overseer.i_like_generic.floor() as i8, 42); + assert_eq!(overseer.i_like_hash.len() as i8, 0); } diff --git a/polkadot/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs b/polkadot/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs index a5c8c3a4a5..be2b00715f 100644 --- a/polkadot/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs +++ b/polkadot/node/overseer/overseer-gen/proc-macro/src/impl_builder.rs @@ -15,41 +15,15 @@ // along with Polkadot. If not, see . 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)> { - // 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! { - - }; - - (generics_ts, to_be_replaced_ty.clone(), to_be_replaced_name, to_keep_name) - }) - .collect::>() +fn recollect_without_idx(x: &[T], idx: usize) -> Vec { + let mut v = Vec::::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::>(); - let subsystem_name_replace_with = &info - .subsystem_names_without_wip() - .iter() - .map(|subsystem_name| format_ident!("replace_{}", subsystem_name)) - .collect::>(); - - 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::>(); + let subsystem_passthrough_state_generics = subsystem_name + .iter() + .enumerate() + .map(|(idx, _)| format_ident!("InitStateSubsystem{}", idx)) + .collect::>(); let error_ty = &info.extern_error_ty; @@ -107,49 +81,256 @@ pub(crate) fn impl_builder(info: &OverseerInfo) -> proc_macro2::TokenStream { }) .collect::>(); - 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! { - - }; + // Field names and real types + let field_name = subsystem_name.iter().chain(baggage_name.iter()).collect::>(); + 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::>(); - // 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`. + 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` with `Init` 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::>(); + 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 }; + + // 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 + #builder + 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 + { + #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 + 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 + #builder + 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(self, gen_replacement_fn: F) + -> #builder + where + #field_type: 'static, + F: 'static + FnOnce(#field_type) -> NEW, + NEW: #support_crate ::Subsystem<#subsystem_ctx_name< #subsystem_consumes >, #error_ty>, + { + let replacement: Init = match self.#field_name { + Init::Fn(fx) => + Init::::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::>(); + 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 + #builder + { + /// Specify the baggage in the builder when it was not initialized before + pub fn #fname (self, var: #field_type ) -> + #builder + { + #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 + #builder { + /// Specify the baggage in the builder when it has been previously initialized + pub fn #fname (self, var: #field_type ) -> + #builder + { + #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 = Box ::std::result::Result >; + + /// Type for the initialized field of the overseer builder + pub enum Init { + /// Defer initialization to a point where the `handle` is available. + Fn(SubsystemInitFn), + /// 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(::core::marker::PhantomData); + + /// Trait used to mark fields status in a builder + trait OverseerFieldState {} + + impl OverseerFieldState for Init {} + impl OverseerFieldState for Missing {} + + impl ::std::default::Default for Missing { + fn default() -> Self { + Missing::(::core::marker::PhantomData::::default()) } } + impl #overseer_name where #spawner_where_clause { + /// Create a new overseer utilizing the builder. + pub fn builder< #( #subsystem_generics),* >() -> + #builder #(, 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 = Box ::std::result::Result >; - - /// Initialization type to be used for a field of the overseer. - enum FieldInitMethod { - /// Defer initialization to a point where the `handle` is available. - Fn(SubsystemInitFn), - /// Directly initialize the subsystem with the given subsystem type `T`. - Value(T), - /// Subsystem field does not have a value just yet. - Uninitialized - } - - impl ::std::default::Default for FieldInitMethod { - 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 { #( - #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<#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() 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::::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 + #builder, #( #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, #( #subsystem_passthrough_state_generics, )* #( #baggage_passthrough_state_generics, )*> { - self.spawner = Some(spawner); - self + #builder { + #( + #field_name: self. #field_name, + )* + spawner: Init::::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<#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, #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, #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 = 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 } diff --git a/polkadot/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs b/polkadot/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs index f06c09061f..2eed4001d6 100644 --- a/polkadot/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs +++ b/polkadot/node/overseer/overseer-gen/proc-macro/src/parse_struct.rs @@ -264,12 +264,21 @@ impl OverseerInfo { .collect::>() } + pub(crate) fn subsystem_generic_types(&self) -> Vec { + self.subsystems + .iter() + .filter(|ssf| !ssf.wip) + .map(|sff| sff.generic.clone()) + .collect::>() + } + + pub(crate) fn baggage(&self) -> &[BaggageField] { + self.baggage.as_slice() + } + pub(crate) fn baggage_names(&self) -> Vec { self.baggage.iter().map(|bag| bag.field_name.clone()).collect::>() } - pub(crate) fn baggage_types(&self) -> Vec { - self.baggage.iter().map(|bag| bag.field_ty.clone()).collect::>() - } pub(crate) fn baggage_decl(&self) -> Vec { self.baggage .iter() @@ -280,15 +289,6 @@ impl OverseerInfo { .collect::>() } - /// Generic types per subsystem, as defined by the user. - pub(crate) fn builder_generic_types(&self) -> Vec { - self.subsystems - .iter() - .filter(|ssf| !ssf.wip) - .map(|sff| sff.generic.clone()) - .collect::>() - } - pub(crate) fn baggage_generic_types(&self) -> Vec { self.baggage .iter() diff --git a/polkadot/node/overseer/overseer-gen/src/tests.rs b/polkadot/node/overseer/overseer-gen/src/tests.rs index 7e7999c670..ad8745b386 100644 --- a/polkadot/node/overseer/overseer-gen/src/tests.rs +++ b/polkadot/node/overseer/overseer-gen/src/tests.rs @@ -7,3 +7,9 @@ fn ui_compile_fail() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/err-*.rs"); } + +#[test] +fn ui_pass() { + let t = trybuild::TestCases::new(); + t.pass("tests/ui/ok-*.rs"); +} diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-01-duplicate-consumer.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-01-duplicate-consumer.stderr index a66f2fa3e7..18ba674324 100644 --- a/polkadot/node/overseer/overseer-gen/tests/ui/err-01-duplicate-consumer.stderr +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-01-duplicate-consumer.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `std::convert::From` for type `AllMessages` - --> $DIR/err-01-duplicate-consumer.rs:19:1 + --> tests/ui/err-01-duplicate-consumer.rs:19:1 | 19 | #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ error[E0119]: conflicting implementations of trait `std::convert::From` for type `OverseerSubsystemSender` - --> $DIR/err-01-duplicate-consumer.rs:19:1 + --> tests/ui/err-01-duplicate-consumer.rs:19:1 | 19 | #[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs b/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs new file mode 100644 index 0000000000..a51248b540 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.rs @@ -0,0 +1,61 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSys; + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSys { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + unimplemented!("starting yay!") + } +} + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgStrukt(u8); + +#[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] +struct Overseer { + #[subsystem(no_dispatch, MsgStrukt)] + sub0: AwesomeSubSys, + i_like_pie: f64, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +struct DummyCtx; + +fn main() { + let _ = Overseer::builder() + .sub0(AwesomeSubSys::default()) + //.i_like_pie(std::f64::consts::PI) // The filed is not initialised + .spawner(DummySpawner) + .build() + .unwrap(); +} \ No newline at end of file diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.stderr new file mode 100644 index 0000000000..dcd9fbaa01 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-05-missing-field.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `build` found for struct `OverseerBuilder, Init, Missing>` in the current scope + --> tests/ui/err-05-missing-field.rs:59:4 + | +22 | #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] + | -------------------------------------------------------------------------------- method `build` not found for this +... +59 | .build() + | ^^^^^ method not found in `OverseerBuilder, Init, Missing>` + | + = note: the method was found for + - `OverseerBuilder, Init, Init>` + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `build`, perhaps you need to implement one of them: + candidate #1: `frame_support::traits::hooks::GenesisBuild` + candidate #2: `prometheus::vec::MetricVecBuilder` diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs b/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs new file mode 100644 index 0000000000..540ed366a0 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.rs @@ -0,0 +1,61 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSys; + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSys { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + unimplemented!("starting yay!") + } +} + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgStrukt(u8); + +#[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] +struct Overseer { + #[subsystem(no_dispatch, MsgStrukt)] + sub0: AwesomeSubSys, + i_like_pie: f64, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +struct DummyCtx; + +fn main() { + let _ = Overseer::builder() + //.sub0(AwesomeSubSys::default()) // Subsystem is uninitialized + .i_like_pie(std::f64::consts::PI) + .spawner(DummySpawner) + .build() + .unwrap(); +} diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.stderr new file mode 100644 index 0000000000..6d7a210fd9 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-06-missing-subsystem.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `build` found for struct `OverseerBuilder, Missing<_>, Init>` in the current scope + --> tests/ui/err-06-missing-subsystem.rs:59:4 + | +22 | #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] + | -------------------------------------------------------------------------------- method `build` not found for this +... +59 | .build() + | ^^^^^ method not found in `OverseerBuilder, Missing<_>, Init>` + | + = note: the method was found for + - `OverseerBuilder, Init, Init>` + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `build`, perhaps you need to implement one of them: + candidate #1: `frame_support::traits::hooks::GenesisBuild` + candidate #2: `prometheus::vec::MetricVecBuilder` diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs b/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs new file mode 100644 index 0000000000..bc25e0c47c --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.rs @@ -0,0 +1,61 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSys; + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSys { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + unimplemented!("starting yay!") + } +} + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgStrukt(u8); + +#[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] +struct Overseer { + #[subsystem(no_dispatch, MsgStrukt)] + sub0: AwesomeSubSys, + i_like_pie: f64, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +struct DummyCtx; + +fn main() { + let _ = Overseer::builder() + .sub0(AwesomeSubSys::default()) + .i_like_pie(std::f64::consts::PI) + //.spawner(DummySpawner) // Spawner is missing + .build() + .unwrap(); +} diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.stderr new file mode 100644 index 0000000000..d265f36f5f --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-07-missing-spawner.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `build` found for struct `OverseerBuilder, Init, Init>` in the current scope + --> tests/ui/err-07-missing-spawner.rs:59:4 + | +22 | #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] + | -------------------------------------------------------------------------------- method `build` not found for this +... +59 | .build() + | ^^^^^ method not found in `OverseerBuilder, Init, Init>` + | + = note: the method was found for + - `OverseerBuilder, Init, Init>` + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `build`, perhaps you need to implement one of them: + candidate #1: `frame_support::traits::hooks::GenesisBuild` + candidate #2: `prometheus::vec::MetricVecBuilder` diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs b/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs new file mode 100644 index 0000000000..ea2ad0b4d1 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.rs @@ -0,0 +1,62 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSys; + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSys { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + unimplemented!("starting yay!") + } +} + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgStrukt(u8); + +#[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] +struct Overseer { + #[subsystem(no_dispatch, MsgStrukt)] + sub0: AwesomeSubSys, + i_like_pie: f64, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +struct DummyCtx; + +fn main() { + let _ = Overseer::builder() + .sub0(AwesomeSubSys::default()) + .sub0(AwesomeSubSys::default()) // Duplicate subsystem + .i_like_pie(std::f64::consts::PI) + .spawner(DummySpawner) + .build() + .unwrap(); +} diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.stderr new file mode 100644 index 0000000000..a0fcee914f --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-08-duplicate-subsystem.stderr @@ -0,0 +1,10 @@ +error[E0599]: no method named `sub0` found for struct `OverseerBuilder, Init, Missing>` in the current scope + --> tests/ui/err-08-duplicate-subsystem.rs:57:4 + | +22 | #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] + | -------------------------------------------------------------------------------- method `sub0` not found for this +... +57 | .sub0(AwesomeSubSys::default()) // Duplicate subsystem + | ^^^^-------------------------- help: remove the arguments + | | + | field, not a method diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs b/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs new file mode 100644 index 0000000000..06ce22dba3 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.rs @@ -0,0 +1,61 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSys; + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSys { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + unimplemented!("starting yay!") + } +} + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgStrukt(u8); + +#[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] +struct Overseer { + #[subsystem(no_dispatch, MsgStrukt)] + sub0: AwesomeSubSys, + i_like_pie: T, +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + unimplemented!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +struct DummyCtx; + +fn main() { + let (_, _): (Overseer<_, f64>, _) = Overseer::builder() + .sub0(AwesomeSubSys::default()) + //.i_like_pie(std::f64::consts::PI) // The filed is not initialised + .spawner(DummySpawner) + .build() + .unwrap(); +} diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.stderr b/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.stderr new file mode 100644 index 0000000000..26998c4e41 --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/err-09-uninit_generic_baggage.stderr @@ -0,0 +1,15 @@ +error[E0599]: no method named `build` found for struct `OverseerBuilder, Init, Missing<_>>` in the current scope + --> tests/ui/err-09-uninit_generic_baggage.rs:59:4 + | +22 | #[overlord(signal=SigSigSig, error=OverseerError, event=Event, gen=AllMessages)] + | -------------------------------------------------------------------------------- method `build` not found for this +... +59 | .build() + | ^^^^^ method not found in `OverseerBuilder, Init, Missing<_>>` + | + = note: the method was found for + - `OverseerBuilder, Init, Init>` + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `build`, perhaps you need to implement one of them: + candidate #1: `frame_support::traits::hooks::GenesisBuild` + candidate #2: `prometheus::vec::MetricVecBuilder` diff --git a/polkadot/node/overseer/overseer-gen/tests/ui/ok-01-wip.rs b/polkadot/node/overseer/overseer-gen/tests/ui/ok-01-wip.rs new file mode 100644 index 0000000000..b07855f67a --- /dev/null +++ b/polkadot/node/overseer/overseer-gen/tests/ui/ok-01-wip.rs @@ -0,0 +1,75 @@ +#![allow(dead_code)] + +use polkadot_overseer_gen::*; + +#[derive(Default)] +struct AwesomeSubSysA; + + +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSysA { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + SpawnedSubsystem { name: "sub A", future: Box::pin(async move { Ok(()) }) } + } +} +impl ::polkadot_overseer_gen::Subsystem, OverseerError> for AwesomeSubSysB { + fn start(self, _ctx: OverseerSubsystemContext) -> SpawnedSubsystem { + SpawnedSubsystem { name: "sub B", future: Box::pin(async move { Ok(()) }) } + } +} + +#[derive(Debug, Clone)] +pub struct DummySpawner; + +impl SpawnNamed for DummySpawner { + fn spawn_blocking( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + println!("spawn blocking {} {}", task_name, subsystem_name.unwrap_or("default")) + } + + fn spawn( + &self, + task_name: &'static str, + subsystem_name: Option<&'static str>, + _future: futures::future::BoxFuture<'static, ()>, + ) { + println!("spawn {} {}", task_name, subsystem_name.unwrap_or("default")) + } +} + +#[derive(Default)] +struct AwesomeSubSysB; + +#[derive(Clone, Debug)] +pub struct SigSigSig; + +pub struct Event; + +#[derive(Clone, Debug)] +pub struct MsgA(u8); + +#[derive(Clone, Debug)] +pub struct MsgB(u8); + +#[overlord(signal=SigSigSig, event=Event, gen=AllMessages, error=OverseerError)] +pub struct Overseer { + #[subsystem(MsgA)] + sub_a: AwesomeSubSysA, + + #[subsystem(wip, MsgB)] + sub_b: AwesomeSubSysB, +} + +pub struct DummyCtx; + +fn main() { + let _overseer_builder = Overseer::builder() + .sub_a(AwesomeSubSysA::default()) + // b is tagged as `wip` + // .sub_b(AwesomeSubSysB::default()) + .spawner(DummySpawner) + .build(); +} diff --git a/polkadot/node/overseer/src/dummy.rs b/polkadot/node/overseer/src/dummy.rs index 216356cf3f..dba531cbf0 100644 --- a/polkadot/node/overseer/src/dummy.rs +++ b/polkadot/node/overseer/src/dummy.rs @@ -15,8 +15,8 @@ // along with Polkadot. If not, see . use crate::{ - prometheus::Registry, AllMessages, HeadSupportsParachains, MetricsTrait, Overseer, - OverseerBuilder, OverseerMetrics, OverseerSignal, OverseerSubsystemContext, SpawnNamed, + prometheus::Registry, AllMessages, HeadSupportsParachains, InitializedOverseerBuilder, + MetricsTrait, Overseer, OverseerMetrics, OverseerSignal, OverseerSubsystemContext, SpawnNamed, KNOWN_LEAVES_CACHE_SIZE, }; use lru::LruCache; @@ -66,7 +66,7 @@ pub fn dummy_overseer_builder<'a, Spawner, SupportsParachains>( supports_parachains: SupportsParachains, registry: Option<&'a Registry>, ) -> Result< - OverseerBuilder< + InitializedOverseerBuilder< Spawner, SupportsParachains, DummySubsystem, @@ -107,7 +107,7 @@ pub fn one_for_all_overseer_builder<'a, Spawner, SupportsParachains, Sub>( subsystem: Sub, registry: Option<&'a Registry>, ) -> Result< - OverseerBuilder< + InitializedOverseerBuilder< Spawner, SupportsParachains, Sub, diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index 2b673e894c..4b4d9682e0 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -30,8 +30,8 @@ pub use polkadot_overseer::{ HeadSupportsParachains, }; use polkadot_overseer::{ - metrics::Metrics as OverseerMetrics, BlockInfo, MetricsTrait, Overseer, OverseerBuilder, - OverseerConnector, OverseerHandle, + metrics::Metrics as OverseerMetrics, BlockInfo, InitializedOverseerBuilder, MetricsTrait, + Overseer, OverseerConnector, OverseerHandle, }; use polkadot_primitives::v2::ParachainHost; @@ -142,7 +142,7 @@ pub fn prepared_overseer_builder<'a, Spawner, RuntimeClient>( pvf_checker_enabled, }: OverseerGenArgs<'a, Spawner, RuntimeClient>, ) -> Result< - OverseerBuilder< + InitializedOverseerBuilder< Spawner, Arc, CandidateValidationSubsystem,