From b1e18041fdb13ba62301e300ce076f3aa9dc1dfe Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 25 Mar 2024 15:58:04 +0100 Subject: [PATCH 1/3] derive: Add delegate_to option for ExtensionDispatch This patch adds a delegate_to option to the ExtensionDispatch derive macro that makes it possible to create an alias for a backend using a different set of extensions. To avoid misunderstandings, delegating backends are forced to use the unit type (). --- derive/examples/extension-dispatch.rs | 26 +++- derive/src/extension_dispatch.rs | 173 ++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 13 deletions(-) diff --git a/derive/examples/extension-dispatch.rs b/derive/examples/extension-dispatch.rs index bcc4862c13b..256dfc13621 100644 --- a/derive/examples/extension-dispatch.rs +++ b/derive/examples/extension-dispatch.rs @@ -1,7 +1,9 @@ use trussed::Error; mod backends { - use super::extensions::{TestExtension, TestReply, TestRequest}; + use super::extensions::{ + SampleExtension, SampleReply, SampleRequest, TestExtension, TestReply, TestRequest, + }; use trussed::{ backend::Backend, platform::Platform, serde_extensions::ExtensionImpl, service::ServiceResources, types::CoreContext, Error, @@ -26,6 +28,18 @@ mod backends { } } + impl ExtensionImpl for ABackend { + fn extension_request( + &mut self, + _core_ctx: &mut CoreContext, + _backend_ctx: &mut Self::Context, + _request: &SampleRequest, + _resources: &mut ServiceResources

, + ) -> Result { + Ok(SampleReply) + } + } + #[derive(Default)] pub struct BBackend; @@ -88,6 +102,7 @@ mod extensions { enum Backend { A, + ASample, B, } @@ -106,6 +121,11 @@ enum Extension { struct Dispatch { #[extensions("Test")] a: backends::ABackend, + + #[dispatch(delegate_to = "a")] + #[extensions("Sample")] + a_sample: (), + b: backends::BBackend, } @@ -135,5 +155,9 @@ fn main() { &[BackendId::Custom(Backend::B)], Some(Error::RequestNotAvailable), ); + run( + &[BackendId::Custom(Backend::ASample)], + Some(Error::RequestNotAvailable), + ); run(&[BackendId::Custom(Backend::A)], None); } diff --git a/derive/src/extension_dispatch.rs b/derive/src/extension_dispatch.rs index 54d0e2a03af..09f073e28d9 100644 --- a/derive/src/extension_dispatch.rs +++ b/derive/src/extension_dispatch.rs @@ -15,6 +15,7 @@ pub struct ExtensionDispatch { dispatch_attrs: DispatchAttrs, extension_attrs: ExtensionAttrs, backends: Vec, + delegated_backends: Vec, } impl ExtensionDispatch { @@ -27,11 +28,29 @@ impl ExtensionDispatch { }; let dispatch_attrs = DispatchAttrs::new(&input)?; let extension_attrs = ExtensionAttrs::new(&input)?; - let backends = data_struct + let raw_backends: Vec<_> = data_struct .fields .iter() - .enumerate() - .map(|(i, field)| Backend::new(i, field, &extension_attrs.extensions)) + .map(RawBackend::new) + .collect::>()?; + let mut backends = Vec::new(); + let mut delegated_backends = Vec::new(); + for raw_backend in raw_backends { + if let Some(delegate_to) = raw_backend.delegate_to.clone() { + delegated_backends.push((raw_backend, delegate_to)); + } else { + backends.push(Backend::new( + backends.len(), + raw_backend, + &extension_attrs.extensions, + )?); + } + } + let delegated_backends = delegated_backends + .into_iter() + .map(|(raw, delegate_to)| { + DelegatedBackend::new(raw, delegate_to, &backends, &extension_attrs.extensions) + }) .collect::>()?; Ok(Self { name: input.ident, @@ -39,6 +58,7 @@ impl ExtensionDispatch { dispatch_attrs, extension_attrs, backends, + delegated_backends, }) } @@ -49,7 +69,15 @@ impl ExtensionDispatch { let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); let context = self.backends.iter().map(Backend::context); let requests = self.backends.iter().map(Backend::request); + let delegated_requests = self + .delegated_backends + .iter() + .map(DelegatedBackend::request); let extension_requests = self.backends.iter().map(Backend::extension_request); + let delegated_extension_requests = self + .delegated_backends + .iter() + .map(DelegatedBackend::extension_request); let extension_impls = self .extension_attrs .extensions @@ -71,6 +99,7 @@ impl ExtensionDispatch { ) -> ::core::result::Result<::trussed::api::Reply, ::trussed::error::Error> { match backend { #(#requests)* + #(#delegated_requests)* } } @@ -84,6 +113,7 @@ impl ExtensionDispatch { ) -> ::core::result::Result<::trussed::api::reply::SerdeExtension, ::trussed::error::Error> { match backend { #(#extension_requests)* + #(#delegated_extension_requests)* } } } @@ -165,32 +195,70 @@ impl ExtensionAttrs { } } -struct Backend { +struct RawBackend { id: Ident, field: Ident, ty: Type, - index: Index, - extensions: Vec, + delegate_to: Option, + extensions: Vec, } -impl Backend { - fn new(i: usize, field: &Field, extension_types: &HashMap) -> Result { +impl RawBackend { + fn new(field: &Field) -> Result { let ident = field.ident.clone().ok_or_else(|| { Error::new_spanned( field, "ExtensionDispatch can only be derived for a struct with named fields", ) })?; + let mut delegate_to = None; + for attr in util::get_attrs(&field.attrs, "dispatch") { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("delegate_to") { + let s: LitStr = meta.value()?.parse()?; + delegate_to = Some(s.parse()?); + Ok(()) + } else { + Err(meta.error("unsupported dispatch attribute")) + } + })?; + } let mut extensions = Vec::new(); for attr in util::get_attrs(&field.attrs, "extensions") { for s in attr.parse_args_with(Punctuated::::parse_terminated)? { - extensions.push(Extension::new(&s, extension_types)?); + extensions.push(s.parse()?); } } Ok(Self { id: util::to_camelcase(&ident), field: ident, ty: field.ty.clone(), + delegate_to, + extensions, + }) + } +} + +#[derive(Clone)] +struct Backend { + id: Ident, + field: Ident, + ty: Type, + index: Index, + extensions: Vec, +} + +impl Backend { + fn new(i: usize, raw: RawBackend, extensions: &HashMap) -> Result { + let extensions = raw + .extensions + .into_iter() + .map(|i| Extension::new(i, extensions)) + .collect::>()?; + Ok(Self { + id: raw.id, + field: raw.field, + ty: raw.ty, index: Index::from(i), extensions, }) @@ -224,17 +292,98 @@ impl Backend { } } +struct DelegatedBackend { + id: Ident, + field: Ident, + backend: Backend, + extensions: Vec, +} + +impl DelegatedBackend { + fn new( + raw: RawBackend, + delegate_to: Ident, + backends: &[Backend], + extensions: &HashMap, + ) -> Result { + match raw.ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => (), + _ => { + return Err(Error::new_spanned( + &raw.ty, + "delegated backends must use the unit type ()", + )); + } + } + + let extensions = raw + .extensions + .into_iter() + .map(|i| Extension::new(i, extensions)) + .collect::>()?; + let backend = backends + .iter() + .find(|backend| backend.field == delegate_to) + .ok_or_else(|| Error::new_spanned(delegate_to, "unknown backend"))? + .clone(); + Ok(Self { + id: raw.id, + field: raw.field, + backend, + extensions, + }) + } + + fn request(&self) -> TokenStream { + let Self { + id, backend, field, .. + } = self; + let Backend { + field: delegated_field, + index: delegated_index, + .. + } = backend; + quote! { + Self::BackendId::#id => { + let _ = self.#field; + ::trussed::backend::Backend::request( + &mut self.#delegated_field, &mut ctx.core, &mut ctx.backends.#delegated_index, request, resources, + ) + } + } + } + + fn extension_request(&self) -> TokenStream { + let Self { + id, + extensions, + backend, + field, + } = self; + let extension_requests = extensions.iter().map(|e| e.extension_request(backend)); + quote! { + Self::BackendId::#id => { + let _ = self.#field; + match extension { + #(#extension_requests)* + _ => Err(::trussed::error::Error::RequestNotAvailable), + } + } + } + } +} + +#[derive(Clone)] struct Extension { id: Ident, ty: Path, } impl Extension { - fn new(s: &LitStr, extensions: &HashMap) -> Result { - let id = s.parse()?; + fn new(id: Ident, extensions: &HashMap) -> Result { let ty = extensions .get(&id) - .ok_or_else(|| Error::new_spanned(s, "unknown extension ID"))? + .ok_or_else(|| Error::new_spanned(&id, "unknown extension ID"))? .clone(); Ok(Self { id, ty }) } From fb842e8ab001aeadfb085df5a85e5bde26896dac Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 25 Mar 2024 16:26:25 +0100 Subject: [PATCH 2/3] derive: Add skip option to ExtensionDispatch This patch adds a skip option to the ExtensionDispatch macro that makes it possible to ignore some fields. --- derive/examples/extension-dispatch.rs | 4 +++ derive/src/extension_dispatch.rs | 36 ++++++++++++++++----------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/derive/examples/extension-dispatch.rs b/derive/examples/extension-dispatch.rs index 256dfc13621..3915f7ba320 100644 --- a/derive/examples/extension-dispatch.rs +++ b/derive/examples/extension-dispatch.rs @@ -127,6 +127,10 @@ struct Dispatch { a_sample: (), b: backends::BBackend, + + #[allow(unused)] + #[dispatch(skip)] + other: String, } fn main() { diff --git a/derive/src/extension_dispatch.rs b/derive/src/extension_dispatch.rs index 09f073e28d9..5c30412d866 100644 --- a/derive/src/extension_dispatch.rs +++ b/derive/src/extension_dispatch.rs @@ -28,11 +28,12 @@ impl ExtensionDispatch { }; let dispatch_attrs = DispatchAttrs::new(&input)?; let extension_attrs = ExtensionAttrs::new(&input)?; - let raw_backends: Vec<_> = data_struct - .fields - .iter() - .map(RawBackend::new) - .collect::>()?; + let mut raw_backends = Vec::new(); + for field in &data_struct.fields { + if let Some(raw_backend) = RawBackend::new(field)? { + raw_backends.push(raw_backend); + } + } let mut backends = Vec::new(); let mut delegated_backends = Vec::new(); for raw_backend in raw_backends { @@ -204,38 +205,45 @@ struct RawBackend { } impl RawBackend { - fn new(field: &Field) -> Result { - let ident = field.ident.clone().ok_or_else(|| { - Error::new_spanned( - field, - "ExtensionDispatch can only be derived for a struct with named fields", - ) - })?; + fn new(field: &Field) -> Result> { let mut delegate_to = None; + let mut skip = false; for attr in util::get_attrs(&field.attrs, "dispatch") { attr.parse_nested_meta(|meta| { if meta.path.is_ident("delegate_to") { let s: LitStr = meta.value()?.parse()?; delegate_to = Some(s.parse()?); Ok(()) + } else if meta.path.is_ident("skip") { + skip = true; + Ok(()) } else { Err(meta.error("unsupported dispatch attribute")) } })?; } + if skip { + return Ok(None); + } + let ident = field.ident.clone().ok_or_else(|| { + Error::new_spanned( + field, + "ExtensionDispatch can only be derived for a struct with named fields", + ) + })?; let mut extensions = Vec::new(); for attr in util::get_attrs(&field.attrs, "extensions") { for s in attr.parse_args_with(Punctuated::::parse_terminated)? { extensions.push(s.parse()?); } } - Ok(Self { + Ok(Some(Self { id: util::to_camelcase(&ident), field: ident, ty: field.ty.clone(), delegate_to, extensions, - }) + })) } } From 5eed121ce987c7e82ad1eaff4b9b7d7bc716ed91 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 25 Mar 2024 16:44:31 +0100 Subject: [PATCH 3/3] derive: Add no_core option to ExtensionDispatch This patch adds an option to skip a backend when dispatching core requests. --- derive/examples/extension-dispatch.rs | 1 + derive/src/extension_dispatch.rs | 61 ++++++++++++++++++++------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/derive/examples/extension-dispatch.rs b/derive/examples/extension-dispatch.rs index 3915f7ba320..13bd4d9d735 100644 --- a/derive/examples/extension-dispatch.rs +++ b/derive/examples/extension-dispatch.rs @@ -119,6 +119,7 @@ enum Extension { Sample = "extensions::SampleExtension" )] struct Dispatch { + #[dispatch(no_core)] #[extensions("Test")] a: backends::ABackend, diff --git a/derive/src/extension_dispatch.rs b/derive/src/extension_dispatch.rs index 5c30412d866..47f1a0abf30 100644 --- a/derive/src/extension_dispatch.rs +++ b/derive/src/extension_dispatch.rs @@ -200,6 +200,7 @@ struct RawBackend { id: Ident, field: Ident, ty: Type, + no_core: bool, delegate_to: Option, extensions: Vec, } @@ -207,6 +208,7 @@ struct RawBackend { impl RawBackend { fn new(field: &Field) -> Result> { let mut delegate_to = None; + let mut no_core = false; let mut skip = false; for attr in util::get_attrs(&field.attrs, "dispatch") { attr.parse_nested_meta(|meta| { @@ -214,6 +216,9 @@ impl RawBackend { let s: LitStr = meta.value()?.parse()?; delegate_to = Some(s.parse()?); Ok(()) + } else if meta.path.is_ident("no_core") { + no_core = true; + Ok(()) } else if meta.path.is_ident("skip") { skip = true; Ok(()) @@ -241,6 +246,7 @@ impl RawBackend { id: util::to_camelcase(&ident), field: ident, ty: field.ty.clone(), + no_core, delegate_to, extensions, })) @@ -253,6 +259,7 @@ struct Backend { field: Ident, ty: Type, index: Index, + no_core: bool, extensions: Vec, } @@ -268,6 +275,7 @@ impl Backend { field: raw.field, ty: raw.ty, index: Index::from(i), + no_core: raw.no_core, extensions, }) } @@ -278,13 +286,23 @@ impl Backend { } fn request(&self) -> TokenStream { - let Self { - index, id, field, .. - } = self; + let id = &self.id; + let request = if self.no_core { + quote! { + Err(::trussed::Error::RequestNotAvailable) + } + } else { + let Self { index, field, .. } = self; + quote! { + ::trussed::backend::Backend::request( + &mut self.#field, &mut ctx.core, &mut ctx.backends.#index, request, resources, + ) + } + }; quote! { - Self::BackendId::#id => ::trussed::backend::Backend::request( - &mut self.#field, &mut ctx.core, &mut ctx.backends.#index, request, resources, - ), + Self::BackendId::#id => { + #request + } } } @@ -304,6 +322,7 @@ struct DelegatedBackend { id: Ident, field: Ident, backend: Backend, + no_core: bool, extensions: Vec, } @@ -338,26 +357,35 @@ impl DelegatedBackend { id: raw.id, field: raw.field, backend, + no_core: raw.no_core, extensions, }) } fn request(&self) -> TokenStream { - let Self { - id, backend, field, .. - } = self; - let Backend { - field: delegated_field, - index: delegated_index, - .. - } = backend; - quote! { - Self::BackendId::#id => { + let id = &self.id; + let request = if self.no_core { + quote! { + Err(::trussed::Error::RequestNotAvailable) + } + } else { + let Self { backend, field, .. } = self; + let Backend { + field: delegated_field, + index: delegated_index, + .. + } = backend; + quote! { let _ = self.#field; ::trussed::backend::Backend::request( &mut self.#delegated_field, &mut ctx.core, &mut ctx.backends.#delegated_index, request, resources, ) } + }; + quote! { + Self::BackendId::#id => { + #request + } } } @@ -367,6 +395,7 @@ impl DelegatedBackend { extensions, backend, field, + .. } = self; let extension_requests = extensions.iter().map(|e| e.extension_request(backend)); quote! {