Skip to content

Commit

Permalink
allow to derive protocols with Any
Browse files Browse the repository at this point in the history
  • Loading branch information
ModProg committed Jul 25, 2023
1 parent ad08e89 commit 4992576
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 4 deletions.
6 changes: 5 additions & 1 deletion crates/rune-macros/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
};

Expand Down Expand Up @@ -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)?;
Expand Down
68 changes: 65 additions & 3 deletions crates/rune-macros/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;
Expand Down Expand Up @@ -72,6 +72,8 @@ pub(crate) struct TypeAttr {
pub(crate) parse: ParseKind,
/// `#[rune(item = <path>)]`.
pub(crate) item: Option<syn::Path>,
/// Protocols to "derive"
pub(crate) protocols: Vec<TypeProtocol>,
/// Parsed documentation.
pub(crate) docs: Vec<syn::Expr>,
}
Expand Down Expand Up @@ -111,6 +113,60 @@ pub(crate) struct FieldProtocol {
custom: Option<syn::Path>,
}

pub(crate) struct TypeProtocol {
protocol: syn::Ident,
handler: Option<syn::Path>,
}

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<Self> {
Ok(Self {
protocol: input.parse()?,
handler: if input.parse::<Token![=]>().is_ok() {
Some(input.parse()?)
} else {
None
},
})
}
}

#[derive(Default)]
pub(crate) struct Context {
pub(crate) errors: RefCell<Vec<syn::Error>>,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -441,6 +497,12 @@ impl Context {
// Parse `#[rune(install_with = <path>)]`
meta.input.parse::<Token![=]>()?;
attr.install_with = Some(parse_path_compat(meta.input)?);
} else if meta.path == PROTOCOLS {
// Parse `#[rune(protocols(<protocol>,*))]`
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,
Expand Down
1 change: 1 addition & 0 deletions crates/rune-macros/src/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit 4992576

Please sign in to comment.