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 Reference(ReferenceType),
98 Path(PathType),
99 Tuple(ParenthesisGroupContaining<CommaDelimitedVec<Box<Type>>>),
100 Slice(BracketGroupContaining<Box<Type>>),
101 Bare(BareType),
102 }
103
104 struct ReferenceType {
105 _and: And,
106 lifetime: Lifetime,
107 rest: Box<Type>,
108 }
109
110 struct PathType {
111 prefix: Ident,
112 _doublesemi: DoubleSemicolon,
113 rest: Box<Type>,
114 }
115
116 struct BareType {
117 name: Ident,
118 generic_params: Option<GenericParams>,
119 }
120
121 struct GenericParams {
122 _lt: Lt,
123 lifetimes: CommaDelimitedVec<Lifetime>,
124 params: CommaDelimitedVec<Type>,
125 _gt: Gt,
126 }
127
128 enum ConstOrMut {
129 Const(KConst),
130 Mut(KMut),
131 }
132
133 struct StructField {
134 attributes: Vec<Attribute>,
135 _vis: Option<Vis>,
136 name: Ident,
137 _colon: Colon,
138 typ: Type,
139 }
140
141 struct TupleField {
142 attributes: Vec<Attribute>,
143 vis: Option<Vis>,
144 typ: Type,
145 }
146
147 struct Enum {
148 attributes: Vec<Attribute>,
149 _pub: Option<KPub>,
150 _kw_enum: KEnum,
151 name: Ident,
152 body: BraceGroupContaining<CommaDelimitedVec<EnumVariantLike>>,
153 }
154
155 enum EnumVariantLike {
156 Tuple(TupleVariant),
157 Struct(StructVariant),
158 Unit(UnitVariant),
159 }
160
161 struct UnitVariant {
162 attributes: Vec<Attribute>,
163 name: Ident,
164 }
165
166 struct TupleVariant {
167 attributes: Vec<Attribute>,
168 name: Ident,
169 fields: ParenthesisGroupContaining<CommaDelimitedVec<TupleField>>,
170 }
171
172 struct StructVariant {
173 attributes: Vec<Attribute>,
174 name: Ident,
175 fields: BraceGroupContaining<CommaDelimitedVec<StructField>>,
176 }
177}
178
179#[proc_macro_derive(Facet, attributes(facet))]
184pub fn facet_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
185 let input = TokenStream::from(input);
186 let mut i = input.to_token_iter();
187
188 match i.parse::<TypeDecl>() {
190 Ok(TypeDecl::Struct(parsed)) => process_struct::process_struct(parsed),
191 Ok(TypeDecl::Enum(parsed)) => process_enum::process_enum(parsed),
192 Err(err) => {
193 panic!(
194 "Could not parse type declaration: {}\nError: {:?}",
195 input, err
196 );
197 }
198 }
199}
200
201impl core::fmt::Display for Type {
202 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203 match self {
204 Type::Reference(reference) => {
205 write!(f, "&{} {}", reference.lifetime, reference.rest)
206 }
207 Type::Path(path) => {
208 write!(f, "{}::{}", path.prefix, path.rest)
209 }
210 Type::Tuple(tuple) => {
211 write!(f, "(")?;
212 for (i, typ) in tuple.content.0.iter().enumerate() {
213 if i > 0 {
214 write!(f, ", ")?;
215 }
216 write!(f, "{}", typ.value)?;
217 }
218 write!(f, ")")
219 }
220 Type::Slice(slice) => {
221 write!(f, "[{}]", slice.content)
222 }
223 Type::Bare(ident) => {
224 write!(f, "{}", ident.name)?;
225 if let Some(generic_params) = &ident.generic_params {
226 write!(f, "<")?;
227 for (i, param) in generic_params.params.0.iter().enumerate() {
228 if i > 0 {
229 write!(f, ", ")?;
230 }
231 write!(f, "{}", param.value)?;
232 }
233 write!(f, ">")?;
234 }
235 Ok(())
236 }
237 }
238 }
239}
240
241impl core::fmt::Display for ConstOrMut {
242 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243 match self {
244 ConstOrMut::Const(_) => write!(f, "const"),
245 ConstOrMut::Mut(_) => write!(f, "mut"),
246 }
247 }
248}
249
250impl core::fmt::Display for Lifetime {
251 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
252 write!(f, "'{}", self.name)
253 }
254}
255
256impl core::fmt::Display for Expr {
257 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258 match self {
259 Expr::Integer(int) => write!(f, "{}", int.value()),
260 }
261 }
262}
263
264pub(crate) fn to_upper_snake_case(input: &str) -> String {
266 input
267 .chars()
268 .enumerate()
269 .fold(String::new(), |mut acc, (i, c)| {
270 if c.is_uppercase() {
271 if i > 0 {
272 acc.push('_');
273 }
274 acc.push(c.to_ascii_uppercase());
275 } else {
276 acc.push(c.to_ascii_uppercase());
277 }
278 acc
279 })
280}
281
282pub(crate) fn generate_static_decl(type_name: &str) -> String {
284 format!(
285 "#[used]\nstatic {}_SHAPE: &'static facet::Shape = <{} as facet::Facet>::SHAPE;",
286 to_upper_snake_case(type_name),
287 type_name
288 )
289}
290
291pub(crate) fn build_maybe_doc(attrs: &[Attribute]) -> String {
292 let doc_lines: Vec<_> = attrs
293 .iter()
294 .filter_map(|attr| match &attr.body.content {
295 AttributeInner::Doc(doc_inner) => Some(doc_inner.value.value()),
296 _ => None,
297 })
298 .collect();
299
300 if doc_lines.is_empty() {
301 String::new()
302 } else {
303 format!(r#".doc(&[{}])"#, doc_lines.join(","))
304 }
305}
306
307pub(crate) fn gen_struct_field(field_name: &str, struct_name: &str, attrs: &[Attribute]) -> String {
308 let mut flags = "facet::FieldFlags::EMPTY";
310 let mut attribute_list: Vec<String> = vec![];
311 let mut doc_lines: Vec<&str> = vec![];
312 for attr in attrs {
313 match &attr.body.content {
314 AttributeInner::Facet(facet_attr) => match &facet_attr.inner.content {
315 FacetInner::Sensitive(_ksensitive) => {
316 flags = "facet::FieldFlags::SENSITIVE";
317 attribute_list.push("facet::FieldAttribute::Sensitive".to_string());
318 }
319 FacetInner::Other(tt) => {
320 attribute_list.push(format!(
321 r#"facet::FieldAttribute::Arbitrary({:?})"#,
322 tt.tokens_to_string()
323 ));
324 }
325 },
326 AttributeInner::Doc(doc_inner) => doc_lines.push(doc_inner.value.value()),
327 AttributeInner::Repr(_) => {
328 }
330 AttributeInner::Any(_) => {
331 }
333 }
334 }
335 let attributes = attribute_list.join(",");
336
337 let maybe_field_doc = if doc_lines.is_empty() {
338 String::new()
339 } else {
340 format!(r#".doc(&[{}])"#, doc_lines.join(","))
341 };
342
343 format!(
345 "facet::Field::builder()
346 .name(\"{field_name}\")
347 .shape(facet::shape_of(&|s: {struct_name}| s.{field_name}))
348 .offset(::core::mem::offset_of!({struct_name}, {field_name}))
349 .flags({flags})
350 .attributes(&[{attributes}])
351 {maybe_field_doc}
352 .build()"
353 )
354}