oors_macros/lib.rs
1/*
2 * Copyright (c) 2025 eligamii.
3 * Licenced under the MIT licence. See the LICENCE file in the project for full licence information
4 */
5
6#![deny(warnings)]
7
8
9mod object;
10mod object_impl;
11
12use proc_macro::{TokenStream};
13use quote::quote;
14use crate::object::impl_object;
15use crate::object_impl::impl_object_impl;
16
17/// Add inheritance capabilities to your struct.
18///
19/// # Syntax
20/// ```ignore
21/// #[oors::object]
22/// struct T { /* ... */ }
23///
24/// #[oors::object(parent = T)]
25/// struct T2 { /* ... */ } // First field will be _base: T
26/// ```
27/// ---
28///
29/// # How it works
30/// This macro will generate and implement various traits + a macro to allow structs to be
31/// safely converted to other compatible structs:
32/// - `IsA</* immediate parent */> for T`: Allows at compile time for `T` to have access to parent
33/// structs' methods and fields, and to be safely converted to parent structs
34/// - `__*Accessors`: Methods to have direct and easy access to field of `T` and all its parents
35/// - `__*Builders`: Methods to easily initialize the struct using the builder pattern
36/// - `__oors_recursive_impl_*!(/* type */)`: Generated macro to implement `IsA</* all parents of T */>` to `T` and its children
37///
38/// - `Object`: Allows at runtime to know what is the actual type + the parents of `T` when inside a `Typed<T>`
39///
40/// **Notes:**<br/>
41/// - This macro will also cause the object to have a C layout (`#[repr(C)]`) to ensure predictability
42/// - (Unless the feature `nightly` is enabled) If you inherit from an object (`C`) from a foreign module/crate (`mod1`), this macro requires importing the module and all
43/// modules containing parent of `C` using the wildcard syntax:
44/// ```ignore
45/// // prefer `pub use` so other modules doesn't have to `use` `mod1` or `parent_of_c`
46/// // to make a child struct of `D`
47/// pub use mod1::*;
48/// pub use crate_b::parent_of_c::*;
49///
50/// // Will use __oors_recursive_impl_C!(...) which
51/// // will use __oors_recursive_impl_<parent of C>!()
52/// // etc.
53/// #[oors::object(parent = C)]
54/// struct D;
55/// ```
56/// (if `nightly` is enabled, the macro will generate `__oors_recursive_impl_*!()` as hygienic `pub macro`
57/// removing the need of importing anything other than the `__oors_recursive_impl_</* immediate parent */>` itself)
58#[proc_macro_attribute]
59pub fn object(attr: TokenStream, item: TokenStream) -> TokenStream {
60 // Impl the IsA</* parent type */> trait
61 // Adds a field _parent_name_snake_case: ParentType
62 // Makes the struct #[repr(C)]
63 // Generate a __StructAccessors that will give access to all fields with methods (StructImpl::name(), ::name_mut())
64 // The __StructAccessors is impl for all IsA<Struct> and uses the Struct's fields offset + pointer magic to access
65 // values
66 // Generate a __StructBuilder to allow for the struct to be initialized
67 impl_object(attr, item)
68}
69
70
71/// Generate implementation for both a struct itself and any child type of the struct.
72///
73/// # Syntax
74/// ```ignore
75/// struct T;
76/// #[oors::object_impl] impl T { /* ... */ }
77/// #[oors::object_impl(pub)] impl T { /* ... */ }
78/// #[oors::object_impl(/* any visibility modifier */)] impl T { /* ... */ }
79/// ```
80///
81/// ---
82///
83/// # How it works
84/// It simply automatically puts every instance methods into a generated `<visibility modifier> trait __TImpl where Self: IsA<T>` trait.
85
86#[proc_macro_attribute]/// The visibility modifier is for the
87pub fn object_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
88 // Change the signature from
89 // "impl Class { /* items */ }"
90 // to
91 // "pub trait ClassImpl: IsA<Class> { /* funcs using self */ } impl<T: IsA<Class>> ClassImpl for T"
92 // and
93 // "impl Class { /* any other items */ }"
94 impl_object_impl(attr, item)
95}
96
97
98
99/// Abstracts one `use <root of crate of object>::__oors__recursive_impl_<object name>` statement.
100/// You can also use the `object_use!` macro to achieve the same result on multiple `use` statements
101///
102/// Usage
103/// ```ignore
104/// #[obj_use] use path::to::Object; // <- Must use the path::to:Struct pattern
105/// #[obj_use] pub use path::to::other::Object;
106/// // #[obj_use] pub use path::to::{Object} won't compile
107/// ```
108#[proc_macro_attribute]
109pub fn obj_use(_attr: TokenStream, item: TokenStream) -> TokenStream {
110 let line = item.to_string();
111 let item = proc_macro2::TokenStream::from(item);
112 let mut res = quote::quote! { #item };
113
114 let mut macro_use = line.split("::").collect::<Vec<_>>();
115 let last = macro_use.last_mut().unwrap();
116 let binding = format!("__oors_recursive_impl_{}", last.trim());
117
118 if cfg!(feature = "nightly") {
119 // Import the macro from the module of the object
120 *last = binding.as_str();
121 let macro_use_tokens = macro_use.join("::").parse::<proc_macro2::TokenStream>().unwrap();
122 res = quote! { #res #macro_use_tokens };
123 } else {
124 // Import the macro from the root of the path of the object
125 let mut split = macro_use.first().unwrap() // "use something"
126 .split_whitespace();
127
128 let use_tokens = split.next().unwrap()
129 .parse::<proc_macro2::TokenStream>()
130 .unwrap(); // "something";
131
132 let root = split.last().unwrap()
133 .parse::<proc_macro2::TokenStream>()
134 .unwrap();
135
136 let macro_name = binding.parse::<proc_macro2::TokenStream>().unwrap();
137
138 // Yes, rust will depreciate `use crate::macro` (with simply use `use macro` instead)
139 if root.to_string() == "crate" {
140 res = quote! { #res #use_tokens #macro_name };
141 } else {
142 res = quote! { #res #use_tokens #root::#macro_name };
143 }
144 }
145
146 res.into()
147}