Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow deriving protocols with Any #587

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 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,13 @@ pub(crate) fn expand_install_with(
}
}

installers.extend(attr.protocols.iter().map(|protocol| protocol.expand()));
installers.extend(attr.functions.iter().map(|function| {
quote_spanned! {function.span()=>
module.function_meta(#function)?;
}
}));

if let Some(install_with) = &attr.install_with {
installers.push(quote_spanned! { input.span() =>
#install_with(module)?;
Expand Down
191 changes: 136 additions & 55 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 @@ -74,6 +74,10 @@ pub(crate) struct TypeAttr {
pub(crate) item: Option<syn::Path>,
/// `#[rune(constructor)]`.
pub(crate) constructor: bool,
/// Protocols to "derive"
pub(crate) protocols: Vec<TypeProtocol>,
/// Assosiated functions
pub(crate) functions: Vec<syn::Path>,
/// Parsed documentation.
pub(crate) docs: Vec<syn::Expr>,
}
Expand Down Expand Up @@ -113,6 +117,57 @@ 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()=>
ModProg marked this conversation as resolved.
Show resolved Hide resolved
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 ::core::fmt::Write as _;
::core::write!(buf, "{this}")
})?;
},
"STRING_DEBUG" => quote_spanned! {self.protocol.span()=>
module.associated_function(rune::runtime::Protocol::STRING_DEBUG, |this: &Self, buf: &mut String| {
use ::core::fmt::Write as _;
::core::write!(buf, "{this:?}")
})?;
},
_ => syn::Error::new_spanned(
&self.protocol,
format!("Rune protocol `{}` cannot be derived", self.protocol),
)
.to_compile_error(),
ModProg marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

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 @@ -397,7 +452,7 @@ impl Context {
let mut error = false;
let mut attr = TypeAttr::default();

for a in input {
'attrs: for a in input {
if a.path().is_ident("doc") {
if let syn::Meta::NameValue(meta) = &a.meta {
attr.docs.push(meta.value.clone());
Expand All @@ -406,60 +461,86 @@ impl Context {
continue;
}

if a.path() == RUNE {
let result = a.parse_nested_meta(|meta| {
if meta.path == PARSE {
// Parse `#[rune(parse = "..")]`
meta.input.parse::<Token![=]>()?;
let s: syn::LitStr = meta.input.parse()?;

match s.value().as_str() {
"meta_only" => {
attr.parse = ParseKind::MetaOnly;
}
other => {
return Err(syn::Error::new(
meta.input.span(),
format!(
"Unsupported `#[rune(parse = ..)]` argument `{}`",
other
),
));
}
};
} else if meta.path == ITEM {
// Parse `#[rune(item = "..")]`
meta.input.parse::<Token![=]>()?;
attr.item = Some(meta.input.parse()?);
} else if meta.path == NAME {
// Parse `#[rune(name = "..")]`
meta.input.parse::<Token![=]>()?;
attr.name = Some(meta.input.parse()?);
} else if meta.path == MODULE {
// Parse `#[rune(module = <path>)]`
meta.input.parse::<Token![=]>()?;
attr.module = Some(parse_path_compat(meta.input)?);
} else if meta.path == INSTALL_WITH {
// Parse `#[rune(install_with = <path>)]`
meta.input.parse::<Token![=]>()?;
attr.install_with = Some(parse_path_compat(meta.input)?);
} else if meta.path == CONSTRUCTOR {
attr.constructor = true;
} else {
return Err(syn::Error::new_spanned(
&meta.path,
"Unsupported type attribute",
));
}
let err = 'error: {
if a.path() == RUNE {
let result = a.parse_nested_meta(|meta| {
if meta.path == PARSE {
// Parse `#[rune(parse = "..")]`
meta.input.parse::<Token![=]>()?;
let s: syn::LitStr = meta.input.parse()?;

match s.value().as_str() {
"meta_only" => {
attr.parse = ParseKind::MetaOnly;
}
other => {
return Err(syn::Error::new(
meta.input.span(),
format!(
"Unsupported `#[rune(parse = ..)]` argument `{}`",
other
),
));
}
};
} else if meta.path == ITEM {
// Parse `#[rune(item = "..")]`
meta.input.parse::<Token![=]>()?;
attr.item = Some(meta.input.parse()?);
} else if meta.path == NAME {
// Parse `#[rune(name = "..")]`
meta.input.parse::<Token![=]>()?;
attr.name = Some(meta.input.parse()?);
} else if meta.path == MODULE {
// Parse `#[rune(module = <path>)]`
meta.input.parse::<Token![=]>()?;
attr.module = Some(parse_path_compat(meta.input)?);
} else if meta.path == INSTALL_WITH {
// Parse `#[rune(install_with = <path>)]`
meta.input.parse::<Token![=]>()?;
attr.install_with = Some(parse_path_compat(meta.input)?);
} else if meta.path == CONSTRUCTOR {
attr.constructor = true;
} else {
return Err(syn::Error::new_spanned(
&meta.path,
"Unsupported type attribute",
));
}

Ok(())
});
Ok(())
});

if let Err(e) = result {
error = true;
self.error(e);
};
}
if let Err(e) = result {
break 'error e;
};
}

if a.path() == RUNE_DERIVE {
attr.protocols.extend(
match a.parse_args_with(Punctuated::<_, Token![,]>::parse_terminated) {
Ok(it) => it,
Err(err) => {
break 'error err;
}
},
);
}

if a.path() == RUNE_FUNCTIONS {
attr.functions.extend(
match a.parse_args_with(Punctuated::<_, Token![,]>::parse_terminated) {
Ok(it) => it,
Err(err) => {
break 'error err;
}
},
);
}
continue 'attrs;
};
error = true;
self.error(err);
}

if error {
Expand Down
2 changes: 2 additions & 0 deletions crates/rune-macros/src/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::fmt;
pub struct Symbol(&'static str);

pub const RUNE: Symbol = Symbol("rune");
pub const RUNE_DERIVE: Symbol = Symbol("rune_derive");
pub const RUNE_FUNCTIONS: Symbol = Symbol("rune_functions");
pub const ID: Symbol = Symbol("id");
pub const SKIP: Symbol = Symbol("skip");
pub const ITER: Symbol = Symbol("iter");
Expand Down