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 (@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 $($arms)*
24 _ => Err(format!("Valore non riconosciuto: '{}'", s)),
25 }
26 }
27 }
28
29 };
30
31 (@step $name:ident, ($($vars:ident),*), ($($arms:tt)*), [ $($l:literal)|+ => $v:ident, $($rest:tt)* ]) => {
34 create_enum!(
35 @step
36 $name,
37 ($($vars,)* $v), ($($arms)* $($l)|+ => Ok($name::$v),), [ $($rest)* ] );
41 };
42 (@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 (@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 (@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 ($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
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