Skip to content

Commit

Permalink
Introduce #[rune::module] macro
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Aug 20, 2023
1 parent af96fb9 commit 67a6504
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 33 deletions.
17 changes: 17 additions & 0 deletions crates/rune-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod inst_display;
mod instrument;
mod internals;
mod macro_;
mod module;
mod opaque;
mod option_spanned;
mod parse;
Expand Down Expand Up @@ -88,6 +89,22 @@ pub fn macro_(
output.into()
}

#[proc_macro_attribute]
pub fn module(
attrs: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let attrs = syn::parse_macro_input!(attrs with crate::module::ModuleAttrs::parse);
let module = syn::parse_macro_input!(item with crate::module::Module::parse);

let output = match module.expand(attrs) {
Ok(output) => output,
Err(e) => return proc_macro::TokenStream::from(e.to_compile_error()),
};

output.into()
}

#[proc_macro_attribute]
pub fn attribute_macro(
attrs: proc_macro::TokenStream,
Expand Down
124 changes: 124 additions & 0 deletions crates/rune-macros/src/module.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::Error;

pub(crate) struct ModuleAttrs {
path: syn::Path,
}

impl ModuleAttrs {
/// Parse the given parse stream.
pub(crate) fn parse(input: ParseStream) -> Result<Self, Error> {
let path = input.parse::<syn::Path>()?;
let stream = input.parse::<TokenStream>()?;

if !stream.is_empty() {
return Err(syn::Error::new_spanned(stream, "Unexpected input"));
}

Ok(Self { path })
}
}

pub(crate) struct Module {
attributes: Vec<syn::Attribute>,
docs: syn::ExprArray,
remainder: TokenStream,
}

impl Module {
/// Parse the given parse stream.
pub(crate) fn parse(input: ParseStream) -> Result<Self, Error> {
let parsed_attributes = input.call(syn::Attribute::parse_outer)?;

let mut docs = syn::ExprArray {
attrs: Vec::new(),
bracket_token: syn::token::Bracket::default(),
elems: Punctuated::default(),
};

let mut attributes = Vec::new();

for attr in parsed_attributes {
if attr.path().is_ident("doc") {
if let syn::Meta::NameValue(name_value) = &attr.meta {
docs.elems.push(name_value.value.clone());
}
}

attributes.push(attr);
}

let remainder = input.parse::<TokenStream>()?;

Ok(Self {
attributes,
docs,
remainder,
})
}

/// Expand the function declaration.
pub(crate) fn expand(self, attrs: ModuleAttrs) -> Result<TokenStream, Error> {
let docs = self.docs;

let item = match attrs.path.leading_colon {
Some(..) => {
let mut it = attrs.path.segments.iter();

let Some(krate) = it.next() else {
return Err(Error::new_spanned(&attrs.path, "missing leading segment"));
};

let krate = syn::LitStr::new(&krate.ident.to_string(), krate.ident.span());
let item = build_item(it);
quote!(rune::__private::ItemBuf::with_crate_item(#krate, #item))
}
None => {
let item = build_item(attrs.path.segments.iter());
quote!(rune::__private::ItemBuf::from_item(#item))
}
};

let mut stream = TokenStream::new();

stream.extend(quote! {
/// Get module metadata.
#[automatically_derived]
#[doc(hidden)]
fn module_meta() -> rune::__private::ModuleMetaData {
rune::__private::ModuleMetaData {
docs: &#docs[..],
item: #item,
}
}
});

for attribute in self.attributes {
attribute.to_tokens(&mut stream);
}

stream.extend(self.remainder);
Ok(stream)
}
}

fn build_item(it: syn::punctuated::Iter<'_, syn::PathSegment>) -> syn::ExprArray {
let mut item = syn::ExprArray {
attrs: Vec::new(),
bracket_token: syn::token::Bracket::default(),
elems: Punctuated::default(),
};

for p in it {
let p = syn::LitStr::new(&p.ident.to_string(), p.ident.span());

item.elems.push(syn::Expr::Lit(syn::ExprLit {
attrs: Vec::new(),
lit: syn::Lit::Str(p),
}))
}
item
}
5 changes: 5 additions & 0 deletions crates/rune/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ pub use rune_macros::macro_;
#[doc(hidden)]
pub use rune_macros::attribute_macro;

/// Macro used to annotate a module with metadata.
pub use rune_macros::module;

cfg_cli! {
pub mod cli;
}
Expand All @@ -441,9 +444,11 @@ cfg_doc! {
/// Privately exported details.
#[doc(hidden)]
pub mod __private {
pub use crate::compile::ItemBuf;
pub use crate::module::module::Module;
pub use crate::module::{
FunctionMetaData, FunctionMetaKind, InstallWith, MacroMetaData, MacroMetaKind,
ModuleMetaData,
};
pub use crate::params::Params;
pub use crate::runtime::TypeOf;
Expand Down
36 changes: 22 additions & 14 deletions crates/rune/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub(crate) use self::function_meta::{AssociatedFunctionName, ToFieldFunction, To
pub use self::function_meta::{FunctionMetaData, FunctionMetaKind, MacroMetaData, MacroMetaKind};
pub use self::function_traits::{Async, Function, FunctionKind, InstanceFunction, Plain};
#[doc(hidden)]
pub use self::module::Module;
pub use self::module::{Module, ModuleMeta, ModuleMetaData};

/// Trait to handle the installation of auxilliary functions for a type
/// installed into a module.
Expand Down Expand Up @@ -259,6 +259,14 @@ impl ItemMut<'_> {
self.docs.set_docs(docs);
self
}

/// Set static documentation.
///
/// This completely replaces any existing documentation.
pub fn static_docs(self, docs: &'static [&'static str]) -> Self {
self.docs.set_docs(docs);
self
}
}

impl fmt::Debug for ItemMut<'_> {
Expand Down Expand Up @@ -382,18 +390,18 @@ impl fmt::Debug for ItemFnMut<'_> {
/// its metadata.
pub struct VariantMut<'a, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
pub(crate) index: usize,
pub(crate) docs: &'a mut Docs,
pub(crate) fields: &'a mut Option<Fields>,
pub(crate) constructor: &'a mut Option<Arc<FunctionHandler>>,
pub(crate) _marker: PhantomData<&'a mut T>,
pub(crate) _marker: PhantomData<T>,
}

impl<T> VariantMut<'_, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
/// Set documentation for an inserted type.
///
Expand Down Expand Up @@ -466,16 +474,16 @@ where
/// Access enum metadata mutably.
pub struct EnumMut<'a, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
docs: &'a mut Docs,
enum_: &'a mut Enum,
_marker: PhantomData<&'a mut T>,
_marker: PhantomData<T>,
}

impl<T> EnumMut<'_, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
/// Set documentation for an inserted type.
///
Expand Down Expand Up @@ -519,15 +527,15 @@ where
/// Access internal enum metadata mutably.
pub struct InternalEnumMut<'a, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
enum_: &'a mut InternalEnum,
_marker: PhantomData<&'a mut T>,
_marker: PhantomData<T>,
}

impl<T> InternalEnumMut<'_, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
/// Set documentation for an inserted internal enum.
///
Expand Down Expand Up @@ -577,18 +585,18 @@ where
/// type.
pub struct TypeMut<'a, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
docs: &'a mut Docs,
spec: &'a mut Option<TypeSpecification>,
constructor: &'a mut Option<Arc<FunctionHandler>>,
item: &'a Item,
_marker: PhantomData<&'a mut T>,
_marker: PhantomData<T>,
}

impl<'a, T> TypeMut<'a, T>
impl<T> TypeMut<'_, T>
where
T: TypeOf,
T: ?Sized + TypeOf,
{
/// Set documentation for an inserted type.
///
Expand Down
28 changes: 27 additions & 1 deletion crates/rune/src/module/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ use crate::runtime::{
};
use crate::Hash;

#[doc(hidden)]
pub struct ModuleMetaData {
#[doc(hidden)]
pub item: ItemBuf,
#[doc(hidden)]
pub docs: &'static [&'static str],
}

/// Type used to collect and store module metadata through the `#[rune::module]`
/// macro.
///
/// This is the argument type for [`Module::from_meta`], and is from a public
/// API perspective completely opaque and might change for any release.
///
/// Calling and making use of `ModuleMeta` manually despite this warning might
/// lead to future breakage.
pub type ModuleMeta = fn() -> ModuleMetaData;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Name {
/// An associated key.
Expand Down Expand Up @@ -104,6 +122,14 @@ impl Module {
Self::inner_new(ItemBuf::with_crate_item(name, iter))
}

/// Construct a new module from the given module meta.
pub fn from_meta(module_meta: ModuleMeta) -> Self {
let meta = module_meta();
let mut m = Self::inner_new(meta.item);
m.item_mut().static_docs(meta.docs);
m
}

fn inner_new(item: ItemBuf) -> Self {
Self {
names: HashSet::new(),
Expand Down Expand Up @@ -170,7 +196,7 @@ impl Module {
/// ```
pub fn ty<T>(&mut self) -> Result<TypeMut<'_, T>, ContextError>
where
T: Named + TypeOf + InstallWith,
T: ?Sized + Named + TypeOf + InstallWith,
{
let item = ItemBuf::with_item([T::BASE_NAME]);
let hash = T::type_hash();
Expand Down
14 changes: 9 additions & 5 deletions crates/rune/src/modules/any.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `std::any` module.
//! The `std::any` rune module.
use core::fmt::{self, Write};

Expand All @@ -8,11 +8,15 @@ use crate as rune;
use crate::runtime::{Type, Value, VmResult};
use crate::{ContextError, Module};

/// Construct the `std::any` module.
/// Utilities for dynamic typing or type reflection.
///
/// # `Type`
///
/// Values of this type indicates the type of any dynamic value and can be
/// constructed through the [`Type::of_val`] function.
#[rune::module(::std::any)]
pub fn module() -> Result<Module, ContextError> {
let mut m = Module::with_crate_item("std", ["any"]);

m.item_mut().docs(["The `std::any` module."]);
let mut m = Module::from_meta(self::module_meta);

m.ty::<Type>()?
.docs(["Represents a type in the Rune type system."]);
Expand Down
14 changes: 7 additions & 7 deletions crates/rune/src/modules/tuple.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! The `std::tuple` module.
use crate as rune;
use crate::runtime::{OwnedTuple, Tuple, Value, Vec, VmResult};
use crate::runtime::{Tuple, Value, Vec, VmResult};
use crate::{ContextError, Module};

/// Install the core package into the given functions namespace.
/// Dynamic tuples.
#[rune::module(::std::tuple)]
pub fn module() -> Result<Module, ContextError> {
let mut m = Module::with_crate_item("std", ["tuple"]);
m.item_mut().docs(["The `std::tuple` module."]);
m.ty::<OwnedTuple>()?.docs(["The tuple type."]);
let mut m = Module::from_meta(self::module_meta);
m.ty::<Tuple>()?.docs(["The tuple type."]);
m.function_meta(len)?;
m.function_meta(is_empty)?;
m.function_meta(get)?;
Expand All @@ -19,7 +19,7 @@ pub fn module() -> Result<Module, ContextError> {
///
/// # Examples
///
/// ```
/// ```rune
/// let a = (1, 2, 3);
/// assert_eq!(a.len(), 3);
/// ```
Expand All @@ -32,7 +32,7 @@ fn len(this: &Tuple) -> usize {
///
/// # Examples
///
/// ```
/// ```rune
/// let a = (1, 2, 3);
/// assert!(!a.is_empty());
///
Expand Down
Loading

0 comments on commit 67a6504

Please sign in to comment.