core_crypto_macros/entity_derive/
parse.rs1use crate::entity_derive::{Column, ColumnType, Columns, IdColumn, IdColumnType, IdTransformation, KeyStoreEntity};
2use heck::ToSnakeCase;
3use proc_macro2::{Ident, Span};
4use quote::ToTokens;
5use syn::spanned::Spanned;
6use syn::{Attribute, Data, DataStruct, Fields, FieldsNamed, Token, Type};
7
8impl syn::parse::Parse for KeyStoreEntity {
9 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
10 let derive_input = input.parse::<syn::DeriveInput>()?;
11 let struct_name = derive_input.ident.clone();
12
13 let (mut collection_name, no_upsert) = Self::parse_outer_attributes(&derive_input.attrs)?;
15 if collection_name.is_empty() {
16 collection_name = struct_name.to_string().to_snake_case() + "s";
17 }
18
19 let named_fields = Self::fields_from_data(&derive_input.data, derive_input.span())?;
20 let id = IdColumn::parse(named_fields)?;
21 let columns = Columns::parse(named_fields, &id.name)?;
22
23 Ok(KeyStoreEntity {
24 struct_name,
25 collection_name,
26 id,
27 columns,
28 no_upsert,
29 })
30 }
31}
32
33impl KeyStoreEntity {
34 fn parse_outer_attributes(attrs: &[Attribute]) -> Result<(String, bool), syn::Error> {
35 let mut collection_name = String::new();
36 let mut no_upsert = false;
37 for attr in attrs {
38 if !attr.path().is_ident("entity") {
39 continue;
40 }
41 let meta = &attr.meta;
42 let list = meta.require_list()?;
43 list.parse_nested_meta(|meta| {
44 let ident = meta.path.require_ident()?;
45 match ident.to_string().as_str() {
46 "collection_name" => {
47 meta.input.parse::<Token![=]>()?;
48 collection_name = meta.input.parse::<syn::LitStr>()?.value();
49 Ok(())
50 }
51 "no_upsert" => {
52 no_upsert = true;
53 Ok(())
54 }
55 _ => Err(syn::Error::new_spanned(ident, "unknown argument")),
56 }
57 })?;
58 }
59 Ok((collection_name, no_upsert))
60 }
61
62 fn fields_from_data(data: &Data, span: Span) -> syn::Result<&FieldsNamed> {
63 match data {
64 Data::Struct(DataStruct {
65 fields: Fields::Named(named_fields),
66 ..
67 }) => Ok(named_fields),
68 _ => Err(syn::Error::new(span, "Expected a struct with named fields.")),
69 }
70 }
71}
72
73impl IdColumn {
74 fn parse(named_fields: &FieldsNamed) -> syn::Result<Self> {
75 let mut id = None;
76 let mut implicit_id = None;
77
78 for field in &named_fields.named {
79 let name = field
80 .ident
81 .as_ref()
82 .expect("named fields always have identifiers")
83 .clone();
84
85 if let Some(attr) = field.attrs.iter().find(|a| a.path().is_ident("id")) {
86 let mut column_name = None;
87 let mut transformation = None;
88 let column_type = IdColumnType::parse(&field.ty)?;
89
90 if let Ok(list) = attr.meta.require_list() {
91 list.parse_nested_meta(|meta| {
92 match meta.path.require_ident()?.to_string().as_str() {
93 "column" => {
94 meta.input.parse::<Token![=]>()?;
95 column_name = Some(meta.input.parse::<syn::LitStr>()?.value());
96 }
97 "hex" => transformation = Some(IdTransformation::Hex),
98 _ => return Err(syn::Error::new_spanned(meta.path, "unknown argument")),
99 }
100 Ok(())
101 })?;
102 }
103
104 if id
105 .replace(IdColumn {
106 name,
107 column_type,
108 column_name,
109 transformation,
110 })
111 .is_some()
112 {
113 return Err(syn::Error::new_spanned(
114 field,
115 "Ambiguous `#[id]` attributes. Provide exactly one.",
116 ));
117 }
118 } else if name == "id" {
119 let column_type = IdColumnType::parse(&field.ty)?;
120 implicit_id = Some(IdColumn {
121 name,
122 column_type,
123 column_name: None,
124 transformation: None,
125 });
126 }
127 }
128
129 id.or(implicit_id).ok_or_else(|| {
130 syn::Error::new_spanned(
131 named_fields,
132 "No field named `id` or annotated `#[id]` attribute provided.",
133 )
134 })
135 }
136}
137
138impl IdColumnType {
139 fn parse(ty: &Type) -> Result<Self, syn::Error> {
140 let mut type_string = ty.to_token_stream().to_string();
141 type_string.retain(|c| !c.is_whitespace());
142 match type_string.as_str() {
143 "String" | "std::string::String" => Ok(Self::String),
144 "Vec<u8>" | "std::vec::Vec<u8>" => Ok(Self::Bytes),
145 type_string => Err(syn::Error::new_spanned(
146 ty,
147 format!("Expected `String` or `Vec<u8>`, not `{type_string}`."),
148 )),
149 }
150 }
151}
152
153impl Columns {
154 fn parse(named_fields: &FieldsNamed, id_column: &Ident) -> syn::Result<Self> {
155 let columns = named_fields
156 .named
157 .iter()
158 .filter(|field| field.ident.as_ref() != Some(id_column))
159 .map(|field| {
160 let field_name = field
161 .ident
162 .as_ref()
163 .expect("named fields always have identifiers")
164 .clone();
165 let field_type = ColumnType::parse(&field.ty)?;
166
167 Ok(Column {
168 name: field_name,
169 column_type: field_type,
170 })
171 })
172 .collect::<syn::Result<Vec<_>>>()?;
173 if columns.is_empty() {
174 return Err(syn::Error::new_spanned(
175 named_fields,
176 "Provide at least one field to be used as a table column.",
177 ));
178 }
179
180 Ok(Self(columns))
181 }
182}
183
184impl ColumnType {
185 fn parse(ty: &Type) -> Result<Self, syn::Error> {
186 let mut type_string = ty.to_token_stream().to_string();
187 type_string.retain(|c| !c.is_whitespace());
188 match type_string.as_str() {
189 "String" | "std::string::String" => Ok(Self::String),
190 "Vec<u8>" | "std::vec::Vec<u8>" => Ok(Self::Bytes),
191 "Option<Vec<u8>>"
192 | "Option<std::vec::Vec<u8>>"
193 | "core::option::Option<Vec<u8>>"
194 | "core::option::Option<std::vec::Vec<u8>>"
195 | "std::option::Option<Vec<u8>>"
196 | "std::option::Option<std::vec::Vec<u8>>" => Ok(Self::OptionalBytes),
197 type_string => Err(syn::Error::new_spanned(
198 ty,
199 format!("Expected `String`, `Vec<u8>`, or `Option<Vec<u8>>` not `{type_string}`."),
200 )),
201 }
202 }
203}