facet_derive/
lib.rs

1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4mod process_enum;
5mod process_struct;
6
7use unsynn::*;
8
9keyword! {
10    KPub = "pub";
11    KStruct = "struct";
12    KEnum = "enum";
13    KDoc = "doc";
14    KRepr = "repr";
15    KCrate = "crate";
16    KConst = "const";
17    KMut = "mut";
18    KFacet = "facet";
19    KSensitive = "sensitive";
20}
21
22operator! {
23    Eq = "=";
24    Semi = ";";
25    Apostrophe = "'";
26    DoubleSemicolon = "::";
27}
28
29unsynn! {
30    enum TypeDecl {
31        Struct(Struct),
32        Enum(Enum),
33    }
34
35    enum Vis {
36        Pub(KPub),
37        PubCrate(Cons<KPub, ParenthesisGroupContaining<KCrate>>),
38    }
39
40    struct Attribute {
41        _pound: Pound,
42        body: BracketGroupContaining<AttributeInner>,
43    }
44
45    enum AttributeInner {
46        Facet(FacetAttr),
47        Doc(DocInner),
48        Repr(ReprInner),
49        Any(Vec<TokenTree>)
50    }
51
52    struct FacetAttr {
53        _facet: KFacet,
54        inner: ParenthesisGroupContaining<FacetInner>,
55    }
56
57    enum FacetInner {
58        Sensitive(KSensitive),
59        Other(Vec<TokenTree>)
60    }
61
62    struct DocInner {
63        _kw_doc: KDoc,
64        _eq: Eq,
65        value: LiteralString,
66    }
67
68    struct ReprInner {
69        _kw_repr: KRepr,
70        attr: ParenthesisGroupContaining<Ident>,
71    }
72
73    struct Struct {
74        attributes: Vec<Attribute>,
75        _vis: Option<Vis>,
76        _kw_struct: KStruct,
77        name: Ident,
78        // if None, Unit struct
79        body: Option<StructBody>,
80    }
81
82    enum StructBody {
83        Struct(BraceGroupContaining<CommaDelimitedVec<StructField>>),
84        TupleStruct(ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>),
85    }
86
87    struct Lifetime {
88        _apostrophe: Apostrophe,
89        name: Ident,
90    }
91
92    enum Expr {
93        Integer(LiteralInteger),
94    }
95
96    enum Type {
97        Path(PathType),
98        Tuple(ParenthesisGroupContaining<CommaDelimitedVec<Box<Type>>>),
99        Slice(BracketGroupContaining<Box<Type>>),
100        Bare(BareType),
101    }
102
103    struct PathType {
104        prefix: Ident,
105        _doublesemi: DoubleSemicolon,
106        rest: Box<Type>,
107    }
108
109    struct BareType {
110        name: Ident,
111        generic_params: Option<GenericParams>,
112    }
113
114    struct GenericParams {
115        _lt: Lt,
116        params: CommaDelimitedVec<Type>,
117        _gt: Gt,
118    }
119
120    enum ConstOrMut {
121        Const(KConst),
122        Mut(KMut),
123    }
124
125    struct StructField {
126        attributes: Vec<Attribute>,
127        _vis: Option<Vis>,
128        name: Ident,
129        _colon: Colon,
130        typ: Type,
131    }
132
133    struct TupleField {
134        attributes: Vec<Attribute>,
135        vis: Option<Vis>,
136        typ: Type,
137    }
138
139    struct Enum {
140        attributes: Vec<Attribute>,
141        _pub: Option<KPub>,
142        _kw_enum: KEnum,
143        name: Ident,
144        body: BraceGroupContaining<CommaDelimitedVec<EnumVariantLike>>,
145    }
146
147    enum EnumVariantLike {
148        Tuple(TupleVariant),
149        Struct(StructVariant),
150        Unit(UnitVariant),
151    }
152
153    struct UnitVariant {
154        attributes: Vec<Attribute>,
155        name: Ident,
156    }
157
158    struct TupleVariant {
159        attributes: Vec<Attribute>,
160        name: Ident,
161        fields: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
162    }
163
164    struct StructVariant {
165        attributes: Vec<Attribute>,
166        name: Ident,
167        fields: BraceGroupContaining<CommaDelimitedVec<StructField>>,
168    }
169}
170
171/// Derive the Facet trait for structs, tuple structs, and enums.
172///
173/// This uses unsynn, so it's light, but it _will_ choke on some Rust syntax because...
174/// there's a lot of Rust syntax.
175#[proc_macro_derive(Facet, attributes(facet))]
176pub fn facet_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
177    let input = TokenStream::from(input);
178    let mut i = input.to_token_iter();
179
180    // Parse as TypeDecl
181    match i.parse::<TypeDecl>() {
182        Ok(TypeDecl::Struct(parsed)) => process_struct::process_struct(parsed),
183        Ok(TypeDecl::Enum(parsed)) => process_enum::process_enum(parsed),
184        Err(err) => {
185            panic!(
186                "Could not parse type declaration: {}\nError: {:?}",
187                input, err
188            );
189        }
190    }
191}
192
193impl core::fmt::Display for Type {
194    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195        match self {
196            Type::Path(path) => {
197                write!(f, "{}::{}", path.prefix, path.rest)
198            }
199            Type::Tuple(tuple) => {
200                write!(f, "(")?;
201                for (i, typ) in tuple.content.0.iter().enumerate() {
202                    if i > 0 {
203                        write!(f, ", ")?;
204                    }
205                    write!(f, "{}", typ.value)?;
206                }
207                write!(f, ")")
208            }
209            Type::Slice(slice) => {
210                write!(f, "[{}]", slice.content)
211            }
212            Type::Bare(ident) => {
213                write!(f, "{}", ident.name)?;
214                if let Some(generic_params) = &ident.generic_params {
215                    write!(f, "<")?;
216                    for (i, param) in generic_params.params.0.iter().enumerate() {
217                        if i > 0 {
218                            write!(f, ", ")?;
219                        }
220                        write!(f, "{}", param.value)?;
221                    }
222                    write!(f, ">")?;
223                }
224                Ok(())
225            }
226        }
227    }
228}
229
230impl core::fmt::Display for ConstOrMut {
231    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232        match self {
233            ConstOrMut::Const(_) => write!(f, "const"),
234            ConstOrMut::Mut(_) => write!(f, "mut"),
235        }
236    }
237}
238
239impl core::fmt::Display for Lifetime {
240    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241        write!(f, "'{}", self.name)
242    }
243}
244
245impl core::fmt::Display for Expr {
246    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247        match self {
248            Expr::Integer(int) => write!(f, "{}", int.value()),
249        }
250    }
251}
252
253/// Converts PascalCase to UPPER_SNAKE_CASE
254pub(crate) fn to_upper_snake_case(input: &str) -> String {
255    input
256        .chars()
257        .enumerate()
258        .fold(String::new(), |mut acc, (i, c)| {
259            if c.is_uppercase() {
260                if i > 0 {
261                    acc.push('_');
262                }
263                acc.push(c.to_ascii_uppercase());
264            } else {
265                acc.push(c.to_ascii_uppercase());
266            }
267            acc
268        })
269}
270
271/// Generate a static declaration that exports the crate
272pub(crate) fn generate_static_decl(type_name: &str) -> String {
273    format!(
274        "#[used]\nstatic {}_SHAPE: &'static facet::Shape = <{} as facet::Facet>::SHAPE;",
275        to_upper_snake_case(type_name),
276        type_name
277    )
278}
279
280pub(crate) fn build_maybe_doc(attrs: &[Attribute]) -> String {
281    let doc_lines: Vec<_> = attrs
282        .iter()
283        .filter_map(|attr| match &attr.body.content {
284            AttributeInner::Doc(doc_inner) => Some(doc_inner.value.value()),
285            _ => None,
286        })
287        .collect();
288
289    if doc_lines.is_empty() {
290        String::new()
291    } else {
292        format!(r#".doc(&[{}])"#, doc_lines.join(","))
293    }
294}
295
296pub(crate) fn gen_struct_field(field_name: &str, struct_name: &str, attrs: &[Attribute]) -> String {
297    // Determine field flags
298    let mut flags = "facet::FieldFlags::EMPTY";
299    let mut attribute_list: Vec<String> = vec![];
300    let mut doc_lines: Vec<&str> = vec![];
301    for attr in attrs {
302        match &attr.body.content {
303            AttributeInner::Facet(facet_attr) => match &facet_attr.inner.content {
304                FacetInner::Sensitive(_ksensitive) => {
305                    flags = "facet::FieldFlags::SENSITIVE";
306                    attribute_list.push("facet::FieldAttribute::Sensitive".to_string());
307                }
308                FacetInner::Other(tt) => {
309                    attribute_list.push(format!(
310                        r#"facet::FieldAttribute::Arbitrary({:?})"#,
311                        tt.tokens_to_string()
312                    ));
313                }
314            },
315            AttributeInner::Doc(doc_inner) => doc_lines.push(doc_inner.value.value()),
316            AttributeInner::Repr(_) => {
317                // muffin
318            }
319            AttributeInner::Any(_) => {
320                // muffin two
321            }
322        }
323    }
324    let attributes = attribute_list.join(",");
325
326    let maybe_field_doc = if doc_lines.is_empty() {
327        String::new()
328    } else {
329        format!(r#".doc(&[{}])"#, doc_lines.join(","))
330    };
331
332    // Generate each field definition
333    format!(
334        "facet::Field::builder()
335    .name(\"{field_name}\")
336    .shape(facet::shape_of(&|s: {struct_name}| s.{field_name}))
337    .offset(::core::mem::offset_of!({struct_name}, {field_name}))
338    .flags({flags})
339    .attributes(&[{attributes}])
340    {maybe_field_doc}
341    .build()"
342    )
343}