macros/
tree.rs

1use quote::quote;
2use syn::{Data, DeriveInput, Error, Fields, Path, spanned::Spanned};
3
4pub fn derive_tagged_links(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
5    let fields = match &input.data {
6        Data::Struct(ds) => match &ds.fields {
7            Fields::Named(named) => &named.named,
8            _ => {
9                return Err(Error::new(
10                    ds.fields.span(),
11                    "TaggedLinks only supports structs with named fields",
12                ));
13            }
14        },
15        _ => {
16            return Err(Error::new(
17                input.span(),
18                "TaggedLinks can only be derived for structs",
19            ));
20        }
21    };
22
23    let rbtree_impls = impl_rbtree(input, fields)?;
24    let list_impls = impl_list(input, fields)?;
25
26    Ok(quote! {
27        #rbtree_impls
28        #list_impls
29    })
30}
31
32fn impl_rbtree(
33    input: &DeriveInput,
34    fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
35) -> syn::Result<proc_macro2::TokenStream> {
36    let struct_ident = &input.ident;
37    let generics = &input.generics;
38
39    let mut impls = Vec::new();
40
41    for field in fields {
42        let Some(field_ident) = field.ident.clone() else {
43            continue;
44        };
45
46        if let (Some(tag_path), Some(idx_path)) = find_tagged(&field.attrs, "rbtree")? {
47            let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
48
49            let impl_block = quote! {
50                impl #impl_generics crate::types::rbtree::Linkable<#tag_path, #idx_path> for #struct_ident #ty_generics #where_clause {
51                    #[inline]
52                    fn links(&self) -> &crate::types::rbtree::Links<#tag_path, #idx_path> {
53                        &self.#field_ident
54                    }
55                    #[inline]
56                    fn links_mut(&mut self) -> &mut crate::types::rbtree::Links<#tag_path, #idx_path> {
57                        &mut self.#field_ident
58                    }
59                }
60            };
61
62            impls.push(impl_block);
63        }
64    }
65
66    Ok(quote! { #(#impls)* })
67}
68
69fn impl_list(
70    input: &DeriveInput,
71    fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
72) -> syn::Result<proc_macro2::TokenStream> {
73    let struct_ident = &input.ident;
74    let generics = &input.generics;
75
76    let mut impls = Vec::new();
77
78    for field in fields {
79        let Some(field_ident) = field.ident.clone() else {
80            continue;
81        };
82
83        if let (Some(tag_path), Some(idx_path)) = find_tagged(&field.attrs, "list")? {
84            let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
85
86            let impl_block = quote! {
87                impl #impl_generics crate::types::list::Linkable<#tag_path, #idx_path> for #struct_ident #ty_generics #where_clause {
88                    #[inline]
89                    fn links(&self) -> &crate::types::list::Links<#tag_path, #idx_path> {
90                        &self.#field_ident
91                    }
92                    #[inline]
93                    fn links_mut(&mut self) -> &mut crate::types::list::Links<#tag_path, #idx_path> {
94                        &mut self.#field_ident
95                    }
96                }
97            };
98
99            impls.push(impl_block);
100        }
101    }
102
103    Ok(quote! { #(#impls)* })
104}
105
106fn find_tagged(
107    attrs: &[syn::Attribute],
108    attr_name: &str,
109) -> syn::Result<(Option<Path>, Option<Path>)> {
110    for attr in attrs {
111        if !attr.path().is_ident(attr_name) {
112            continue;
113        }
114
115        let mut tag: Option<Path> = None;
116        let mut idx: Option<Path> = None;
117
118        attr.parse_nested_meta(|meta| {
119            if meta.path.is_ident("tag") {
120                let value = meta.value()?; // expects '='
121                let p: Path = value.parse()?;
122                tag = Some(p);
123                Ok(())
124            } else if meta.path.is_ident("idx") {
125                let value = meta.value()?; // expects '='
126                let p: Path = value.parse()?;
127                idx = Some(p);
128                Ok(())
129            } else {
130                Err(meta.error("expected `tag = SomePath` or `idx = SomePath`"))
131            }
132        })?;
133
134        return Ok((tag, idx));
135    }
136    Ok((None, None))
137}