facet_derive/
lib.rs

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