Skip to content

Commit 4992576

Browse files
committed
allow to derive protocols with Any
fixes rune-rs#583
1 parent ad08e89 commit 4992576

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

crates/rune-macros/src/any.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ impl Derive {
7979
let generics = &self.input.generics;
8080
let mut installers = Vec::new();
8181

82-
let Ok(()) = expand_install_with(&cx, &self.input, &tokens, &attr, generics, &mut installers) else {
82+
let Ok(()) =
83+
expand_install_with(&cx, &self.input, &tokens, &attr, generics, &mut installers)
84+
else {
8385
return Err(cx.errors.into_inner());
8486
};
8587

@@ -152,6 +154,8 @@ pub(crate) fn expand_install_with(
152154
}
153155
}
154156

157+
installers.extend(attr.protocols.iter().map(|protocol| protocol.expand()));
158+
155159
if let Some(install_with) = &attr.install_with {
156160
installers.push(quote_spanned! { input.span() =>
157161
#install_with(module)?;

crates/rune-macros/src/context.rs

+65-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::cell::RefCell;
33
use crate::internals::*;
44
use proc_macro2::Span;
55
use proc_macro2::TokenStream;
6-
use quote::quote_spanned;
7-
use quote::{quote, ToTokens};
6+
use quote::{quote, quote_spanned, ToTokens};
7+
use syn::parse::Parse;
88
use syn::parse::ParseStream;
99
use syn::punctuated::Punctuated;
1010
use syn::spanned::Spanned as _;
@@ -72,6 +72,8 @@ pub(crate) struct TypeAttr {
7272
pub(crate) parse: ParseKind,
7373
/// `#[rune(item = <path>)]`.
7474
pub(crate) item: Option<syn::Path>,
75+
/// Protocols to "derive"
76+
pub(crate) protocols: Vec<TypeProtocol>,
7577
/// Parsed documentation.
7678
pub(crate) docs: Vec<syn::Expr>,
7779
}
@@ -111,6 +113,60 @@ pub(crate) struct FieldProtocol {
111113
custom: Option<syn::Path>,
112114
}
113115

116+
pub(crate) struct TypeProtocol {
117+
protocol: syn::Ident,
118+
handler: Option<syn::Path>,
119+
}
120+
121+
impl TypeProtocol {
122+
pub fn expand(&self) -> TokenStream {
123+
if let Some(handler) = &self.handler {
124+
let protocol = &self.protocol;
125+
return quote_spanned! {protocol.span()=>
126+
module.associated_function(rune::runtime::Protocol::#protocol, #handler)?;
127+
};
128+
}
129+
match self.protocol.to_string().as_str() {
130+
"ADD" => quote_spanned! {self.protocol.span()=>
131+
module.associated_function(rune::runtime::Protocol::ADD, |this: Self, other: Self| this + other)?;
132+
},
133+
"STRING_DISPLAY" => quote_spanned! {self.protocol.span()=>
134+
module.associated_function(rune::runtime::Protocol::STRING_DISPLAY, |this: &Self, buf: &mut String| {
135+
use ::std::fmt::Write as _;
136+
::std::write!(buf, "{this}")
137+
})?;
138+
},
139+
"STRING_DEBUG" => quote_spanned! {self.protocol.span()=>
140+
module.associated_function(rune::runtime::Protocol::STRING_DISPLAY, |this: &Self, buf: &mut String| {
141+
use ::std::fmt::Write as _;
142+
::std::write!(buf, "{this:?}")
143+
})?;
144+
},
145+
_ => syn::Error::new_spanned(
146+
&self.protocol,
147+
format!(
148+
"`{}` is not a protocol supported for automatic generation on a type",
149+
self.protocol
150+
),
151+
)
152+
.to_compile_error(),
153+
}
154+
}
155+
}
156+
157+
impl Parse for TypeProtocol {
158+
fn parse(input: ParseStream) -> syn::Result<Self> {
159+
Ok(Self {
160+
protocol: input.parse()?,
161+
handler: if input.parse::<Token![=]>().is_ok() {
162+
Some(input.parse()?)
163+
} else {
164+
None
165+
},
166+
})
167+
}
168+
}
169+
114170
#[derive(Default)]
115171
pub(crate) struct Context {
116172
pub(crate) errors: RefCell<Vec<syn::Error>>,
@@ -145,7 +201,7 @@ impl Context {
145201
self.errors.borrow_mut().push(error)
146202
}
147203

148-
/// Get a field identifier.
204+
/// Get a field identifier.FunctionMetaKind
149205
pub(crate) fn field_ident<'a>(&self, field: &'a syn::Field) -> Result<&'a syn::Ident, ()> {
150206
let Some(ident) = &field.ident else {
151207
self.error(syn::Error::new_spanned(
@@ -441,6 +497,12 @@ impl Context {
441497
// Parse `#[rune(install_with = <path>)]`
442498
meta.input.parse::<Token![=]>()?;
443499
attr.install_with = Some(parse_path_compat(meta.input)?);
500+
} else if meta.path == PROTOCOLS {
501+
// Parse `#[rune(protocols(<protocol>,*))]`
502+
let protocols;
503+
syn::parenthesized!(protocols in meta.input);
504+
attr.protocols
505+
.extend(protocols.parse_terminated(TypeProtocol::parse, Token![,])?);
444506
} else {
445507
return Err(syn::Error::new_spanned(
446508
&meta.path,

crates/rune-macros/src/internals.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub const NAME: Symbol = Symbol("name");
1919
pub const ITEM: Symbol = Symbol("item");
2020
pub const MODULE: Symbol = Symbol("module");
2121
pub const INSTALL_WITH: Symbol = Symbol("install_with");
22+
pub const PROTOCOLS: Symbol = Symbol("protocols");
2223

2324
pub const CONSTRUCTOR: Symbol = Symbol("constructor");
2425
pub const GET: Symbol = Symbol("get");

0 commit comments

Comments
 (0)