use std::{ fs::{self, File, Metadata}, io::{self, Write}, path::{Path, PathBuf}, }; use super::resource::{ collect_resources, generate_function_end, generate_function_header, generate_resource_insert, generate_uses, generate_variable_header, generate_variable_return, DEFAULT_VARIABLE_NAME, }; /// Defines the split strategie. pub trait SetSplitStrategie { /// Register next file from resources. fn register(&mut self, path: &Path, metadata: &Metadata); /// Determine, should we split modules now. fn should_split(&self) -> bool; /// Resets internal counters after split. fn reset(&mut self); } /// Split modules by files count. pub struct SplitByCount { current: usize, max: usize, } impl SplitByCount { pub fn new(max: usize) -> Self { Self { current: 0, max } } } impl SetSplitStrategie for SplitByCount { fn register(&mut self, _path: &Path, _metadata: &Metadata) { self.current += 1; } fn should_split(&self) -> bool { self.current >= self.max } fn reset(&mut self) { self.current = 0; } } /// Generate resources for `project_dir` using `filter` /// breaking them into separate modules using `set_split_strategie` (recommended for large > 128 Mb setups). /// /// Result saved in module named `module_name`. It exports /// only one function named `fn_name`. It is then exported from /// `generated_filename`. `generated_filename` is also used to determine /// the parent directory for the module. /// /// in `build.rs`: /// ```rust /// /// use std::{env, path::Path}; /// use pagetop_statics::sets::{generate_resources_sets, SplitByCount}; /// /// fn main() { /// let out_dir = env::var("OUT_DIR").unwrap(); /// let generated_filename = Path::new(&out_dir).join("generated_sets.rs"); /// generate_resources_sets( /// "./tests", /// None, /// generated_filename, /// "sets", /// "generate", /// &mut SplitByCount::new(2), /// "pagetop_statics", /// ) /// .unwrap(); /// } /// ``` /// /// in `main.rs`: /// ```rust /// include!(concat!(env!("OUT_DIR"), "/generated_sets.rs")); /// /// fn main() { /// let generated_file = generate(); /// /// assert_eq!(generated_file.len(), 4); /// /// } /// ``` pub fn generate_resources_sets( project_dir: P, filter: Option bool>, generated_filename: G, module_name: &str, fn_name: &str, set_split_strategie: &mut S, crate_name: &str, ) -> io::Result<()> where P: AsRef, G: AsRef, S: SetSplitStrategie, { let resources = collect_resources(&project_dir, filter)?; let mut generated_file = File::create(&generated_filename)?; let module_dir = generated_filename.as_ref().parent().map_or_else( || PathBuf::from(module_name), |parent| parent.join(module_name), ); fs::create_dir_all(&module_dir)?; let mut module_file = File::create(module_dir.join("mod.rs"))?; generate_uses(&mut module_file, crate_name)?; writeln!( module_file, " use ::{crate_name}::StaticResource; use ::std::collections::HashMap;" )?; let mut modules_count = 1; let mut set_file = create_set_module_file(&module_dir, modules_count)?; let mut should_split = set_split_strategie.should_split(); for resource in &resources { let (path, metadata) = &resource; if should_split { set_split_strategie.reset(); modules_count += 1; generate_function_end(&mut set_file)?; set_file = create_set_module_file(&module_dir, modules_count)?; } set_split_strategie.register(path, metadata); should_split = set_split_strategie.should_split(); generate_resource_insert(&mut set_file, &project_dir, DEFAULT_VARIABLE_NAME, resource)?; } generate_function_end(&mut set_file)?; for module_index in 1..=modules_count { writeln!(module_file, "mod set_{module_index};")?; } generate_function_header(&mut module_file, fn_name, crate_name)?; generate_variable_header(&mut module_file, DEFAULT_VARIABLE_NAME)?; for module_index in 1..=modules_count { writeln!( module_file, "set_{module_index}::generate(&mut {DEFAULT_VARIABLE_NAME});", )?; } generate_variable_return(&mut module_file, DEFAULT_VARIABLE_NAME)?; generate_function_end(&mut module_file)?; writeln!( generated_file, "mod {module_name}; pub use {module_name}::{fn_name};", )?; Ok(()) } fn create_set_module_file(module_dir: &Path, module_index: usize) -> io::Result { let mut set_module = File::create(module_dir.join(format!("set_{module_index}.rs")))?; writeln!( set_module, "#[allow(clippy::wildcard_imports)] use super::*; #[allow(clippy::unreadable_literal)] pub(crate) fn generate({DEFAULT_VARIABLE_NAME}: &mut HashMap<&'static str, StaticResource>) {{", )?; Ok(set_module) }