oors-macros 0.9.0

Macros for oors -- See https://crates.io/crates/oors
Documentation
/*
 * Copyright (c) 2025 eligamii.
 * Licenced under the MIT licence. See the LICENCE file in the project for full licence information
 */

#![deny(warnings)]


mod object;
mod object_impl;

use proc_macro::{TokenStream};
use quote::quote;
use crate::object::impl_object;
use crate::object_impl::impl_object_impl;

/// Add inheritance capabilities to your struct.
///
/// # Syntax
/// ```ignore
/// #[oors::object]
/// struct T { /* ... */ }
///
/// #[oors::object(parent = T)]
/// struct T2 { /* ... */ } // First field will be _base: T
/// ```
/// ---
///
/// # How it works
/// This macro will generate and implement various traits + a macro to allow structs to be
/// safely converted to other compatible structs:
/// - `IsA</* immediate parent */> for T`: Allows at compile time for `T` to have access to parent
///     structs' methods and fields, and to be safely converted to parent structs
/// - `__*Accessors`: Methods to have direct and easy access to field of `T` and all its parents
/// - `__*Builders`: Methods to easily initialize the struct using the builder pattern
/// - `__oors_recursive_impl_*!(/* type */)`: Generated macro to implement `IsA</* all parents of T */>` to `T` and its children
/// 
/// - `Object`: Allows at runtime to know what is the actual type + the parents of `T` when inside a `Typed<T>`
///
/// **Notes:**<br/>
/// - This macro will also cause the object to have a C layout (`#[repr(C)]`) to ensure predictability
/// - (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
/// modules containing parent of `C` using the wildcard syntax:
/// ```ignore
/// // prefer `pub use` so other modules doesn't have to `use` `mod1` or `parent_of_c`
/// // to make a child struct of `D`
/// pub use mod1::*;
/// pub use crate_b::parent_of_c::*;
///
/// // Will use __oors_recursive_impl_C!(...) which
/// // will use __oors_recursive_impl_<parent of C>!()
/// // etc.
/// #[oors::object(parent = C)]
/// struct D;
/// ```
/// (if `nightly` is enabled, the macro will generate `__oors_recursive_impl_*!()` as hygienic `pub macro`
/// removing the need of importing anything other than the `__oors_recursive_impl_</* immediate parent */>` itself)
#[proc_macro_attribute]
pub fn object(attr: TokenStream, item: TokenStream) -> TokenStream {
    // Impl the IsA</* parent type */> trait
    // Adds a field _parent_name_snake_case: ParentType
    // Makes the struct #[repr(C)]
    // Generate a __StructAccessors that will give access to all fields with methods (StructImpl::name(), ::name_mut())
    // The __StructAccessors is impl for all IsA<Struct> and uses the Struct's fields offset + pointer magic to access
    // values
    // Generate a __StructBuilder to allow for the struct to be initialized
    impl_object(attr, item)
}


/// Generate implementation for both a struct itself and any child type of the struct.
///
/// # Syntax
/// ```ignore
/// struct T;
/// #[oors::object_impl] impl T { /* ... */ }
/// #[oors::object_impl(pub)] impl T { /* ... */ }
/// #[oors::object_impl(/* any visibility modifier */)] impl T { /* ... */ }
/// ```
///
/// ---
///
/// # How it works
/// It simply automatically puts every instance methods into a generated `<visibility modifier> trait __TImpl where Self: IsA<T>` trait.

#[proc_macro_attribute]/// The visibility modifier is for the 
pub fn object_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
    // Change the signature from
    // "impl Class { /* items */ }"
    // to
    // "pub trait ClassImpl: IsA<Class> { /* funcs using self */ } impl<T: IsA<Class>> ClassImpl for T"
    // and
    // "impl Class { /* any other items */ }"
    impl_object_impl(attr, item)
}



/// Abstracts one `use <root of crate of object>::__oors__recursive_impl_<object name>` statement.
/// You can also use the `object_use!` macro to achieve the same result on multiple `use` statements
///
/// Usage
/// ```ignore
/// #[obj_use] use path::to::Object; // <- Must use the path::to:Struct pattern
/// #[obj_use] pub use path::to::other::Object;
/// // #[obj_use] pub use path::to::{Object} won't compile
/// ```
#[proc_macro_attribute]
pub fn obj_use(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let line = item.to_string();
    let item = proc_macro2::TokenStream::from(item);
    let mut res = quote::quote! { #item };

    let mut macro_use = line.split("::").collect::<Vec<_>>();
    let last = macro_use.last_mut().unwrap();
    let binding = format!("__oors_recursive_impl_{}", last.trim());

    if cfg!(feature = "nightly") {
        // Import the macro from the module of the object
        *last = binding.as_str();
        let macro_use_tokens = macro_use.join("::").parse::<proc_macro2::TokenStream>().unwrap();
        res = quote! { #res #macro_use_tokens };
    } else {
        // Import the macro from the root of the path of the object
        let mut split = macro_use.first().unwrap() // "use something"
            .split_whitespace();

        let use_tokens = split.next().unwrap()
            .parse::<proc_macro2::TokenStream>()
            .unwrap(); // "something";

        let root = split.last().unwrap()
            .parse::<proc_macro2::TokenStream>()
            .unwrap();

        let macro_name = binding.parse::<proc_macro2::TokenStream>().unwrap();

        // Yes, rust will depreciate `use crate::macro` (with simply use `use macro` instead)
        if root.to_string() == "crate" {
            res = quote! { #res #use_tokens #macro_name };
        } else {
            res = quote! { #res #use_tokens #root::#macro_name };
        }
    }

    res.into()
}