facet_macros_impl/
derive.rs

1use crate::{ToTokens, *};
2use quote::{TokenStreamExt as _, quote};
3
4use crate::{LifetimeName, RenameRule, process_enum, process_struct};
5
6/// Generate a static declaration that pre-evaluates `<T as Facet>::SHAPE`.
7/// Only emitted in release builds to avoid slowing down debug compile times.
8/// Skipped for generic types since we can't create a static for an unmonomorphized type.
9pub(crate) fn generate_static_decl(
10    type_name: &Ident,
11    facet_crate: &TokenStream,
12    has_type_or_const_generics: bool,
13) -> TokenStream {
14    // Can't generate a static for generic types - the type parameters aren't concrete
15    if has_type_or_const_generics {
16        return quote! {};
17    }
18
19    let type_name_str = type_name.to_string();
20    let screaming_snake_name = RenameRule::ScreamingSnakeCase.apply(&type_name_str);
21
22    let static_name_ident = quote::format_ident!("{}_SHAPE", screaming_snake_name);
23
24    quote! {
25        #[cfg(not(debug_assertions))]
26        static #static_name_ident: &'static #facet_crate::Shape = <#type_name as #facet_crate::Facet>::SHAPE;
27    }
28}
29
30/// Main entry point for the `#[derive(Facet)]` macro. Parses type declarations and generates Facet trait implementations.
31pub fn facet_macros(input: TokenStream) -> TokenStream {
32    let mut i = input.clone().to_token_iter();
33
34    // Parse as TypeDecl
35    match i.parse::<Cons<AdtDecl, EndOfStream>>() {
36        Ok(it) => match it.first {
37            AdtDecl::Struct(parsed) => process_struct::process_struct(parsed),
38            AdtDecl::Enum(parsed) => process_enum::process_enum(parsed),
39        },
40        Err(err) => {
41            panic!("Could not parse type declaration: {input}\nError: {err}");
42        }
43    }
44}
45
46pub(crate) fn build_where_clauses(
47    where_clauses: Option<&WhereClauses>,
48    generics: Option<&GenericParams>,
49    opaque: bool,
50    facet_crate: &TokenStream,
51) -> TokenStream {
52    let mut where_clause_tokens = TokenStream::new();
53    let mut has_clauses = false;
54
55    if let Some(wc) = where_clauses {
56        for c in wc.clauses.iter() {
57            if has_clauses {
58                where_clause_tokens.extend(quote! { , });
59            }
60            where_clause_tokens.extend(c.value.to_token_stream());
61            has_clauses = true;
62        }
63    }
64
65    if let Some(generics) = generics {
66        for p in generics.params.iter() {
67            match &p.value {
68                GenericParam::Lifetime { name, .. } => {
69                    let facet_lifetime = LifetimeName(quote::format_ident!("{}", "ʄ"));
70                    let lifetime = LifetimeName(name.name.clone());
71                    if has_clauses {
72                        where_clause_tokens.extend(quote! { , });
73                    }
74                    where_clause_tokens
75                        .extend(quote! { #lifetime: #facet_lifetime, #facet_lifetime: #lifetime });
76
77                    has_clauses = true;
78                }
79                GenericParam::Const { .. } => {
80                    // ignore for now
81                }
82                GenericParam::Type { name, .. } => {
83                    if has_clauses {
84                        where_clause_tokens.extend(quote! { , });
85                    }
86                    // Only specify lifetime bound for opaque containers
87                    if opaque {
88                        where_clause_tokens.extend(quote! { #name: });
89                    } else {
90                        where_clause_tokens.extend(quote! { #name: #facet_crate::Facet<> });
91                    }
92                    has_clauses = true;
93                }
94            }
95        }
96    }
97
98    if !has_clauses {
99        quote! {}
100    } else {
101        quote! { where #where_clause_tokens }
102    }
103}
104
105/// Build the `.type_params(...)` builder call, returning empty if no type params.
106pub(crate) fn build_type_params_call(
107    generics: Option<&GenericParams>,
108    opaque: bool,
109    facet_crate: &TokenStream,
110) -> TokenStream {
111    if opaque {
112        return quote! {};
113    }
114
115    let mut type_params = Vec::new();
116    if let Some(generics) = generics {
117        for p in generics.params.iter() {
118            match &p.value {
119                GenericParam::Lifetime { .. } => {
120                    // ignore for now
121                }
122                GenericParam::Const { .. } => {
123                    // ignore for now
124                }
125                GenericParam::Type { name, .. } => {
126                    let name_str = name.to_string();
127                    type_params.push(quote! {
128                        #facet_crate::TypeParam {
129                            name: #name_str,
130                            shape: <#name as #facet_crate::Facet>::SHAPE
131                        }
132                    });
133                }
134            }
135        }
136    }
137
138    if type_params.is_empty() {
139        quote! {}
140    } else {
141        quote! { .type_params(&[#(#type_params),*]) }
142    }
143}
144
145/// Generate the `type_name` function for the `ValueVTable`,
146/// displaying realized generics if present.
147pub(crate) fn generate_type_name_fn(
148    type_name: &Ident,
149    generics: Option<&GenericParams>,
150    opaque: bool,
151    facet_crate: &TokenStream,
152) -> TokenStream {
153    let type_name_str = type_name.to_string();
154
155    let write_generics = (!opaque)
156        .then_some(generics)
157        .flatten()
158        .and_then(|generics| {
159            let params = generics.params.iter();
160            let write_each = params.filter_map(|param| match &param.value {
161                // Lifetimes not shown by `std::any::type_name`, this is parity.
162                GenericParam::Lifetime { .. } => None,
163                GenericParam::Const { name, .. } => Some(quote! {
164                    write!(f, "{:?}", #name)?;
165                }),
166                GenericParam::Type { name, .. } => Some(quote! {
167                    <#name as #facet_crate::Facet>::SHAPE.write_type_name(f, opts)?;
168                }),
169            });
170            // TODO: is there a way to construct a DelimitedVec from an iterator?
171            let mut tokens = TokenStream::new();
172            tokens.append_separated(write_each, quote! { write!(f, ", ")?; });
173            if tokens.is_empty() {
174                None
175            } else {
176                Some(tokens)
177            }
178        });
179
180    match write_generics {
181        Some(write_generics) => {
182            quote! {
183                |_shape, f, opts| {
184                    write!(f, #type_name_str)?;
185                    if let Some(opts) = opts.for_children() {
186                        write!(f, "<")?;
187                        #write_generics
188                        write!(f, ">")?;
189                    } else {
190                        write!(f, "<…>")?;
191                    }
192                    Ok(())
193                }
194            }
195        }
196        None => quote! { |_shape, f, _opts| ::core::fmt::Write::write_str(f, #type_name_str) },
197    }
198}