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 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#[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 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
253pub(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
271pub(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 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 }
319 AttributeInner::Any(_) => {
320 }
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 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}