csv_deserializer/
enum_gen.rs

1#![allow(clippy::uninlined_format_args)]
2
3use crate::{COLUMN_TYPE_ENUM_NAME,ColName, CsvAny, CsvDataset, dataset_info::{ColumnInfo, Variant}, sanitizer::sanitize_identifier};
4
5
6#[macro_export]
7macro_rules! create_enum {
8    // --- 1. Caso Base: Input finito ---
9    // Quando la lista di input `[]` รจ vuota, generiamo il codice finale.
10    (@step $name:ident, ($($variants:ident),*), ($($arms:tt)*), []) => {
11        
12        
13        #[derive(Debug, PartialEq, Clone, Copy)]
14        pub enum $name {
15            $($variants),*
16        }
17
18        impl std::str::FromStr for $name {
19            type Err = String;
20            fn from_str(s: &str) -> Result<Self, Self::Err> {
21                match s {
22                    // Inseriamo qui tutti i rami match accumulati
23                    $($arms)*
24                    _ => Err(format!("Valore non riconosciuto: '{}'", s)),
25                }
26            }
27        }
28        
29    };
30
31    // --- 2. Caso Complesso: "str1" | "str2" => Variante ---
32    // Riconosce il pattern stringa/e => Identificatore
33    (@step $name:ident, ($($vars:ident),*), ($($arms:tt)*), [ $($l:literal)|+ => $v:ident, $($rest:tt)* ]) => {
34        create_enum!(
35            @step 
36            $name, 
37            ($($vars,)* $v), // Aggiunge la variante alla lista
38            ($($arms)* $($l)|+ => Ok($name::$v),), // Aggiunge il match arm personalizzato
39            [ $($rest)* ] // Continua con il resto
40        );
41    };
42    // Gestione dell'ultimo elemento (senza virgola finale) per il caso complesso
43    (@step $name:ident, ($($vars:ident),*), ($($arms:tt)*), [ $($l:literal)|+ => $v:ident ]) => {
44        create_enum!(@step $name, ($($vars,)* $v), ($($arms)* $($l)|+ => Ok($name::$v),), []);
45    };
46
47    // --- 3. Caso Semplice: Variante ---
48    // Riconosce solo l'Identificatore (usa il nome stesso come stringa)
49    (@step $name:ident, ($($vars:ident),*), ($($arms:tt)*), [ $v:ident, $($rest:tt)* ]) => {
50        create_enum!(
51            @step 
52            $name, 
53            ($($vars,)* $v), 
54            ($($arms)* stringify!($v) => Ok($name::$v),), 
55            [ $($rest)* ]
56        );
57    };
58    // Gestione dell'ultimo elemento (senza virgola finale) per il caso semplice
59    (@step $name:ident, ($($vars:ident),*), ($($arms:tt)*), [ $v:ident ]) => {
60        create_enum!(@step $name, ($($vars,)* $v), ($($arms)* stringify!($v) => Ok($name::$v),), []);
61    };
62
63    // --- Entry Point ---
64    // Inizializza gli accumulatori vuoti
65    ($name:ident; $($input:tt)*) => {
66        create_enum!(@step $name, (), (), [ $($input)* ]);
67    };
68}
69
70
71
72
73pub fn generate_enums_from(dataset: &mut CsvDataset) -> String{
74    
75    let mut full_string = String::new();
76
77    let (value_names_view, info) = dataset.split_view_and_info();
78    let col_name = value_names_view.names;
79    let enums = col_name.iter().map(|col_name| {
80
81        let mut col_info = ColumnInfo::new(value_names_view, &col_name.raw);
82
83
84        if !col_info.unique_values.iter().any(|x| x.csvany == CsvAny::Null){
85            let str = String::from("Null");
86            col_info.unique_values.push(Variant{ raw: str.clone(), sanitized: str, csvany: CsvAny::Null});
87        }
88
89        info.push(col_info.clone());
90        
91        let unique_val_iter = col_info.unique_values.iter();
92
93        
94        let is_str = unique_val_iter.clone().all(|x| {
95            matches!(x.csvany, CsvAny::Str(_) | CsvAny::Empty | CsvAny::Null)
96        });
97        let is_int = unique_val_iter.clone().all(|x| {
98            matches!(x.csvany, CsvAny::Int(_) | CsvAny::Empty | CsvAny::Null)
99        });
100        let is_float = unique_val_iter.clone().all(|x| {
101            matches!(x.csvany, CsvAny::Float(_) | CsvAny::Empty | CsvAny::Null)
102        });
103        
104        if is_int{
105            gen_int_enum(col_name)
106        }else if is_float{
107            gen_float_enum(col_name)
108        }
109        else if is_str{
110            gen_str_enum(col_name, unique_val_iter)
111        }else {
112            println!("enum generation log: column `{}` contains numbers and strings", col_name.raw);
113            gen_str_enum(col_name, unique_val_iter)
114        }
115        
116    } + "\n\n").collect::<String>();
117
118    
119    let mut columns_enum = format!("#[derive(Debug)]\npub enum {COLUMN_TYPE_ENUM_NAME}{{\n");
120
121    for col_name in col_name.iter() {
122        let sanitized = &col_name.sanitized.0;
123        columns_enum.push_str(&format!("{sanitized}(Vec<{sanitized}>),\n"));
124    }
125    columns_enum.push_str("}\n\n");
126
127    
128
129    full_string.push_str(&enums);
130    full_string.push_str(&columns_enum);
131    // full_string.push_str(&columns_enum_from_str);
132    full_string
133}
134
135fn gen_str_enum<'a>(col_name: &ColName, unique_values: impl Iterator<Item = &'a Variant>) -> String{
136    let variants = unique_values
137            .map(|var| match &var.csvany {
138                CsvAny::Int(int) => sanitize_identifier(&int.to_string()),
139                CsvAny::Str(str) => format!("\"{}\" => {}", str, sanitize_identifier(str)),
140                CsvAny::Empty => "Empty".to_string(),
141                CsvAny::Null => "Null".to_string(),
142                CsvAny::Float(_) => panic!("Should not be used on float since they cannot represent categories"),
143            } + ",\n")
144            .collect::<String>();
145        format!("create_enum!({};\n{variants});", col_name.sanitized.0)
146}
147fn gen_float_enum(col_name: &ColName) -> String {
148    let name = &col_name.sanitized.0;
149    format!("
150    #[derive(Debug, Clone, Copy, PartialEq,PartialOrd)]
151    pub enum {name} {{ Float(f64), Null }}
152
153    impl std::str::FromStr for {name}{{
154        type Err = String;
155
156        fn from_str(s: &str) -> Result<Self, Self::Err> {{ 
157            let f = s.parse::<f64>().unwrap();
158            Ok({name}::Float(f))
159        }}
160    }}")
161}
162
163fn gen_int_enum(col_name: &ColName) -> String {
164    let name = &col_name.sanitized.0;
165    format!("
166    #[derive(Debug, Clone, Copy, PartialEq,PartialOrd)]
167    pub enum {name} {{ Int(i64), Null }}
168
169    impl std::str::FromStr for {name}{{
170        type Err = String;
171
172        fn from_str(s: &str) -> Result<Self, Self::Err> {{ 
173            let i = s.parse::<i64>().unwrap();
174            Ok({name}::Int(i))
175        }}
176    }}
177    ")
178}
179
180/*
181let mut columns_enum_from_str = format!("\
182impl std::str::FromStr for {COLUMN_TYPE_ENUM_NAME}{{
183    type Err = String;
184    fn from_str(s: &str) -> Result<Self, Self::Err> {{ 
185        match s{{
186");
187    col_name.iter().for_each(|col_name|{
188        let ColName{raw, sanitized} = col_name;
189        let SanitizedStr(sanitized) = sanitized; 
190        columns_enum_from_str.push_str(&format!("\t\t\t\"{raw}\" => Ok({COLUMN_TYPE_ENUM_NAME}::{sanitized}),\n"));
191    });
192    // last case
193    columns_enum_from_str.push_str("_ => Err(format!(\"Unknown string: '{}'\", s)),\n");
194    columns_enum_from_str.push_str("}\n}\n}"); */