Skip to main content

ruby_macros/
lib.rs

1#![warn(missing_docs)]
2
3//! Procedural macros for the Rusty Ruby ecosystem
4//!
5//! This crate provides various procedural macros to simplify development
6//! within the Rusty Ruby ecosystem.
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn;
11
12/// A simple example procedural macro
13///
14/// This macro demonstrates the basic structure of a procedural macro in this crate.
15#[proc_macro]
16pub fn example_macro(_input: TokenStream) -> TokenStream {
17    // Example implementation that returns a simple expression
18    let expanded = quote! {
19        println!("Hello from ruby-macros!");
20    };
21
22    TokenStream::from(expanded)
23}
24
25/// A derive macro example
26///
27/// This macro demonstrates how to create a derive macro in this crate.
28#[proc_macro_derive(ExampleDerive)]
29pub fn example_derive(input: TokenStream) -> TokenStream {
30    // Parse the input tokens into a syntax tree
31    let ast: syn::DeriveInput = syn::parse(input).unwrap();
32
33    // Build the output, possibly using quasi-quotation
34    let generated = quote! {
35        impl ExampleDerive for #ast {
36            fn example_method(&self) {
37                println!("Example derive macro implemented!");
38            }
39        }
40    };
41
42    TokenStream::from(generated)
43}
44
45/// An attribute macro example
46///
47/// This macro demonstrates how to create an attribute macro in this crate.
48#[proc_macro_attribute]
49pub fn example_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream {
50    // For this example, we just return the item unchanged
51    // In a real macro, you would process the attribute and modify the item
52    item
53}
54
55/// A derive macro for defining Ruby classes
56///
57/// This macro generates code that defines a Ruby class with the same name as the struct,
58/// handles struct fields appropriately, and integrates with the existing Ruby API.
59#[proc_macro_derive(RubyClass)]
60pub fn ruby_class_derive(input: TokenStream) -> TokenStream {
61    // Parse the input tokens into a syntax tree
62    let ast: syn::DeriveInput = syn::parse(input).unwrap();
63
64    // Generate the Ruby class implementation
65    impl_ruby_class(&ast)
66}
67
68/// Generate Ruby class implementation for a struct
69fn impl_ruby_class(ast: &syn::DeriveInput) -> TokenStream {
70    // Extract the struct name
71    let struct_name = &ast.ident;
72    let class_name = struct_name.to_string();
73
74    // Extract struct fields
75    let fields = if let syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(ref fields), .. }) = ast.data {
76        fields
77    }
78    else {
79        panic!("RubyClass can only be derived for structs with named fields");
80    };
81
82    // Generate field names and types
83    let field_names: Vec<_> = fields
84        .named
85        .iter()
86        .map(|f| {
87            let name = &f.ident;
88            let name_str = name.as_ref().unwrap().to_string();
89            quote! { #name_str }
90        })
91        .collect();
92
93    let field_ids: Vec<_> = fields
94        .named
95        .iter()
96        .map(|f| {
97            let name = &f.ident;
98            quote! { #name }
99        })
100        .collect();
101
102    // Generate the implementation
103    let expanded = quote! {
104        impl #struct_name {
105            /// Convert the struct to a Ruby Value
106            pub fn to_ruby_value(&self) -> ::ruby_types::RubyValue {
107                use ::ruby_types::RubyValue;
108
109                let mut fields = std::collections::HashMap::new();
110                #(fields.insert(#field_names.to_string(), self.#field_ids.to_ruby_value());)*
111
112                RubyValue::Object(#class_name.to_string(), fields)
113            }
114
115            /// Define this class in the Ruby runtime
116            pub fn define_ruby_class(ruby: &mut ::ruby::Ruby) -> Result<(), Box<dyn std::error::Error>> {
117                ruby.define_class(#class_name)?;
118                Ok(())
119            }
120        }
121
122        impl ::ruby::ToRubyValue for #struct_name {
123            fn to_ruby_value(&self) -> ::ruby_types::RubyValue {
124                self.to_ruby_value()
125            }
126        }
127    };
128
129    TokenStream::from(expanded)
130}