mod codeblocks;
pub use codeblocks::{TextBlock, MatchBlock};
mod enumdecl;
mod propfns;
mod utils;
mod trait_impl;
mod testblock;
use std::fs;
use std::path::{PathBuf, Path};
mod fragments;
mod gen_error;
use gen_error::GenError;
use super::reader::write_lines_to_file;
use self::enumdecl::{generate_enum_decl, generate_get_all_variants_fn, generate_variant_str_fns};
use self::propfns::generate_property_fns;
use self::testblock::generate_testblock;
use self::trait_impl::{generate_impl_block, generate_impl_fmt_display};
use super::{EnumTable, RTypeTrait};
use super::EnumOptions;
pub struct EnumModule<'a> {
pub options: &'a EnumOptions,
pub enumname: String,
pub properties: Vec<String>,
pub imports: Vec<String>,
pub enumdeclaration: TextBlock,
pub get_all_variants_fn: TextBlock,
pub variants_as_str_module: TextBlock,
pub propfn_blocks: Vec<(String, TextBlock)>,
pub impl_block: TextBlock,
pub fmt_block: TextBlock,
pub test_block: TextBlock,
}
impl <'a> EnumModule<'a> {
pub fn new(et: &EnumTable, options: &'a EnumOptions) -> Self {
let make_variant_str_fns = options.gen_variant_str_fns;
let mut imports: Vec<String> = vec![];
for types in &et.parsed_types {
if types.to_typestr_no_ref() == "Regex" {
imports.push("extern crate regex;".to_string());
imports.push("use regex::Regex;".to_string());
imports.push("use std::sync::OnceLock;".to_string());
break;
}
}
EnumModule {
options,
enumname: et.get_name().to_string(),
properties: et.get_properties().to_vec(),
imports,
enumdeclaration: generate_enum_decl(et),
get_all_variants_fn: generate_get_all_variants_fn(et),
variants_as_str_module: if make_variant_str_fns {generate_variant_str_fns(et)} else {TextBlock::new()},
propfn_blocks: generate_property_fns(et),
impl_block: if options.gen_impl_links {generate_impl_block(et, make_variant_str_fns)} else {TextBlock::new()},
fmt_block: generate_impl_fmt_display(et),
test_block: generate_testblock(et),
}
}
pub fn to_lines(&self) -> Vec<String> {
let mut lines = vec![];
for imp in &self.imports {
lines.push(imp.to_string());
}
self.enumdeclaration.collect_lines_into(&mut lines);
self.get_all_variants_fn.collect_lines_into(&mut lines);
self.variants_as_str_module.collect_lines_into(&mut lines);
for blk in &self.propfn_blocks {
blk.1.collect_lines_into(&mut lines);
}
self.impl_block.collect_lines_into(&mut lines);
self.fmt_block.collect_lines_into(&mut lines);
self.test_block.collect_lines_into(&mut lines);
lines
}
pub fn print_configured_to_file(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let filepath = &self.options.path_to_outfile;
let enum_lc = self.enumname.to_ascii_lowercase();
let def_file_name = format!("{}.rs",enum_lc);
let mut parentpath: PathBuf = filepath.to_owned();
let mut printfile: PathBuf = parentpath.join(def_file_name);
if let Some(extension) = filepath.extension() {
if extension == "rs" {
parentpath = filepath.parent().unwrap().to_path_buf();
printfile = filepath.to_path_buf();
} else {
let new_filename = Path::new(filepath.file_stem().unwrap())
.with_extension("rs")
.to_owned();
parentpath = filepath.parent().unwrap().to_path_buf();
printfile = filepath.with_file_name(new_filename);
}
}
if !parentpath.exists() {
fs::create_dir_all(&parentpath)?;
}
let mainfile = printfile.to_str().unwrap();
if self.options.split_files {
let mut nested_path = parentpath.clone();
nested_path.push(enum_lc);
if !nested_path.exists() {
fs::create_dir_all(&nested_path)?;
}
for prop_block in &self.propfn_blocks {
let prop = &prop_block.0;
let prop_lc = prop.to_ascii_lowercase();
let prop_file = format!("{}.rs", prop_lc);
self.imports.push(format!("mod {};", prop_lc));
self.imports.push(format!("use {}::*;", prop_lc));
let prop_file = nested_path.join(prop_file);
let prop_file = prop_file.to_str().unwrap();
let mut lines = vec![format!("use super::{};", &self.enumname)];
prop_block.1.collect_lines_into(&mut lines);
write_lines_to_file(prop_file, lines)?;
}
self.propfn_blocks = vec![];
if self.options.gen_variant_str_fns {
let var_file = "variantstr.rs";
self.imports.push("mod variantstr;".to_string());
self.imports.push("use variantstr::*;".to_string());
let var_file = nested_path.join(var_file);
let var_file = var_file.to_str().unwrap();
let mut lines = vec![format!("use super::{};", &self.enumname)];
self.variants_as_str_module.collect_lines_into(&mut lines);
write_lines_to_file(var_file, lines)?;
self.variants_as_str_module = TextBlock::new();
}
}
let mut full_lines: Vec<String> = self.imports.clone();
self.enumdeclaration.collect_lines_into(&mut full_lines);
if self.options.gen_impl_links {
self.impl_block.collect_lines_into(&mut full_lines);
}
if self.options.gen_variant_str_fns {
self.fmt_block.collect_lines_into(&mut full_lines);
}
self.get_all_variants_fn.collect_lines_into(&mut full_lines);
self.variants_as_str_module.collect_lines_into(&mut full_lines);
for blk in &self.propfn_blocks {
blk.1.collect_lines_into(&mut full_lines);
}
if self.options.gen_impl_links {
self.test_block.collect_lines_into(&mut full_lines);
}
write_lines_to_file(mainfile, full_lines)?;
println!("Successfully generated @ {}", mainfile);
Ok(())
}
}
impl <'a> std::fmt::Display for EnumModule<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Main file: {}", self.enumname)?;
writeln!(f, "Imports: ")?;
for imp in &self.imports {
writeln!(f, "{}", imp)?;
}
write!(f,"{}", self.enumdeclaration)?;
write!(f,"{}", self.get_all_variants_fn)?;
write!(f,"{}", self.variants_as_str_module)?;
for blk in &self.propfn_blocks {
writeln!(f, "{}", blk.1)?;
}
write!(f,"{}", self.impl_block)?;
write!(f,"{}", self.fmt_block)?;
write!(f,"{}", self.test_block)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_enummodule() {
use crate::parser::TableParser;
let rows: Vec<&str> = vec![
"TYPES, &str, (usize,f64), &str",
"MyEnumName, Property1, Property2, Property3",
"Variant1, standard, (0, 3.14), cheap",
"Variant2, medium, (0, 9.82), pricey",
];
let table_parser = TableParser::from_csv_lines(rows).unwrap();
let enumtable = table_parser.to_enumtable().unwrap();
assert_eq!(enumtable.get_col_of_property("Property1"), Some(0));
let options = EnumOptions::default();
let enummodule = EnumModule::new(&enumtable, &options);
println!("{}", enummodule);
}
}