Skip to content

Commit 5cf35bc

Browse files
committed
allow to derive protocols with Any
fixes rune-rs#583
1 parent 2f0e7af commit 5cf35bc

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
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

+64-2
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 _;
@@ -74,6 +74,8 @@ pub(crate) struct TypeAttr {
7474
pub(crate) item: Option<syn::Path>,
7575
/// `#[rune(constructor)]`.
7676
pub(crate) constructor: bool,
77+
/// Protocols to "derive"
78+
pub(crate) protocols: Vec<TypeProtocol>,
7779
/// Parsed documentation.
7880
pub(crate) docs: Vec<syn::Expr>,
7981
}
@@ -113,6 +115,60 @@ pub(crate) struct FieldProtocol {
113115
custom: Option<syn::Path>,
114116
}
115117

118+
pub(crate) struct TypeProtocol {
119+
protocol: syn::Ident,
120+
handler: Option<syn::Path>,
121+
}
122+
123+
impl TypeProtocol {
124+
pub fn expand(&self) -> TokenStream {
125+
if let Some(handler) = &self.handler {
126+
let protocol = &self.protocol;
127+
return quote_spanned! {protocol.span()=>
128+
module.associated_function(rune::runtime::Protocol::#protocol, #handler)?;
129+
};
130+
}
131+
match self.protocol.to_string().as_str() {
132+
"ADD" => quote_spanned! {self.protocol.span()=>
133+
module.associated_function(rune::runtime::Protocol::ADD, |this: Self, other: Self| this + other)?;
134+
},
135+
"STRING_DISPLAY" => quote_spanned! {self.protocol.span()=>
136+
module.associated_function(rune::runtime::Protocol::STRING_DISPLAY, |this: &Self, buf: &mut String| {
137+
use ::std::fmt::Write as _;
138+
::std::write!(buf, "{this}")
139+
})?;
140+
},
141+
"STRING_DEBUG" => quote_spanned! {self.protocol.span()=>
142+
module.associated_function(rune::runtime::Protocol::STRING_DEBUG, |this: &Self, buf: &mut String| {
143+
use ::std::fmt::Write as _;
144+
::std::write!(buf, "{this:?}")
145+
})?;
146+
},
147+
_ => syn::Error::new_spanned(
148+
&self.protocol,
149+
format!(
150+
"`{}` is not a protocol supported for automatic generation on a type",
151+
self.protocol
152+
),
153+
)
154+
.to_compile_error(),
155+
}
156+
}
157+
}
158+
159+
impl Parse for TypeProtocol {
160+
fn parse(input: ParseStream) -> syn::Result<Self> {
161+
Ok(Self {
162+
protocol: input.parse()?,
163+
handler: if input.parse::<Token![=]>().is_ok() {
164+
Some(input.parse()?)
165+
} else {
166+
None
167+
},
168+
})
169+
}
170+
}
171+
116172
#[derive(Default)]
117173
pub(crate) struct Context {
118174
pub(crate) errors: RefCell<Vec<syn::Error>>,
@@ -445,6 +501,12 @@ impl Context {
445501
attr.install_with = Some(parse_path_compat(meta.input)?);
446502
} else if meta.path == CONSTRUCTOR {
447503
attr.constructor = true;
504+
} else if meta.path == PROTOCOLS {
505+
// Parse `#[rune(protocols(<protocol>,*))]`
506+
let protocols;
507+
syn::parenthesized!(protocols in meta.input);
508+
attr.protocols
509+
.extend(protocols.parse_terminated(TypeProtocol::parse, Token![,])?);
448510
} else {
449511
return Err(syn::Error::new_spanned(
450512
&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)