rune_macros/
lib.rs

1//! # rune-macros
2//!
3//! `rune-macros` is the crate that generates two proc macros for use in other parts of the `rune` architecture:
4//!
5//! * [`macro@defun`]: Functions hydrated to emacs lisp.
6//! * [Trace](`macro@Trace`): Implement Trace for a struct.
7use darling::{Error, FromMeta, ast::NestedMeta};
8use proc_macro::TokenStream;
9use syn::parse_macro_input;
10
11mod defun;
12mod trace;
13mod variantly;
14
15/// ## `#[defun]`
16///
17/// Represents the functions that are going to be hydrated to emacs lisp, through the `rune` VM execution. As of
18/// today, they are not documented with the GNU Emacs documentation, though definitely is a point of improvement.
19/// Following Rust convention, `defun` names are written in `snake_case`, though if you search them in GNU Emacs,
20/// you'll find them in `kebab-case`.
21///
22/// Arguments of the `defun`s follow the arguments on its corresponding documentation, or rather, definition on
23/// the C Emacs core.
24///
25/// ### Examples
26///
27/// For the `make-vector` function, here's its signature:
28///
29/// > make-vector is a function defined in `alloc.c`. Signature `(make-vector LENGTH INIT)`
30///
31/// Its corresponding `rune` `#[defun]` signature would be:
32///
33/// ```ignore
34/// #[defun]
35/// fn make_vector(length: usize, init: GcObj) -> Vec<GcObj> {}
36/// ```
37///
38/// The return object is interesting, as it's not so easily inferrable from the signature, but rather from documentation.
39/// In this case, the `make-vector` defun returns a *newly created vector*.
40#[proc_macro_attribute]
41pub fn defun(attr_ts: TokenStream, fn_ts: TokenStream) -> TokenStream {
42    let function = parse_macro_input!(fn_ts as defun::Function);
43    match NestedMeta::parse_meta_list(attr_ts.into()) {
44        Ok(args) => match defun::Spec::from_list(&args) {
45            Ok(spec) => defun::expand(function, spec).into(),
46            Err(e) => TokenStream::from(e.write_errors()),
47        },
48        Err(e) => TokenStream::from(Error::from(e).write_errors()),
49    }
50}
51
52/// ## `Trace`
53///
54/// TODO: Document `Trace` macro.
55#[proc_macro_derive(Trace, attributes(no_trace))]
56pub fn trace_derive(stream: TokenStream) -> TokenStream {
57    let derived = parse_macro_input!(stream as syn::DeriveInput);
58    trace::expand(&derived).into()
59}
60
61/// ## `elprop`
62///
63/// Defines a function template to used for property testing functions. Each element can be a type,
64/// list of types, a regex matching a string, or the placeholder '_'.
65///
66/// ## Examples:
67///
68/// test the first argument as a integer and the second as a char
69/// ```ignore
70/// #[elprop(i64, char)]
71/// ```
72///
73/// test the second argument as a char. The first argument uses the function types like normal.
74/// ```ignore
75/// #[elprop(_, char)]
76/// ```
77///
78/// test the first argument as a string starting with 'a'
79/// ```ignore
80/// #[elprop("^a.*")]
81/// ```
82///
83/// test the first argument as a list of 2 elements of char
84/// ```ignore
85/// #[elprop((char, char))]
86/// ```
87///
88/// test the first argument as either a string or a char
89/// ```ignore
90/// #[elprop(String | char)]
91/// ```
92#[proc_macro_attribute]
93pub fn elprop(_: TokenStream, fn_ts: TokenStream) -> TokenStream {
94    // The handling of this macro is in elprop
95    fn_ts
96}
97
98/// The generated methods assume that `TargetType` provides an appropriate `untag()` method
99/// (and `untag_mut()` if mutable access methods are used) that allows access to the
100/// underlying enum (`MyEnum` in the example).
101///
102/// For methods that construct a new instance of `TargetType` (like `and_then`, `or_else`),
103/// it's assumed that the enum variant (e.g., `MyEnum::Tuple(...)`) can be converted into
104/// `TargetType` via `.into()`. This means `TargetType` must implement `From<MyEnum>`.
105#[proc_macro_attribute]
106pub fn enum_methods(attr: TokenStream, item: TokenStream) -> TokenStream {
107    match variantly::variantly_attribute_macro_impl(attr.into(), item.into()) {
108        Ok(ts) => ts.into(),
109        Err(e) => e.into_compile_error(),
110    }
111}