csvenum/
code_gen.rs

1
2mod codeblocks;
3pub use codeblocks::{TextBlock, MatchBlock};
4mod enumdecl;
5
6mod propfns;
7
8mod utils;
9
10mod trait_impl;
11
12mod testblock;
13
14use std::fs;
15use std::path::{PathBuf, Path};
16
17mod fragments;
18
19mod gen_error;
20use gen_error::GenError;
21
22use super::reader::write_lines_to_file;
23
24use self::enumdecl::{generate_enum_decl, generate_get_all_variants_fn, generate_variant_str_fns};
25
26use self::propfns::generate_property_fns;
27
28use self::testblock::generate_testblock;
29use self::trait_impl::{generate_impl_block, generate_impl_fmt_display};
30
31use super::{EnumTable, RTypeTrait};
32
33use super::EnumOptions;
34
35
36pub struct EnumModule<'a> {
37    pub options: &'a EnumOptions,
38    pub enumname: String,
39    pub properties: Vec<String>,
40// 0. Imports 
41    pub imports: Vec<String>,
42// 1. Enum declaration with docs
43    pub enumdeclaration: TextBlock,
44// 2. static vec for get all
45    pub get_all_variants_fn: TextBlock,
46// 3. variant string representation
47    pub variants_as_str_module: TextBlock,
48// 4. const & static declares && as_prop from_prop functions
49    pub propfn_blocks: Vec<(String, TextBlock)>,
50// 5. impl MyEnum with as_prop link for pure implementations 
51    pub impl_block: TextBlock,
52// 6. impl std::fmt::Display for MyEnum to display the enum variants with all properties
53    pub fmt_block: TextBlock,
54// 7. test: MyEnum do a barrel roll... convert between all representations
55    pub test_block: TextBlock,
56}
57
58impl <'a> EnumModule<'a> {
59    pub fn new(et: &EnumTable, options: &'a EnumOptions) -> Self {
60        let make_variant_str_fns = options.gen_variant_str_fns;
61        let mut imports: Vec<String> = vec![];
62        for types in &et.parsed_types {
63            if types.to_typestr_no_ref() == "Regex" {
64                imports.push("extern crate regex;".to_string());
65                imports.push("use regex::Regex;".to_string());
66                imports.push("use std::sync::OnceLock;".to_string());
67
68                break;
69            }
70        }
71
72
73        EnumModule { 
74            options,
75            //rootfile, 
76            enumname: et.get_name().to_string(),
77            properties: et.get_properties().to_vec(),
78            imports, 
79            enumdeclaration: generate_enum_decl(et), 
80            get_all_variants_fn: generate_get_all_variants_fn(et), 
81            variants_as_str_module: if make_variant_str_fns {generate_variant_str_fns(et)} else {TextBlock::new()},
82            propfn_blocks: generate_property_fns(et),
83            impl_block:  if options.gen_impl_links {generate_impl_block(et, make_variant_str_fns)} else {TextBlock::new()},
84            fmt_block: generate_impl_fmt_display(et),
85            test_block: generate_testblock(et),  
86        }
87    }
88    /// for printing to a singular file
89    pub fn to_lines(&self) -> Vec<String> {
90        let mut lines = vec![];
91        
92
93        for imp in &self.imports {
94           lines.push(imp.to_string());
95        }
96        self.enumdeclaration.collect_lines_into(&mut lines);
97        self.get_all_variants_fn.collect_lines_into(&mut lines);
98        self.variants_as_str_module.collect_lines_into(&mut lines);
99
100        for blk in &self.propfn_blocks {
101            blk.1.collect_lines_into(&mut lines);
102        }
103        self.impl_block.collect_lines_into(&mut lines);
104        self.fmt_block.collect_lines_into(&mut lines);
105        self.test_block.collect_lines_into(&mut lines);     
106        lines
107    }
108
109    pub fn print_configured_to_file(&mut self) -> Result<(), Box<dyn std::error::Error>> {
110
111        // construct root filename
112        let filepath = &self.options.path_to_outfile;
113        let enum_lc = self.enumname.to_ascii_lowercase();
114        let def_file_name = format!("{}.rs",enum_lc); 
115
116        let mut parentpath: PathBuf = filepath.to_owned();
117        let mut printfile: PathBuf = parentpath.join(def_file_name);
118
119        // check if parent path already contains a filename to write to
120        if let Some(extension) = filepath.extension() {
121            if extension == "rs" {
122                // we already have a file name, lets just use that
123                parentpath = filepath.parent().unwrap().to_path_buf();
124                printfile = filepath.to_path_buf();
125            } else {
126                // we have an invalid extension, 
127                let new_filename = Path::new(filepath.file_stem().unwrap())
128                .with_extension("rs")
129                .to_owned();
130
131                parentpath = filepath.parent().unwrap().to_path_buf();
132                printfile = filepath.with_file_name(new_filename);
133            }
134        }
135        if !parentpath.exists() {
136            fs::create_dir_all(&parentpath)?;
137        }
138
139        let mainfile = printfile.to_str().unwrap();
140        //println!("{}", mainfile);
141
142        // will the functions be split into separate files?
143        if self.options.split_files {
144
145            // Create a new PathBuf for the nested directory
146            let mut nested_path = parentpath.clone();
147            nested_path.push(enum_lc);
148
149            // Create the nested directory if it doesn't exist
150            if !nested_path.exists() {
151                fs::create_dir_all(&nested_path)?;
152            }
153
154
155            for prop_block in &self.propfn_blocks {
156                let prop = &prop_block.0;
157                let prop_lc = prop.to_ascii_lowercase();
158                let prop_file = format!("{}.rs", prop_lc);
159                self.imports.push(format!("mod {};", prop_lc));
160                self.imports.push(format!("use {}::*;", prop_lc));
161                let prop_file = nested_path.join(prop_file);
162                let prop_file = prop_file.to_str().unwrap();
163
164                let mut lines = vec![format!("use super::{};", &self.enumname)];
165                prop_block.1.collect_lines_into(&mut lines);
166                // write to file
167                write_lines_to_file(prop_file, lines)?;
168            }
169            self.propfn_blocks = vec![];
170
171            if self.options.gen_variant_str_fns {
172                let var_file = "variantstr.rs";
173                self.imports.push("mod variantstr;".to_string());
174                self.imports.push("use variantstr::*;".to_string());
175                let var_file = nested_path.join(var_file);
176                let var_file = var_file.to_str().unwrap();
177                let mut lines = vec![format!("use super::{};", &self.enumname)];
178                self.variants_as_str_module.collect_lines_into(&mut lines);
179                write_lines_to_file(var_file, lines)?;
180                self.variants_as_str_module = TextBlock::new();
181            }
182        }
183        
184        let mut full_lines: Vec<String> = self.imports.clone();
185        self.enumdeclaration.collect_lines_into(&mut full_lines);
186
187        if self.options.gen_impl_links {
188            self.impl_block.collect_lines_into(&mut full_lines);
189        }
190
191        if self.options.gen_variant_str_fns {
192            self.fmt_block.collect_lines_into(&mut full_lines);
193        }
194
195        self.get_all_variants_fn.collect_lines_into(&mut full_lines);
196        self.variants_as_str_module.collect_lines_into(&mut full_lines);
197        for blk in &self.propfn_blocks {
198            blk.1.collect_lines_into(&mut full_lines);
199        }
200
201        if self.options.gen_impl_links {
202            self.test_block.collect_lines_into(&mut full_lines);  
203        }
204        write_lines_to_file(mainfile, full_lines)?;
205
206        println!("Successfully generated @ {}", mainfile);
207        Ok(())
208    }
209
210}
211
212impl <'a> std::fmt::Display for EnumModule<'a> {
213    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214
215        writeln!(f, "Main file: {}", self.enumname)?;
216        writeln!(f, "Imports: ")?;
217        for imp in &self.imports {
218            writeln!(f, "{}", imp)?;
219        }
220        write!(f,"{}", self.enumdeclaration)?;
221        write!(f,"{}", self.get_all_variants_fn)?;
222        write!(f,"{}", self.variants_as_str_module)?;
223        for blk in &self.propfn_blocks {
224            writeln!(f, "{}", blk.1)?;
225        }
226        write!(f,"{}", self.impl_block)?;
227        write!(f,"{}", self.fmt_block)?;
228        write!(f,"{}", self.test_block)?;
229        Ok(())
230    }
231} 
232
233
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_make_enummodule() {
241        use crate::parser::TableParser;
242                                                        // if this is enum: MyEnum2 then the as_func will need to return Option<MyEnum2>
243        let rows: Vec<&str> = vec![
244            "TYPES,         &str,       (usize,f64),    &str",
245            "MyEnumName,    Property1,  Property2,      Property3",
246            "Variant1,      standard,   (0, 3.14),      cheap",
247            "Variant2,      medium,     (0, 9.82),      pricey",
248        ];
249 
250        let table_parser = TableParser::from_csv_lines(rows).unwrap();
251        let enumtable = table_parser.to_enumtable().unwrap();
252        assert_eq!(enumtable.get_col_of_property("Property1"), Some(0));
253
254        let options = EnumOptions::default();
255
256        let enummodule = EnumModule::new(&enumtable, &options);
257
258        println!("{}", enummodule);
259
260    }
261
262}