zone_alloc_strong_handle_derive/
lib.rs

1//! Procedural macro for deriving the [`zone_alloc::StrongHandle`] interface on simple wrappers
2//! around [`zone_alloc::Handle`].
3//!
4//! # Example
5//! ```
6//! use zone_alloc::{
7//!     Handle,
8//!     StrongRegistry,
9//! };
10//! use zone_alloc_strong_handle_derive::StrongHandle;
11//!
12//! #[derive(Clone, Copy, Debug, PartialEq, Eq, StrongHandle)]
13//! struct NodeHandle(Handle);
14//!
15//! #[derive(Debug, PartialEq, Eq)]
16//! struct Node<T> {
17//!     parent: Option<NodeHandle>,
18//!     value: T,
19//! }
20//!
21//! impl<T> Node<T> {
22//!     pub fn new(parent: Option<NodeHandle>, value: T) -> Self {
23//!         Self { parent, value }
24//!     }
25//! }
26//!
27//! fn main() {
28//!     let registry = StrongRegistry::<NodeHandle, Node<&str>>::new();
29//!     let root_handle = registry.register(Node::new(None, "first"));
30//!     let handle = registry.register(Node::new(Some(root_handle), "second"));
31//!     let handle = registry.register(Node::new(Some(handle), "third"));
32//!     registry.get_mut(root_handle).unwrap().parent = Some(handle);
33//!
34//!     let node = registry.get(handle).unwrap();
35//!     assert_eq!(node.value, "third");
36//!     let node = registry.get(node.parent.unwrap()).unwrap();
37//!     assert_eq!(node.value, "second");
38//!     let node = registry.get(node.parent.unwrap()).unwrap();
39//!     assert_eq!(node.value, "first");
40//!     let node = registry.get(node.parent.unwrap()).unwrap();
41//!     assert_eq!(node.value, "third");
42//! }
43//! ```
44
45#![no_std]
46
47extern crate proc_macro;
48
49mod parse;
50
51use parse::Input;
52use proc_macro::TokenStream;
53use proc_macro2::{
54    Ident,
55    Span,
56};
57use proc_macro_crate::{
58    crate_name,
59    FoundCrate,
60};
61use quote::quote;
62use syn::parse_macro_input;
63
64#[proc_macro_derive(StrongHandle)]
65pub fn derive_strong_handle(input: TokenStream) -> TokenStream {
66    let input = parse_macro_input!(input as Input);
67    let ident = input.ident;
68
69    let call_site = Span::call_site();
70    let found_crate = crate_name("zone-alloc").expect("zone-alloc is present in `Cargo.toml`");
71    let crate_token = match found_crate {
72        FoundCrate::Itself => quote!(crate),
73        FoundCrate::Name(name) => {
74            let ident = Ident::new(&name, call_site);
75            quote!(#ident)
76        }
77    };
78    let handle_type = quote! {
79        #crate_token::Handle
80    };
81    let strong_handle = quote! {
82        #crate_token::StrongHandle
83    };
84
85    TokenStream::from(quote! {
86        impl core::convert::From<#handle_type> for #ident {
87            fn from(value: #handle_type) -> Self {
88                Self(value.into())
89            }
90        }
91        impl #strong_handle for #ident {
92            fn handle(&self) -> #handle_type {
93                self.0
94            }
95        }
96    })
97}