diff --git a/crates/rune-macros/src/any.rs b/crates/rune-macros/src/any.rs index 4f2bb86d5..2a66ea117 100644 --- a/crates/rune-macros/src/any.rs +++ b/crates/rune-macros/src/any.rs @@ -79,7 +79,9 @@ impl Derive { let generics = &self.input.generics; let mut installers = Vec::new(); - let Ok(()) = expand_install_with(&cx, &self.input, &tokens, &attr, generics, &mut installers) else { + let Ok(()) = + expand_install_with(&cx, &self.input, &tokens, &attr, generics, &mut installers) + else { return Err(cx.errors.into_inner()); }; @@ -152,6 +154,8 @@ pub(crate) fn expand_install_with( } } + installers.extend(attr.protocols.iter().map(|protocol| protocol.expand())); + if let Some(install_with) = &attr.install_with { installers.push(quote_spanned! { input.span() => #install_with(module)?; diff --git a/crates/rune-macros/src/context.rs b/crates/rune-macros/src/context.rs index b41dc8733..1aba535f6 100644 --- a/crates/rune-macros/src/context.rs +++ b/crates/rune-macros/src/context.rs @@ -3,8 +3,8 @@ use std::cell::RefCell; use crate::internals::*; use proc_macro2::Span; use proc_macro2::TokenStream; -use quote::quote_spanned; -use quote::{quote, ToTokens}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::parse::Parse; use syn::parse::ParseStream; use syn::punctuated::Punctuated; use syn::spanned::Spanned as _; @@ -72,6 +72,8 @@ pub(crate) struct TypeAttr { pub(crate) parse: ParseKind, /// `#[rune(item = )]`. pub(crate) item: Option, + /// Protocols to "derive" + pub(crate) protocols: Vec, /// Parsed documentation. pub(crate) docs: Vec, } @@ -111,6 +113,60 @@ pub(crate) struct FieldProtocol { custom: Option, } +pub(crate) struct TypeProtocol { + protocol: syn::Ident, + handler: Option, +} + +impl TypeProtocol { + pub fn expand(&self) -> TokenStream { + if let Some(handler) = &self.handler { + let protocol = &self.protocol; + return quote_spanned! {protocol.span()=> + module.associated_function(rune::runtime::Protocol::#protocol, #handler)?; + }; + } + match self.protocol.to_string().as_str() { + "ADD" => quote_spanned! {self.protocol.span()=> + module.associated_function(rune::runtime::Protocol::ADD, |this: Self, other: Self| this + other)?; + }, + "STRING_DISPLAY" => quote_spanned! {self.protocol.span()=> + module.associated_function(rune::runtime::Protocol::STRING_DISPLAY, |this: &Self, buf: &mut String| { + use ::std::fmt::Write as _; + ::std::write!(buf, "{this}") + })?; + }, + "STRING_DEBUG" => quote_spanned! {self.protocol.span()=> + module.associated_function(rune::runtime::Protocol::STRING_DISPLAY, |this: &Self, buf: &mut String| { + use ::std::fmt::Write as _; + ::std::write!(buf, "{this:?}") + })?; + }, + _ => syn::Error::new_spanned( + &self.protocol, + format!( + "`{}` is not a protocol supported for automatic generation on a type", + self.protocol + ), + ) + .to_compile_error(), + } + } +} + +impl Parse for TypeProtocol { + fn parse(input: ParseStream) -> syn::Result { + Ok(Self { + protocol: input.parse()?, + handler: if input.parse::().is_ok() { + Some(input.parse()?) + } else { + None + }, + }) + } +} + #[derive(Default)] pub(crate) struct Context { pub(crate) errors: RefCell>, @@ -145,7 +201,7 @@ impl Context { self.errors.borrow_mut().push(error) } - /// Get a field identifier. + /// Get a field identifier.FunctionMetaKind pub(crate) fn field_ident<'a>(&self, field: &'a syn::Field) -> Result<&'a syn::Ident, ()> { let Some(ident) = &field.ident else { self.error(syn::Error::new_spanned( @@ -441,6 +497,12 @@ impl Context { // Parse `#[rune(install_with = )]` meta.input.parse::()?; attr.install_with = Some(parse_path_compat(meta.input)?); + } else if meta.path == PROTOCOLS { + // Parse `#[rune(protocols(,*))]` + let protocols; + syn::parenthesized!(protocols in meta.input); + attr.protocols + .extend(protocols.parse_terminated(TypeProtocol::parse, Token![,])?); } else { return Err(syn::Error::new_spanned( &meta.path, diff --git a/crates/rune-macros/src/internals.rs b/crates/rune-macros/src/internals.rs index 96575846a..7d87142c7 100644 --- a/crates/rune-macros/src/internals.rs +++ b/crates/rune-macros/src/internals.rs @@ -19,6 +19,7 @@ pub const NAME: Symbol = Symbol("name"); pub const ITEM: Symbol = Symbol("item"); pub const MODULE: Symbol = Symbol("module"); pub const INSTALL_WITH: Symbol = Symbol("install_with"); +pub const PROTOCOLS: Symbol = Symbol("protocols"); pub const CONSTRUCTOR: Symbol = Symbol("constructor"); pub const GET: Symbol = Symbol("get");