Tholp's bespoke website generator
at main 7.2 kB view raw
1use crate::types::InputFile; 2use std::{ 3 fs, 4 path::{Path, PathBuf}, 5}; 6use toml::Table; 7 8pub struct Project { 9 pub filegroups: Vec<FileGroup>, 10 //pub settings: ProjectSettings, 11 //pub context: ProjectContext, 12 pub input_folder: PathBuf, 13 pub output_folder: PathBuf, 14 pub global_pre_insert: PathBuf, 15 pub global_post_insert: PathBuf, 16 17 pub filemap: Vec<PathBuf>, // mapped to index 18 pub section_name_map: Vec<String>, 19} 20 21pub struct FileGroup { 22 pub name: String, 23 pub files: Vec<InputFile>, 24 pub pre_insert: PathBuf, 25 pub post_insert: PathBuf, 26 pub process: bool, 27 pub convert_html: bool, 28} 29 30// pub struct ProjectContext { 31// pub input_folder: PathBuf, 32// pub output_folder: PathBuf, 33// pub global_pre_insert: PathBuf, 34// pub global_post_insert: PathBuf, 35 36// pub filemap: Vec<PathBuf>, // mapped to index 37// } 38 39macro_rules! get_table_bool_or_default { 40 ($table:ident, $key:expr, $default:expr) => { 41 $table 42 .get($key) 43 .unwrap_or(&toml::Value::try_from($default).unwrap()) 44 .as_bool() 45 .unwrap_or($default) 46 }; 47} 48 49macro_rules! get_table_string_or_default { 50 ($table:ident, $key:expr, $default:expr) => { 51 // $table 52 // .get($key) 53 // .unwrap_or(&toml::Value::try_from($default).unwrap()) 54 // .as_str() 55 // .unwrap_or($default) 56 if $table.contains_key($key) { 57 $table.get($key).unwrap().as_str().unwrap() 58 } else { 59 $default 60 } 61 }; 62} 63 64pub fn parse_project(tomlpath: &Path) -> Project { 65 let tomlfile = fs::read_to_string(tomlpath).expect("Project file unreadable or missing."); 66 67 let mut project: Project = Project { 68 filegroups: Vec::new(), 69 input_folder: PathBuf::new(), 70 output_folder: PathBuf::new(), 71 global_pre_insert: PathBuf::new(), 72 global_post_insert: PathBuf::new(), 73 filemap: Vec::new(), 74 section_name_map: Vec::new(), 75 }; 76 let config = tomlfile 77 .parse::<Table>() 78 .expect("Project file not in propper toml format"); 79 let settings_section = config["settings"] 80 .as_table() 81 .expect("Project file missing [settings] section"); 82 let filegroups_section = config["fileGroups"] 83 .as_table() 84 .expect("Project file contains no file groups "); 85 86 let project_root = tomlpath 87 .parent() 88 .expect("Project file unreadable or missing."); 89 90 project.input_folder = PathBuf::from(get_table_string_or_default!( 91 settings_section, 92 "inputFolder", 93 "skid" 94 )); 95 96 project.output_folder = PathBuf::from(get_table_string_or_default!( 97 settings_section, 98 "outputFolder", 99 "content" 100 )); 101 102 project.global_pre_insert = project_root.join(get_table_string_or_default!( 103 settings_section, 104 "preInsertGlobal", 105 "" 106 )); 107 project.global_post_insert = project_root.join(get_table_string_or_default!( 108 settings_section, 109 "postInsertGlobal", 110 "" 111 )); 112 113 for (k, v) in filegroups_section { 114 if !v.is_table() { 115 continue; 116 } 117 let filegroup_def: &toml::map::Map<String, toml::Value> = v.as_table().unwrap(); 118 119 let pre_insert = get_table_string_or_default!(filegroup_def, "preInsert", ""); 120 let post_insert = get_table_string_or_default!(filegroup_def, "postInsert", ""); 121 let process = get_table_bool_or_default!(filegroup_def, "process", true); 122 let convert_html = get_table_bool_or_default!(filegroup_def, "convertHTML", true); 123 let extention = get_table_string_or_default!(filegroup_def, "outputExtention", "html"); 124 125 let recurse_find = get_table_bool_or_default!(filegroup_def, "recursiveFind", false); 126 127 let dir = get_table_string_or_default!(filegroup_def, "folder", ""); 128 129 let mut group = FileGroup { 130 files: Vec::new(), 131 name: k.clone(), 132 pre_insert: pre_insert.into(), 133 post_insert: post_insert.into(), 134 process, 135 convert_html, 136 }; 137 138 if filegroup_def.contains_key("files") { 139 let file_array = filegroup_def["files"].as_array().unwrap_or_else(|| { 140 panic!("'files' section of fileGroup.{} needs to be an array", k) 141 }); 142 for file in file_array { 143 let filename = file.as_str().unwrap_or_else(|| { 144 panic!( 145 "'files' section of fileGroup.{} needs to only contain strings", 146 k 147 ) 148 }); 149 150 let mut new_file = crate::types::InputFile::new(); 151 new_file.file_input = project.input_folder.clone(); 152 new_file.file_input.push(filename); 153 154 new_file.file_out = project.output_folder.clone(); 155 new_file.file_out.push(filename); 156 new_file.file_out.set_extension(extention); 157 158 new_file.file_skidout = new_file.file_out.clone(); 159 new_file.file_skidout.set_extension("sko"); 160 161 group.files.push(new_file); 162 } 163 } 164 165 project.filegroups.push(group); 166 } 167 168 return project; 169} 170 171pub trait Indexing { 172 fn index_of_file(&mut self, f: &PathBuf) -> usize; 173 fn file_for_index(&self, i: usize) -> Option<PathBuf>; 174 fn file_for_index_canonical(&self, i: usize) -> Option<&PathBuf>; 175 176 fn index_of_section_name(&mut self, name: &String) -> usize; 177 fn section_name_for_index(&self, index: usize) -> Option<&String>; 178} 179 180impl Indexing for Project { 181 fn index_of_file(&mut self, f: &PathBuf) -> usize { 182 let cannonical = f.canonicalize().unwrap(); 183 let mut index = 0; 184 for p in &self.filemap { 185 if cannonical == *p { 186 return index; 187 } 188 index = index + 1; 189 } 190 self.filemap.push(cannonical); 191 return self.filemap.len() - 1; 192 } 193 194 fn file_for_index(&self, i: usize) -> Option<PathBuf> { 195 if i >= self.filemap.len() { 196 return None; 197 } 198 let path = self.filemap[i].strip_prefix(&self.input_folder.canonicalize().unwrap()); 199 return Some(path.unwrap().to_path_buf()); 200 } 201 202 fn file_for_index_canonical(&self, i: usize) -> Option<&PathBuf> { 203 if i >= self.filemap.len() { 204 return None; 205 } 206 return Some(&self.filemap[i]); 207 } 208 209 // Some weirdly placed + and - 1 because 0 is the default index 210 fn index_of_section_name(&mut self, name: &String) -> usize { 211 let mut index = 0; 212 while index < self.section_name_map.len() { 213 if *name == self.section_name_map[index] { 214 return index + 1; 215 } 216 index += 1; 217 } 218 self.section_name_map.push(name.clone()); 219 return self.section_name_map.len(); 220 } 221 222 fn section_name_for_index(&self, index: usize) -> Option<&String> { 223 if (index - 1) >= self.section_name_map.len() { 224 return None; 225 } 226 return Some(&self.section_name_map[index - 1]); 227 } 228}