1use syn::{GenericArgument, Type, TypePath};
2
3use crate::schema::{CollectionType, CrdtCollectionType, ScalarType, TypeRef};
4
5#[derive(Debug, thiserror::Error)]
7pub enum NormalizeError {
8 #[error("type path error: {0}")]
9 TypePathError(String),
10 #[error("unsupported map key type: {0}")]
11 UnsupportedMapKey(String),
12 #[error("unsupported array element type: {0}")]
13 UnsupportedArrayElement(String),
14}
15
16#[derive(Debug, Clone, Copy)]
18pub enum ResolvedLocal {
19 NewtypeBytes { size: usize },
21 Record,
23 Variant,
25}
26
27pub trait TypeResolver {
29 fn resolve_local(&self, name: &str) -> Option<ResolvedLocal>;
30}
31
32pub fn normalize_type(
34 ty: &Type,
35 wasm32: bool,
36 resolver: &dyn TypeResolver,
37) -> Result<TypeRef, NormalizeError> {
38 match ty {
39 Type::Path(type_path) => normalize_path_type(type_path, wasm32, resolver),
40 Type::Reference(type_ref) => {
41 normalize_type(&type_ref.elem, wasm32, resolver)
43 }
44 Type::Slice(type_slice) => {
45 let item_type = normalize_type(&type_slice.elem, wasm32, resolver)?;
47 Ok(TypeRef::list(item_type))
48 }
49 Type::Array(type_array) => {
50 let elem_type = &*type_array.elem;
52 let len = &type_array.len;
53
54 if let Type::Path(TypePath { path, .. }) = elem_type {
56 eprintln!("Checking if array element is u8");
57 if is_u8_type(path) {
58 eprintln!("Array element is u8, extracting length");
59 let size = extract_array_len(len)?;
60 eprintln!("Array size: {size}");
61 return Ok(TypeRef::bytes_with_size(size, None));
62 }
63 }
64
65 let item_type = normalize_type(elem_type, wasm32, resolver)?;
67 Ok(TypeRef::list(item_type))
68 }
69 Type::Tuple(type_tuple) => {
70 if type_tuple.elems.is_empty() {
72 Ok(TypeRef::unit())
73 } else {
74 Err(NormalizeError::TypePathError(
75 "unsupported tuple".to_owned(),
76 ))
77 }
78 }
79 _ => Err(NormalizeError::TypePathError("unsupported type".to_owned())),
80 }
81}
82
83fn normalize_path_type(
85 type_path: &TypePath,
86 wasm32: bool,
87 resolver: &dyn TypeResolver,
88) -> Result<TypeRef, NormalizeError> {
89 let path = &type_path.path;
90
91 eprintln!("Path segments: {}", path.segments.len());
92 for (i, seg) in path.segments.iter().enumerate() {
93 eprintln!(" Segment {}: {}", i, seg.ident);
94 }
95
96 if path.segments.len() == 1 {
97 let segment = &path.segments[0];
98 let ident = &segment.ident;
99
100 eprintln!(
101 "Processing path type: {} with {} segments",
102 ident,
103 path.segments.len()
104 );
105
106 if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
108 eprintln!("Found angle-bracketed arguments");
109 return normalize_generic_type(ident, args, wasm32, resolver);
110 }
111
112 eprintln!("No angle-bracketed arguments, treating as scalar");
114 return normalize_scalar_type(path, wasm32, resolver);
115 } else if path.segments.len() == 2 {
116 let first_segment = &path.segments[0];
118 let second_segment = &path.segments[1];
119
120 eprintln!(
121 "Processing qualified path: {}::{}",
122 first_segment.ident, second_segment.ident
123 );
124
125 if first_segment.ident == "app" && second_segment.ident == "Result" {
127 if let syn::PathArguments::AngleBracketed(args) = &second_segment.arguments {
128 return normalize_generic_type(&second_segment.ident, args, wasm32, resolver);
129 }
130 }
131
132 if first_segment.ident == "serde_json" && second_segment.ident == "Value" {
134 return Ok(TypeRef::string());
135 }
136 }
137
138 Err(NormalizeError::TypePathError(
139 "invalid type path".to_owned(),
140 ))
141}
142
143fn normalize_generic_type(
145 ident: &syn::Ident,
146 args: &syn::AngleBracketedGenericArguments,
147 wasm32: bool,
148 resolver: &dyn TypeResolver,
149) -> Result<TypeRef, NormalizeError> {
150 let ident_str = ident.to_string();
151 eprintln!(
152 "Processing generic type: '{}' (len: {}) with {} args",
153 ident_str,
154 ident_str.len(),
155 args.args.len()
156 );
157 match ident_str.as_str() {
158 "Option" => {
159 if args.args.len() != 1 {
161 return Err(NormalizeError::TypePathError(
162 "invalid Option type".to_owned(),
163 ));
164 }
165 let arg = &args.args[0];
166 let GenericArgument::Type(ty) = arg else {
167 return Err(NormalizeError::TypePathError(
168 "invalid Option argument".to_owned(),
169 ));
170 };
171 normalize_type(ty, wasm32, resolver)
172 }
173 "Vec" | "VecDeque" | "LinkedList" => {
175 if args.args.len() != 1 {
177 return Err(NormalizeError::TypePathError(format!(
178 "invalid {ident_str} type - expected 1 type argument"
179 )));
180 }
181 let item_arg = &args.args[0];
182 let GenericArgument::Type(item_ty) = item_arg else {
183 return Err(NormalizeError::TypePathError(format!(
184 "invalid {ident_str} item type"
185 )));
186 };
187
188 if ident_str == "Vec" {
190 if let Type::Path(TypePath { path, .. }) = item_ty {
191 if is_u8_type(path) {
192 return Ok(TypeRef::Scalar(ScalarType::Bytes {
193 size: None,
194 encoding: None,
195 }));
196 }
197 }
198 }
199
200 let item_type = normalize_type(item_ty, wasm32, resolver)?;
201 Ok(TypeRef::list(item_type))
202 }
203 "BTreeMap" | "HashMap" | "UnorderedMap" | "IndexMap" => {
205 if args.args.len() != 2 {
208 return Err(NormalizeError::TypePathError(format!(
209 "invalid {ident_str} type - expected 2 type arguments"
210 )));
211 }
212
213 let key_arg = &args.args[0];
214 let value_arg = &args.args[1];
215
216 let GenericArgument::Type(key_ty) = key_arg else {
217 return Err(NormalizeError::TypePathError(format!(
218 "invalid {ident_str} key type"
219 )));
220 };
221
222 let GenericArgument::Type(value_ty) = value_arg else {
223 return Err(NormalizeError::TypePathError(format!(
224 "invalid {ident_str} value type"
225 )));
226 };
227
228 let _key_type = normalize_type(key_ty, wasm32, resolver)?;
230 let value_type = normalize_type(value_ty, wasm32, resolver)?;
231
232 let crdt_type = if ident_str == "UnorderedMap" {
234 Some(CrdtCollectionType::UnorderedMap)
235 } else {
236 None
237 };
238
239 Ok(TypeRef::Collection {
240 collection: CollectionType::Map {
241 key: Box::new(TypeRef::Scalar(ScalarType::String)),
242 value: Box::new(value_type),
243 },
244 crdt_type,
245 inner_type: None, })
247 }
248 "HashSet" | "BTreeSet" | "IndexSet" => {
250 if args.args.len() != 1 {
252 return Err(NormalizeError::TypePathError(format!(
253 "invalid {ident_str} type - expected 1 type argument"
254 )));
255 }
256
257 let arg = &args.args[0];
258 let GenericArgument::Type(item_ty) = arg else {
259 return Err(NormalizeError::TypePathError(format!(
260 "invalid {ident_str} argument"
261 )));
262 };
263
264 let item_type = normalize_type(item_ty, wasm32, resolver)?;
265 Ok(TypeRef::list(item_type))
266 }
267 "Result" => {
268 if args.args.len() == 1 {
271 let arg = &args.args[0];
273 let GenericArgument::Type(ty) = arg else {
274 return Err(NormalizeError::TypePathError(
275 "invalid Result argument".to_owned(),
276 ));
277 };
278 normalize_type(ty, wasm32, resolver)
279 } else if args.args.len() == 2 {
280 let arg = &args.args[0];
282 let GenericArgument::Type(ty) = arg else {
283 return Err(NormalizeError::TypePathError(
284 "invalid Result argument".to_owned(),
285 ));
286 };
287 normalize_type(ty, wasm32, resolver)
288 } else {
289 return Err(NormalizeError::TypePathError(
290 "invalid Result type".to_owned(),
291 ));
292 }
293 }
294 "LwwRegister"
296 | "Counter"
297 | "ReplicatedGrowableArray"
298 | "Vector"
299 | "UnorderedSet"
300 | "FrozenValue" => {
301 if ident_str == "Counter" || ident_str == "ReplicatedGrowableArray" {
305 if ident_str == "Counter" {
308 return Ok(TypeRef::Collection {
311 collection: CollectionType::Record {
312 fields: vec![], },
314 crdt_type: Some(CrdtCollectionType::Counter),
315 inner_type: None, });
317 } else {
318 return Ok(TypeRef::Collection {
320 collection: CollectionType::Record {
321 fields: vec![], },
323 crdt_type: Some(CrdtCollectionType::ReplicatedGrowableArray),
324 inner_type: None,
325 });
326 }
327 }
328
329 if args.args.is_empty() {
331 return Err(NormalizeError::TypePathError(format!(
332 "invalid {ident_str} type - expected 1 type argument"
333 )));
334 }
335 let arg = &args.args[0];
336 let GenericArgument::Type(ty) = arg else {
337 return Err(NormalizeError::TypePathError(
338 "invalid CRDT argument".to_owned(),
339 ));
340 };
341 let inner_type = normalize_type(ty, wasm32, resolver)?;
342
343 match ident_str.as_str() {
345 "LwwRegister" => {
346 Ok(TypeRef::Collection {
350 collection: CollectionType::Record {
351 fields: vec![], },
353 crdt_type: Some(CrdtCollectionType::LwwRegister),
354 inner_type: Some(Box::new(inner_type)),
355 })
356 }
357 "Vector" => {
358 Ok(TypeRef::Collection {
361 collection: CollectionType::List {
362 items: Box::new(inner_type),
363 },
364 crdt_type: Some(CrdtCollectionType::Vector),
365 inner_type: None, })
367 }
368 "UnorderedSet" => {
369 Ok(TypeRef::Collection {
372 collection: CollectionType::List {
373 items: Box::new(inner_type),
374 },
375 crdt_type: Some(CrdtCollectionType::UnorderedSet),
376 inner_type: None, })
378 }
379 "FrozenValue" => {
380 Ok(inner_type)
382 }
383 _ => Ok(inner_type),
384 }
385 }
386 "UserStorage" | "FrozenStorage" => {
388 if args.args.is_empty() {
391 return Err(NormalizeError::TypePathError(format!(
392 "invalid {ident_str} type - expected 1 type argument"
393 )));
394 }
395 let arg = &args.args[0];
396 let GenericArgument::Type(ty) = arg else {
397 return Err(NormalizeError::TypePathError(
398 "invalid storage argument".to_owned(),
399 ));
400 };
401 let value_type = normalize_type(ty, wasm32, resolver)?;
402
403 Ok(TypeRef::Collection {
405 collection: CollectionType::Map {
406 key: Box::new(TypeRef::Scalar(ScalarType::String)),
407 value: Box::new(value_type),
408 },
409 crdt_type: None,
410 inner_type: None,
411 })
412 }
413 _ => Err(NormalizeError::TypePathError(format!(
414 "unknown generic type: {ident}"
415 ))),
416 }
417}
418
419fn is_u8_type(path: &syn::Path) -> bool {
421 path.segments.len() == 1 && path.segments[0].ident == "u8"
422}
423
424fn extract_array_len(len: &syn::Expr) -> Result<usize, NormalizeError> {
426 if let syn::Expr::Lit(syn::ExprLit {
427 lit: syn::Lit::Int(lit),
428 ..
429 }) = len
430 {
431 lit.base10_parse()
432 .map_err(|_| NormalizeError::TypePathError("failed to parse array length".to_owned()))
433 } else {
434 Err(NormalizeError::TypePathError(
435 "array length must be a literal integer".to_owned(),
436 ))
437 }
438}
439
440fn normalize_scalar_type(
442 path: &syn::Path,
443 wasm32: bool,
444 resolver: &dyn TypeResolver,
445) -> Result<TypeRef, NormalizeError> {
446 if path.segments.len() != 1 {
447 return Err(NormalizeError::TypePathError(
448 "invalid scalar type path".to_owned(),
449 ));
450 }
451
452 let ident = &path.segments[0].ident;
453 match ident.to_string().as_str() {
454 "bool" => Ok(TypeRef::bool()),
455 "i8" | "i16" | "i32" => Ok(TypeRef::i32()),
456 "i64" => Ok(TypeRef::i64()),
457 "u8" | "u16" | "u32" => Ok(TypeRef::u32()),
458 "u64" => Ok(TypeRef::u64()),
459 "f32" => Ok(TypeRef::f32()),
460 "f64" => Ok(TypeRef::f64()),
461 "String" | "str" => Ok(TypeRef::string()),
462 "usize" => {
463 if wasm32 {
464 Ok(TypeRef::u32())
465 } else {
466 Ok(TypeRef::u64())
467 }
468 }
469 "isize" => {
470 if wasm32 {
471 Ok(TypeRef::i32())
472 } else {
473 Ok(TypeRef::i64())
474 }
475 }
476 "PublicKey" => {
478 Ok(TypeRef::bytes_with_size(32, None))
480 }
481 "ProposalId" => {
483 Ok(TypeRef::bytes_with_size(32, None))
485 }
486 "Counter" => Ok(TypeRef::Collection {
488 collection: CollectionType::Record {
489 fields: vec![], },
491 crdt_type: Some(CrdtCollectionType::Counter),
492 inner_type: None, }),
494 "ReplicatedGrowableArray" => Ok(TypeRef::Collection {
495 collection: CollectionType::Record {
496 fields: vec![], },
498 crdt_type: Some(CrdtCollectionType::ReplicatedGrowableArray),
499 inner_type: None,
500 }),
501 _ => {
502 resolver.resolve_local(&ident.to_string()).map_or_else(
504 || {
505 Err(NormalizeError::TypePathError(format!(
506 "unknown type: {ident}"
507 )))
508 },
509 |resolved| match resolved {
510 ResolvedLocal::NewtypeBytes { size } => {
511 Ok(TypeRef::bytes_with_size(size, None))
512 }
513 ResolvedLocal::Record | ResolvedLocal::Variant => {
514 Ok(TypeRef::reference(&ident.to_string()))
515 }
516 },
517 )
518 }
519 }
520}
521
522pub trait TypeRefExt {
524 fn set_nullable(&mut self, nullable: bool);
525}
526
527impl TypeRefExt for TypeRef {
528 fn set_nullable(&mut self, _nullable: bool) {
529 }
531}