Skip to main content

metaxy_cli/parser/
types.rs

1use syn::{Fields, FieldsNamed, FieldsUnnamed, Type};
2
3use super::serde as serde_attr;
4use crate::model::{FieldDef, RustType};
5
6/// Converts a `syn::Type` into our `RustType` representation.
7///
8/// Handles paths (simple and generic), references, tuples, arrays, and unit.
9/// Unknown or unsupported types fall back to their token representation.
10pub fn extract_rust_type(ty: &Type) -> RustType {
11    match ty {
12        Type::Path(type_path) => {
13            let segments = &type_path.path.segments;
14            if segments.is_empty() {
15                let token_str = quote::quote!(#ty).to_string();
16                return RustType::simple(token_str);
17            }
18            // Preserve the full qualified path (e.g. "chrono::DateTime")
19            let name = segments
20                .iter()
21                .map(|s| s.ident.to_string())
22                .collect::<Vec<_>>()
23                .join("::");
24            let generics =
25                extract_generic_args(&segments.last().expect("non-empty segments").arguments);
26
27            if generics.is_empty() {
28                RustType::simple(name)
29            } else {
30                RustType::with_generics(name, generics)
31            }
32        }
33
34        Type::Reference(type_ref) => extract_rust_type(&type_ref.elem),
35
36        Type::Tuple(tuple) => {
37            if tuple.elems.is_empty() {
38                RustType::simple("()")
39            } else {
40                let inner: Vec<RustType> = tuple.elems.iter().map(extract_rust_type).collect();
41                RustType::with_generics("tuple", inner)
42            }
43        }
44
45        Type::Array(array) => {
46            let elem = extract_rust_type(&array.elem);
47            RustType::with_generics("Array", vec![elem])
48        }
49
50        Type::Slice(slice) => {
51            let elem = extract_rust_type(&slice.elem);
52            RustType::with_generics("Array", vec![elem])
53        }
54
55        _ => {
56            let token_str = quote::quote!(#ty).to_string();
57            RustType::simple(token_str)
58        }
59    }
60}
61
62/// Extracts generic type arguments from a path segment (e.g. `<String, i32>`).
63fn extract_generic_args(arguments: &syn::PathArguments) -> Vec<RustType> {
64    match arguments {
65        syn::PathArguments::AngleBracketed(args) => args
66            .args
67            .iter()
68            .filter_map(|arg| match arg {
69                syn::GenericArgument::Type(ty) => Some(extract_rust_type(ty)),
70                _ => None,
71            })
72            .collect(),
73        _ => vec![],
74    }
75}
76
77/// Extracts unnamed (tuple) fields from a struct into `RustType` values.
78///
79/// Returns an empty vec for named or unit structs.
80pub fn extract_tuple_fields(fields: &Fields) -> Vec<RustType> {
81    match fields {
82        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
83            unnamed.iter().map(|f| extract_rust_type(&f.ty)).collect()
84        }
85        _ => vec![],
86    }
87}
88
89/// Extracts named fields from a struct/variant into `FieldDef` values,
90/// including serde attributes (`rename`, `skip`, `default`).
91pub fn extract_struct_fields(fields: &Fields) -> Vec<FieldDef> {
92    match fields {
93        Fields::Named(FieldsNamed { named, .. }) => named
94            .iter()
95            .filter_map(|f| {
96                let name = f.ident.as_ref()?.to_string();
97                let ty = extract_rust_type(&f.ty);
98                let rename = serde_attr::parse_rename(&f.attrs);
99                let skip = serde_attr::is_skipped(&f.attrs);
100                let has_default = serde_attr::has_default(&f.attrs);
101                let flatten = serde_attr::is_flattened(&f.attrs);
102                Some(FieldDef {
103                    name,
104                    ty,
105                    rename,
106                    skip,
107                    has_default,
108                    flatten,
109                })
110            })
111            .collect(),
112        _ => vec![],
113    }
114}