From 840b980dcb96a11daeb27a6f70bcc44fd136c3a3 Mon Sep 17 00:00:00 2001 From: Jarrod Davis Date: Wed, 16 Mar 2022 22:32:39 -0600 Subject: [PATCH] Add "impl bridges" to `Component` derive macro --- core/tests/component.rs | 40 +++++++++++++++++++++++++++++-- derive/src/component.rs | 52 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/core/tests/component.rs b/core/tests/component.rs index 79ddcccf..aacbd5ad 100644 --- a/core/tests/component.rs +++ b/core/tests/component.rs @@ -3,7 +3,10 @@ mod example_app; use self::example_app::{ExampleApp, ExampleConfig}; -use abscissa_core::{component, Component, FrameworkError, FrameworkErrorKind::ComponentError}; +use abscissa_core::{ + component, Application, Component, FrameworkError, FrameworkErrorKind::ComponentError, Shutdown, +}; +use std::sync::atomic::{AtomicBool, Ordering}; /// ID for `FoobarComponent` (example component #1) const FOOBAR_COMPONENT_ID: component::Id = component::Id::new("component::FoobarComponent"); @@ -30,7 +33,23 @@ impl FoobarComponent { /// Example component #2 #[derive(Component, Debug, Default)] -pub struct BazComponent {} +#[component(after_config, before_shutdown)] +pub struct BazComponent { + pub after_config_run: bool, + pub before_shutdown_run: AtomicBool, +} + +impl BazComponent { + fn after_config(&mut self, _config: &A::Cfg) -> Result<(), FrameworkError> { + self.after_config_run = true; + Ok(()) + } + + fn before_shutdown(&self, _kind: Shutdown) -> Result<(), FrameworkError> { + self.before_shutdown_run.store(true, Ordering::Relaxed); + Ok(()) + } +} /// Example component #3 #[derive(Component, Debug, Default)] @@ -148,3 +167,20 @@ fn dependency_injection() { let quux = registry.get_downcast_ref::().unwrap(); assert_eq!(quux.foobar_state.as_ref().unwrap(), "original foobar state"); } + +#[test] +fn impl_bridges() { + let mut registry = component::Registry::default(); + let components = init_components(); + + registry.register(components).unwrap(); + registry.after_config(&ExampleConfig::default()).unwrap(); + + let baz = registry.get_downcast_ref::().unwrap(); + assert!(baz.after_config_run); + + registry + .shutdown(&ExampleApp::default(), Shutdown::Graceful) + .unwrap(); + assert!(baz.before_shutdown_run.load(Ordering::Relaxed)); +} diff --git a/derive/src/component.rs b/derive/src/component.rs index da1deaa6..a170202d 100644 --- a/derive/src/component.rs +++ b/derive/src/component.rs @@ -10,6 +10,8 @@ pub fn derive_component(s: Structure<'_>) -> TokenStream { let attrs = ComponentAttributes::from_derive_input(s.ast()); let name = &s.ast().ident; let abscissa_core = attrs.abscissa_core_crate(); + let after_config = attrs.after_config(); + let before_shutdown = attrs.before_shutdown(); let dependency_methods = attrs.dependency_methods(); s.gen_impl(quote! { @@ -29,6 +31,10 @@ pub fn derive_component(s: Structure<'_>) -> TokenStream { } #dependency_methods + + #after_config + + #before_shutdown } }) } @@ -36,6 +42,10 @@ pub fn derive_component(s: Structure<'_>) -> TokenStream { /// Parsed `#[component(...)]` attribute fields #[derive(Debug)] struct ComponentAttributes { + after_config: bool, + + before_shutdown: bool, + /// Special attribute used by `abscissa_core` to `derive(Component)`. /// /// Workaround for using custom derive on traits defined in the same crate: @@ -49,6 +59,8 @@ struct ComponentAttributes { impl ComponentAttributes { /// Parse component attributes from custom derive input. pub fn from_derive_input(input: &DeriveInput) -> Self { + let mut after_config = false; + let mut before_shutdown = false; let mut core = false; let mut inject = Vec::new(); @@ -61,9 +73,12 @@ impl ComponentAttributes { Meta::List(MetaList { nested, .. }) => { for meta in &nested { match meta { - NestedMeta::Meta(Meta::Path(path)) if path.is_ident("core") => { - core = true - } + NestedMeta::Meta(Meta::Path(path)) => match path.get_ident() { + Some(id) if id == "after_config" => after_config = true, + Some(id) if id == "before_shutdown" => before_shutdown = true, + Some(id) if id == "core" => core = true, + _ => panic!("malformed `component` attribute: {:?}", meta), + }, NestedMeta::Meta(Meta::NameValue { .. }) => { inject.push(InjectAttribute::from_nested_meta(meta)) } @@ -75,7 +90,12 @@ impl ComponentAttributes { }; } - Self { core, inject } + Self { + after_config, + before_shutdown, + core, + inject, + } } /// Ident for the `abscissa_core` crate. @@ -88,6 +108,30 @@ impl ComponentAttributes { Ident::new(crate_name, Span::call_site()) } + pub fn after_config(&self) -> TokenStream { + if !self.after_config { + return quote!(); + } + + quote! { + fn after_config(&mut self, config: &A::Cfg) -> Result<(), FrameworkError> { + self.after_config::(config) + } + } + } + + pub fn before_shutdown(&self) -> TokenStream { + if !self.before_shutdown { + return quote!(); + } + + quote! { + fn before_shutdown(&self, kind: Shutdown) -> Result<(), FrameworkError> { + self.before_shutdown(kind) + } + } + } + /// Generate `Component::dependencies()` and `register_dependencies()` pub fn dependency_methods(&self) -> TokenStream { if self.inject.is_empty() {