1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3use syn::{DataStruct, DeriveInput, Field, Fields, Ident};
4
5#[proc_macro_derive(Entry, attributes(lapdog))]
6pub fn implement_from_entry(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let input = syn::parse_macro_input!(item as DeriveInput);
8 let name = input.ident;
9 let (fields, object_name_field) = match parse_fields(
10 match input.data {
11 syn::Data::Struct(DataStruct { fields, .. }) => match fields {
12 Fields::Named(f) => f,
13 _ => panic!("Structs fields/attributes must be named to be derivable"),
14 },
15 _ => unimplemented!("non-struct derives are not supported"),
16 }
17 .named,
18 ) {
19 Ok(f) => f,
20 Err(e) => return e.into_compile_error().into(),
21 };
22
23 let insert_object_name = object_name_field.as_ref().map(insert_object_name);
24 let field_quotes = fields.iter().map(field_line);
25 let field_names = fields.iter().map(|x| x.ident());
26 let attribute_names = fields.iter().map(|x| x.attribute_name.clone());
27 quote!(
28 impl lapdog::search::FromEntry for #name {
29 fn from_entry(entry: lapdog::search::RawEntry) -> Result<#name, lapdog::search::FailedToGetFromEntry> {
30 #( #field_quotes )*
31 Ok(#name { #(#field_names,)* #insert_object_name })
32 }
33
34 fn attributes() -> Option<impl Iterator<Item = &'static str>> {
35 Some(vec![#(#attribute_names,)*].into_iter())
36 }
37 }
38 )
39 .into()
40}
41
42fn insert_object_name(field: &Field) -> TokenStream {
43 let field_name = field.ident.as_ref().expect("checked to be named field");
44 let ty = &field.ty;
45 quote! {
46 #field_name: <#ty as From<String>>::from(entry.object_name)
47 }
48}
49
50struct AttributeField {
51 attribute_name: String,
52 multiple: bool,
53 default: bool,
54 field: Field,
55}
56impl AttributeField {
57 fn ident(&self) -> Ident {
58 self.field.ident.clone().expect("checked to be named field")
59 }
60}
61fn parse_fields(
62 raw_fields: impl IntoIterator<Item = Field>,
63) -> Result<(Vec<AttributeField>, Option<Field>), syn::Error> {
64 let mut fields: Vec<AttributeField> = Vec::new();
65 let mut object_name_field = None;
66 'fields: for field in raw_fields {
67 let mut multiple = false;
68 let mut default = false;
69 let mut replaced_attribute_name = None;
70 for attr in &field.attrs {
71 let mut has_set_object_name_field = false;
72 attr.parse_nested_meta(|meta| {
73 if meta.path.is_ident("object_name") {
74 if object_name_field.replace(field.clone()).is_some() {
75 return Err(meta.error("\"object_name\" can only be declared on one field"));
76 };
77 has_set_object_name_field = true;
78 return Ok(());
79 }
80 if meta.path.require_ident()? == "rename" {
81 let lookahead = meta.input.lookahead1();
82 if lookahead.peek(syn::Token![=]) {
83 let expr = meta
84 .value()
85 .expect("Meta has no value")
86 .parse()
87 .expect("Meta is no expression");
88 let mut value = &expr;
89 while let syn::Expr::Group(e) = value {
90 value = &e.expr;
91 }
92 if let syn::Expr::Lit(syn::ExprLit {
93 lit: syn::Lit::Str(lit),
94 ..
95 }) = value
96 {
97 replaced_attribute_name = Some(lit.value());
98 } else {
99 return Err(meta.error("rename argument must be a string literal"));
100 }
101 } else {
102 return Err(meta.error("rename must be used like \"rename = <LDAP NAME>\""));
103 }
104 }
105 if meta.path.require_ident()? == "multiple" {
106 multiple = true;
107 }
108 if meta.path.require_ident()? == "default" {
109 default = true;
110 }
111 Ok(())
112 })?;
113 if has_set_object_name_field {
114 continue 'fields;
115 }
116 }
117 let attribute_name = replaced_attribute_name
118 .unwrap_or_else(|| field.ident.as_ref().expect("checked as named field").to_string());
119 fields.push(AttributeField {
120 attribute_name,
121 multiple,
122 default,
123 field,
124 })
125 }
126 Ok((fields, object_name_field))
127}
128
129fn field_line(data: &AttributeField) -> TokenStream {
130 let lookup_name = &data.attribute_name;
131 let field_type = &data.field.ty;
132 let varname = format_ident!("{}", data.ident());
133 let fallback = if data.default {
134 quote! { <#field_type as Default>::default() }
135 } else {
136 quote! { return Err(lapdog::search::FailedToGetFromEntry::MissingField(#lookup_name)) }
137 };
138 if data.multiple {
139 quote! {
140 let #varname = match entry.attributes.iter().find(|x| x.r#type == #lookup_name) {
141 Some(attrs) => <#field_type as lapdog::search::FromMultipleOctetStrings>::from_multiple_octet_strings(attrs.values.iter().map(|x| x.as_ref()))
142 .map_err(|b| lapdog::search::FailedToGetFromEntry::FailedToParseField(#lookup_name, Box::new(b)))?,
143 None => {#fallback},
144 };
145 }
146 } else {
147 quote! {
148 let #varname = match entry.attributes.iter().find(|x| x.r#type == #lookup_name).map(|x| x.values.as_slice()) {
149 Some([attr]) => <#field_type as lapdog::search::FromOctetString>::from_octet_string(attr).map_err(|b| lapdog::search::FailedToGetFromEntry::FailedToParseField(#lookup_name, Box::new(b)))?,
150 Some([]) | None => {#fallback},
151 Some(_) => {return Err(lapdog::search::FailedToGetFromEntry::TooManyValues(#lookup_name))}
152 };
153 }
154 }
155}