diff --git a/zork++/src/lib/bounds/mod.rs b/zork++/src/lib/bounds/mod.rs index 90315581..0b0c212e 100644 --- a/zork++/src/lib/bounds/mod.rs +++ b/zork++/src/lib/bounds/mod.rs @@ -1,6 +1,7 @@ //! The higher abstractions of the program use core::fmt::Debug; +use std::borrow::Cow; use std::fmt::Display; use std::path::PathBuf; @@ -28,10 +29,10 @@ pub trait TranslationUnit: Display + Debug { fn path(&self) -> PathBuf; /// Outputs the declared file stem for this translation unit - fn file_stem(&self) -> String; + fn file_stem(&self) -> Cow<'_, str>; /// Outputs the declared extension for `self` - fn extension(&self) -> String; + fn extension(&self) -> Cow<'_, str>; /// Outputs the file stem concatenated with the extension for a given tu fn file_with_extension(&self) -> String { diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 319c9e76..3b235d84 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -33,7 +33,7 @@ use walkdir::WalkDir; /// Standalone utility for load from the file system the Zork++ cache file /// for the target [`CppCompiler`] -pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { +pub fn load<'a>(program_data: &'a ZorkModel<'_>, cli_args: &CliArgs) -> Result> { let compiler = program_data.compiler.cpp_compiler; let cache_path = &program_data .build @@ -88,22 +88,14 @@ pub fn save( } #[derive(Serialize, Deserialize, Default)] -pub struct ZorkCache { +pub struct ZorkCache<'a> { pub compiler: CppCompiler, pub last_program_execution: DateTime, - pub compilers_metadata: CompilersMetadata, + pub compilers_metadata: CompilersMetadata<'a>, pub generated_commands: Commands, } -impl ZorkCache { - pub fn new() -> Self { - Self { - compiler: todo!(), - last_program_execution: todo!(), - compilers_metadata: todo!(), - generated_commands: todo!(), - } - } +impl<'a> ZorkCache<'a> { pub fn last_program_execution(&self) -> &DateTime { &self.last_program_execution } @@ -150,7 +142,7 @@ impl ZorkCache { } /// The tasks associated with the cache after load it from the file system - pub fn run_tasks(&mut self, program_data: &ZorkModel<'_>) -> Result<()> { + pub fn run_tasks(&mut self, program_data: &'a ZorkModel<'_>) -> Result<()> { let compiler = program_data.compiler.cpp_compiler; if cfg!(target_os = "windows") && compiler == CppCompiler::MSVC { msvc::load_metadata(self, program_data)? @@ -186,9 +178,13 @@ impl ZorkCache { } } - if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) { + if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) + && program_data.modules.is_some() + { self.compilers_metadata.system_modules = program_data .modules + .as_ref() + .unwrap() .sys_modules .iter() .map(|e| e.to_string()) @@ -303,10 +299,10 @@ impl ZorkCache { /// to avoid recompiling them on every process /// NOTE: This feature should be deprecated and therefore, removed from Zork++ when GCC and /// Clang fully implement the required procedures to build the C++ std library as a module - fn track_system_modules<'a>( + fn track_system_modules<'b: 'a>( // TODO move it to helpers - program_data: &'a ZorkModel<'_>, - ) -> impl Iterator + 'a { + program_data: &'b ZorkModel<'b>, + ) -> impl Iterator + 'b { let root = if program_data.compiler.cpp_compiler == CppCompiler::GCC { Path::new(GCC_CACHE_DIR).to_path_buf() } else { @@ -327,11 +323,18 @@ impl ZorkCache { .expect("Error retrieving metadata") .is_file() { - program_data + program_data // TODO: review this, since it's too late and I am just satisfying the borrow checker .modules - .sys_modules + .as_ref() + .map(|modules| modules.sys_modules.clone()) + .unwrap_or_default() .iter() - .any(|sys_mod| file.file_name().to_str().unwrap().starts_with(sys_mod)) + .any(|sys_mod| { + file.file_name() + .to_str() + .unwrap() + .starts_with(&sys_mod.to_string()) + }) } else { false } @@ -390,20 +393,22 @@ pub struct MainCommandLineDetail { pub type EnvVars = HashMap; #[derive(Deserialize, Serialize, Debug, Default, Clone)] -pub struct CompilersMetadata { +pub struct CompilersMetadata<'a> { + // TODO: apply the same solution a have a fat pointer or better convert them into a Union/enum? // ALL of them must be optional, since only exists - pub msvc: MsvcMetadata, + pub msvc: MsvcMetadata<'a>, pub clang: ClangMetadata, pub gcc: GccMetadata, pub system_modules: Vec, // TODO: This hopefully will dissappear soon + // TODO: Vec of Cow } #[derive(Deserialize, Serialize, Debug, Default, Clone)] -pub struct MsvcMetadata { +pub struct MsvcMetadata<'a> { pub compiler_version: Option, pub dev_commands_prompt: Option, - pub vs_stdlib_path: Option, // std.ixx path for the MSVC std lib location - pub vs_c_stdlib_path: Option, // std.compat.ixx path for the MSVC std lib location + pub vs_stdlib_path: Option>, // std.ixx path for the MSVC std lib location + pub vs_c_stdlib_path: Option>, // std.compat.ixx path for the MSVC std lib location pub stdlib_bmi_path: PathBuf, // BMI byproduct after build in it at the target out dir of // the user pub stdlib_obj_path: PathBuf, // Same for the .obj file @@ -414,8 +419,8 @@ pub struct MsvcMetadata { pub env_vars: EnvVars, } -impl MsvcMetadata { - pub fn is_loaded(&self) -> bool { +impl<'a> MsvcMetadata<'_> { + pub fn is_loaded(&'a self) -> bool { self.dev_commands_prompt.is_some() && self.vs_stdlib_path.is_some() } } @@ -439,6 +444,7 @@ mod msvc { use crate::utils::constants; use color_eyre::eyre::{Context, OptionExt}; use regex::Regex; + use std::borrow::Cow; use std::collections::HashMap; use std::path::Path; @@ -484,13 +490,13 @@ mod msvc { Path::new(msvc.env_vars.get("VCToolsInstallDir").unwrap()).join("modules"); msvc.vs_stdlib_path = Some(SourceFile { path: vs_stdlib_path.clone(), - file_stem: String::from("std"), - extension: compiler.get_default_module_extension().to_string(), + file_stem: Cow::Borrowed("std"), + extension: compiler.default_module_extension(), }); msvc.vs_c_stdlib_path = Some(SourceFile { path: vs_stdlib_path, - file_stem: String::from("std.compat"), - extension: compiler.get_default_module_extension().to_string(), + file_stem: Cow::Borrowed("std.compat"), + extension: compiler.default_module_extension(), }); let modular_stdlib_byproducts_path = Path::new(&program_data.build.output_dir) .join(compiler.as_ref()) diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index a266c01c..3abd69fb 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -184,7 +184,7 @@ pub mod clang_args { } pub(crate) fn add_direct_module_interfaces_dependencies( - dependencies: &[&str], + dependencies: &[Cow], compiler: CppCompiler, out_dir: &Path, arguments: &mut Arguments, @@ -196,7 +196,7 @@ pub mod clang_args { .join(compiler.as_ref()) .join("modules") .join("interfaces") - .join(ifc_dep) + .join::<&str>(ifc_dep) .with_extension(compiler.get_typical_bmi_extension()) .display() ))) diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 40936946..758138b6 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -2,6 +2,7 @@ //! by Zork++ use std::collections::HashMap; +use std::ffi::OsStr; use std::fmt::Debug; use std::slice::Iter; use std::{ @@ -131,7 +132,9 @@ fn execute_command( ) ); - std::process::Command::new(compiler.get_driver(&model.compiler)) + let driver = compiler.get_driver(&model.compiler); + let os_driver = OsStr::new(driver.as_ref()); + std::process::Command::new(os_driver) .args(arguments) .envs(cache.get_process_env_args()) .spawn()? diff --git a/zork++/src/lib/compiler/data_factory.rs b/zork++/src/lib/compiler/data_factory.rs index 25d386ee..f6e9120a 100644 --- a/zork++/src/lib/compiler/data_factory.rs +++ b/zork++/src/lib/compiler/data_factory.rs @@ -2,7 +2,10 @@ //! translation unit, having shared data without replicating it until the final command line must //! be generated in order to be stored (in cache) and executed (in the underlying shell) -use std::{borrow::Cow, path::{Path, PathBuf}}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use serde::{Deserialize, Serialize}; @@ -48,7 +51,10 @@ impl<'a> From<&'a ZorkModel<'_>> for CommonArgs { pub trait CompilerCommonArguments {} impl Default for Box { fn default() -> Self { - Box::new(ClangCommonArgs::default()) // TODO: isn't this a code smell? + Box::::default() // TODO: isn't this a code smell? + // TODO: should we just panic? Or maybe fix the default? Or maybe have an associated + // and pass the compiler to the trait fn? So we can ensure that the default has sense? + // TODO: we can just fix as well the serialization function, removing the default } } @@ -92,10 +98,10 @@ pub struct MsvcCommonArgs { exception_handling_model: Cow<'static, str>, /* no_logo: &'a str, no_compile: &'a str, // TODO: should be in the general and pass in the model? */ - // ref_stdlib: &'static str, // TODO: this are tecnically two args, /reference and the value - // ref_stdlib_compat: &'static str, // TODO: this are tecnically two args, /reference and the value - // TODO: split the dual cases per switches - // TODO: can we have switches like tuples? like switch-value pairs? + // ref_stdlib: &'static str, // TODO: this are tecnically two args, /reference and the value + // ref_stdlib_compat: &'static str, // TODO: this are tecnically two args, /reference and the value + // TODO: split the dual cases per switches + // TODO: can we have switches like tuples? like switch-value pairs? } impl MsvcCommonArgs { pub fn new() -> Self { diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index f8ae9c12..1e7e8f53 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -46,17 +46,19 @@ pub fn build_project<'a>( let mut commands = Commands::new(model, general_args, compiler_specific_common_args); // TODO from cache, and find them here instead from the cache - // Pre-tasks - if model.compiler.cpp_compiler == CppCompiler::GCC && !model.modules.sys_modules.is_empty() { - helpers::build_sys_modules(model, &mut commands, cache) - } - // TODO: add them to the commands DS, so they are together until they're generated // Build the std library as a module build_modular_stdlib(model, cache, &mut commands); // TODO: ward it with an if for only call this fn for the // 1st - Build the modules - process_modules(model, cache, &mut commands)?; + if let Some(modules) = &model.modules { + // Pre-tasks + if model.compiler.cpp_compiler == CppCompiler::GCC && !modules.sys_modules.is_empty() { + helpers::build_sys_modules(model, &mut commands, cache) + } + + process_modules(model, cache, &mut commands)?; + }; // 2nd - Build the non module sources build_sources(model, cache, &mut commands, tests)?; // 3rd - Build the executable or the tests @@ -190,10 +192,20 @@ fn process_modules( commands: &mut Commands, ) -> Result<()> { log::info!("Generating the commands for the module interfaces and partitions..."); - process_module_interfaces(model, cache, &model.modules.interfaces, commands); + process_module_interfaces( + model, + cache, + &model.modules.as_ref().unwrap().interfaces, + commands, + ); log::info!("Generating the commands for the module implementations and partitions..."); - process_module_implementations(model, cache, &model.modules.implementations, commands); + process_module_implementations( + model, + cache, + &model.modules.as_ref().unwrap().implementations, + commands, + ); Ok(()) } @@ -570,7 +582,6 @@ mod sources { let mut arguments = Arguments::default(); arguments.push(model.compiler.language_level_arg()); arguments.extend_from_slice(model.compiler.extra_args()); - arguments.extend_from_slice(model.modules.extra_args()); match compiler { CppCompiler::CLANG => { @@ -626,7 +637,7 @@ mod sources { .join(compiler.as_ref()) .join("modules") .join("implementations") - .join(implementation.file_stem()) + .join::<&str>(&*implementation.file_stem()) .with_extension(compiler.get_obj_file_extension()); commands.add_linker_file_path(&obj_file_path); @@ -646,10 +657,7 @@ mod sources { } } - let command_line = SourceCommandLine::for_translation_unit(implementation, arguments); - // commands.implementations.push(command_line); - - command_line + SourceCommandLine::for_translation_unit(implementation, arguments) } } @@ -685,15 +693,15 @@ mod helpers { let mod_unit = if compiler.eq(&CppCompiler::CLANG) { let mut temp = String::new(); if let Some(partition) = &interface.partition { - temp.push_str(partition.module); + temp.push_str(&partition.module); temp.push('-'); if !partition.partition_name.is_empty() { - temp.push_str(partition.partition_name) + temp.push_str(&partition.partition_name) } else { temp.push_str(&interface.file_stem()) } } else { - temp.push_str(interface.module_name) + temp.push_str(&interface.module_name) } temp } else { @@ -723,7 +731,7 @@ mod helpers { .join(compiler.as_ref()) .join("modules") .join("implementations") - .join(implementation.file_stem()) + .join::<&str>(&*implementation.file_stem()) .with_extension(compiler.get_obj_file_extension()) } @@ -746,21 +754,23 @@ mod helpers { let language_level = model.compiler.language_level_arg(); let sys_modules = model .modules - .sys_modules + .as_ref() + .map(|modules| modules.sys_modules.clone()) + .unwrap_or_default() .iter() .filter(|sys_module| { !cache .compilers_metadata .system_modules .iter() - .any(|s| s.eq(**sys_module)) + .any(|s| s.eq(*sys_module)) }) .map(|sys_module| { let mut v = vec![ language_level.clone(), Argument::from("-x"), Argument::from("c++-system-header"), - Argument::from(*sys_module), + Argument::from(sys_module), ]; match model.compiler.cpp_compiler { @@ -771,7 +781,7 @@ mod helpers { .join(model.compiler.cpp_compiler.as_ref()) .join("modules") .join("interfaces") - .join(sys_module) + .join(sys_module.to_string()) .with_extension( model.compiler.cpp_compiler.get_typical_bmi_extension(), ), @@ -905,7 +915,7 @@ mod helpers { out_dir .join(compiler.as_ref()) .join("sources") - .join(source.file_stem()) + .join::<&str>(&*source.file_stem()) .with_extension(compiler.get_obj_file_extension()) } } diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index a87f4227..d44ce81c 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -1,5 +1,4 @@ //!! The core section to instruct the compiler to work with C++20 modules. The most important are the base path to the interfaces and implementation files -use std::borrow::Cow; use serde::{Deserialize, Serialize}; @@ -63,18 +62,18 @@ use serde::{Deserialize, Serialize}; /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct ModulesAttribute<'a> { - // #[serde(borrow)] - pub base_ifcs_dir: Option>, - // #[serde(borrow)] + #[serde(borrow)] + pub base_ifcs_dir: Option<&'a str>, + #[serde(borrow)] pub interfaces: Option>>, - // #[serde(borrow)] - pub base_impls_dir: Option>, - // #[serde(borrow)] + #[serde(borrow)] + pub base_impls_dir: Option<&'a str>, + #[serde(borrow)] pub implementations: Option>>, - // #[serde(borrow)] - pub sys_modules: Option>>, - // #[serde(borrow)] - pub extra_args: Option>>, + #[serde(borrow)] + pub sys_modules: Option>, + #[serde(borrow)] + pub extra_args: Option>, } /// [`ModuleInterface`] - A module interface structure for dealing @@ -137,14 +136,14 @@ pub struct ModulesAttribute<'a> { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(deny_unknown_fields)] pub struct ModuleInterface<'a> { - // #[serde(borrow)] - pub file: Cow<'a, str>, - // #[serde(borrow)] - pub module_name: Option>, - // #[serde(borrow)] + #[serde(borrow)] + pub file: &'a str, + #[serde(borrow)] + pub module_name: Option<&'a str>, + #[serde(borrow)] pub partition: Option>, - // #[serde(borrow)] - pub dependencies: Option>>, + #[serde(borrow)] + pub dependencies: Option>, } /// [`ModulePartition`] - Type for dealing with the parse work @@ -162,10 +161,10 @@ pub struct ModuleInterface<'a> { /// This option only takes effect with MSVC #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct ModulePartition<'a> { - // #[serde(borrow)] - pub module: Cow<'a, str>, - // #[serde(borrow)] - pub partition_name: Option>, + #[serde(borrow)] + pub module: &'a str, + #[serde(borrow)] + pub partition_name: Option<&'a str>, pub is_internal_partition: Option, } @@ -204,8 +203,8 @@ pub struct ModulePartition<'a> { /// ``` #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct ModuleImplementation<'a> { - // #[serde(borrow)] - pub file: Cow<'a, str>, - // #[serde(borrow)] - pub dependencies: Option>>, + #[serde(borrow)] + pub file: &'a str, + #[serde(borrow)] + pub dependencies: Option>, } diff --git a/zork++/src/lib/config_file/project.rs b/zork++/src/lib/config_file/project.rs index 6e7765cd..edc22c1c 100644 --- a/zork++/src/lib/config_file/project.rs +++ b/zork++/src/lib/config_file/project.rs @@ -1,7 +1,5 @@ //! Metadata about the user's project -use std::borrow::Cow; - use serde::{Deserialize, Serialize}; /// [`ProjectAttribute`] - Metadata about the user's project @@ -41,11 +39,11 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[serde(deny_unknown_fields)] pub struct ProjectAttribute<'a> { - // #[serde(borrow)] - pub name: Cow<'a, str>, - // #[serde(borrow)] - pub authors: Option>>, + #[serde(borrow)] + pub name: &'a str, + #[serde(borrow)] + pub authors: Option>, pub compilation_db: Option, - // #[serde(borrow)] - pub project_root: Option>, + #[serde(borrow)] + pub project_root: Option<&'a str>, } diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 8a1bec2b..fad3b581 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -127,7 +127,7 @@ pub mod worker { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, &program_data.build.output_dir, - program_data.executable.executable_name, + &program_data.executable.executable_name, ), Err(e) => Err(e), } diff --git a/zork++/src/lib/project_model/compiler.rs b/zork++/src/lib/project_model/compiler.rs index e31138c7..7eb9f2dd 100644 --- a/zork++/src/lib/project_model/compiler.rs +++ b/zork++/src/lib/project_model/compiler.rs @@ -17,10 +17,10 @@ pub struct CompilerModel<'a> { impl<'a> CompilerModel<'a> { pub fn language_level(&self) -> Cow<'static, str> { match self.cpp_compiler { - CppCompiler::CLANG | CppCompiler::GCC => - format!("-std=c++{}", self.cpp_standard), + CppCompiler::CLANG | CppCompiler::GCC => format!("-std=c++{}", self.cpp_standard), CppCompiler::MSVC => format!("/std:c++{}", self.cpp_standard), - }.into() + } + .into() } pub fn language_level_arg(&self) -> Argument { @@ -79,12 +79,27 @@ impl CppCompiler { } } - pub fn get_default_module_extension(&self) -> &str { - match *self { + pub fn default_module_extension<'a>(&self) -> Cow<'a, str> { + Cow::Borrowed(match *self { CppCompiler::CLANG => "cppm", CppCompiler::MSVC => "ixx", CppCompiler::GCC => "cc", - } + }) + } + pub fn get_default_module_extension<'a>(&self) -> Cow<'a, str> { + Cow::Borrowed(match *self { + CppCompiler::CLANG => "cppm", + CppCompiler::MSVC => "ixx", + CppCompiler::GCC => "cc", + }) + } + + pub fn typical_bmi_extension(&self) -> Cow<'_, str> { + Cow::Borrowed(match *self { + CppCompiler::CLANG => "pcm", + CppCompiler::MSVC => "ifc", + CppCompiler::GCC => "o", + }) } pub fn get_typical_bmi_extension(&self) -> &str { @@ -95,6 +110,14 @@ impl CppCompiler { } } + #[inline(always)] + pub fn obj_file_extension(&self) -> Cow<'_, str> { + Cow::Borrowed(match *self { + CppCompiler::CLANG | CppCompiler::GCC => "o", + CppCompiler::MSVC => "obj", + }) + } + #[inline(always)] pub fn get_obj_file_extension(&self) -> &str { match *self { @@ -135,7 +158,8 @@ impl AsRef for LanguageLevel { #[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] pub enum StdLib { STDLIBCPP, - #[default] LIBCPP, + #[default] + LIBCPP, } impl fmt::Display for StdLib { diff --git a/zork++/src/lib/project_model/executable.rs b/zork++/src/lib/project_model/executable.rs index 010b2a6c..523d9360 100644 --- a/zork++/src/lib/project_model/executable.rs +++ b/zork++/src/lib/project_model/executable.rs @@ -10,7 +10,7 @@ use super::sourceset::SourceSet; #[derive(Debug, PartialEq, Eq)] pub struct ExecutableModel<'a> { pub executable_name: Cow<'a, str>, - pub sourceset: SourceSet, + pub sourceset: SourceSet<'a>, pub extra_args: Vec, } diff --git a/zork++/src/lib/project_model/mod.rs b/zork++/src/lib/project_model/mod.rs index 34779303..add9d7ff 100644 --- a/zork++/src/lib/project_model/mod.rs +++ b/zork++/src/lib/project_model/mod.rs @@ -19,6 +19,6 @@ pub struct ZorkModel<'a> { pub compiler: CompilerModel<'a>, pub build: BuildModel, pub executable: ExecutableModel<'a>, - pub modules: ModulesModel<'a>, - pub tests: TestsModel, + pub modules: Option>, + pub tests: TestsModel<'a>, } diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index 0f68235a..55e637c2 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -25,8 +25,8 @@ impl<'a> ExtraArgs<'a> for ModulesModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleInterfaceModel<'a> { pub path: PathBuf, - pub file_stem: String, - pub extension: String, + pub file_stem: Cow<'a, str>, + pub extension: Cow<'a, str>, pub module_name: Cow<'a, str>, pub partition: Option>, pub dependencies: Vec>, @@ -44,42 +44,40 @@ impl<'a> fmt::Display for ModuleInterfaceModel<'a> { impl<'a> TranslationUnit for ModuleInterfaceModel<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); - tmp.push(&self.extension); - PathBuf::from(tmp) + self.path + .join::<&str>(&self.file_stem) + .join::<&str>(&self.extension) } fn path(&self) -> PathBuf { self.path.clone() } - fn file_stem(&self) -> String { + fn file_stem(&self) -> Cow<'_, str> { self.file_stem.clone() } - fn extension(&self) -> String { + fn extension(&self) -> Cow<'_, str> { self.extension.clone() } } impl<'a> TranslationUnit for &'a ModuleInterfaceModel<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); - tmp.push(&self.extension); - PathBuf::from(tmp) + self.path + .join::<&str>(&self.file_stem) + .join::<&str>(&self.extension) } fn path(&self) -> PathBuf { self.path.clone() } - fn file_stem(&self) -> String { + fn file_stem(&self) -> Cow<'_, str> { self.file_stem.clone() } - fn extension(&self) -> String { + fn extension(&self) -> Cow<'a, str> { self.extension.clone() } } @@ -91,32 +89,31 @@ pub struct ModulePartitionModel<'a> { pub is_internal_partition: bool, } - impl<'a> From> for ModulePartitionModel<'a> { fn from(value: ModulePartition<'a>) -> Self { Self { - module: value.module, - partition_name: value.partition_name.unwrap_or_default(), + module: Cow::Borrowed(value.module), + partition_name: Cow::Borrowed(value.partition_name.unwrap_or_default()), is_internal_partition: value.is_internal_partition.unwrap_or_default(), } } } -impl<'a> From<&ModulePartition<'a>> for ModulePartitionModel<'a> { - fn from(value: &ModulePartition<'a>) -> &'a Self { - Self { - module: value.module., - partition_name: value.partition_name.unwrap_or_default(), - is_internal_partition: value.is_internal_partition.unwrap_or_default(), - } - } -} +// impl<'a> From<&ModulePartition<'a>> for ModulePartitionModel<'a> { +// fn from(value: &ModulePartition<'a>) -> &'a Self { +// Self { +// module: value.module., +// partition_name: value.partition_name.unwrap_or_default(), +// is_internal_partition: value.is_internal_partition.unwrap_or_default(), +// } +// } +// } #[derive(Debug, PartialEq, Eq)] pub struct ModuleImplementationModel<'a> { pub path: PathBuf, - pub file_stem: String, - pub extension: String, + pub file_stem: Cow<'a, str>, + pub extension: Cow<'a, str>, pub dependencies: Vec>, } @@ -126,44 +123,22 @@ impl<'a> fmt::Display for ModuleImplementationModel<'a> { } } -impl<'a> TranslationUnit for ModuleImplementationModel<'a> { - fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); - tmp.push(&self.extension); - PathBuf::from(tmp) - } - - fn path(&self) -> PathBuf { - self.path.clone() - } - - fn file_stem(&self) -> String { - self.file_stem.clone() - } - - fn extension(&self) -> String { - self.extension.clone() - } -} - impl<'a> TranslationUnit for &'a ModuleImplementationModel<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); - tmp.push(&self.extension); - PathBuf::from(tmp) + self.path + .join::<&str>(&self.file_stem) + .with_extension::<&str>(&self.extension) } fn path(&self) -> PathBuf { self.path.clone() } - fn file_stem(&self) -> String { + fn file_stem(&self) -> Cow<'_, str> { self.file_stem.clone() } - fn extension(&self) -> String { + fn extension(&self) -> Cow<'_, str> { self.extension.clone() } } diff --git a/zork++/src/lib/project_model/project.rs b/zork++/src/lib/project_model/project.rs index 7c8a3c7a..e7905cfa 100644 --- a/zork++/src/lib/project_model/project.rs +++ b/zork++/src/lib/project_model/project.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; #[derive(Debug, PartialEq, Eq)] pub struct ProjectModel<'a> { pub name: Cow<'a, str>, - pub authors: &'a [Cow<'a, str>], + pub authors: Vec>, pub compilation_db: bool, pub project_root: Option>, } diff --git a/zork++/src/lib/project_model/sourceset.rs b/zork++/src/lib/project_model/sourceset.rs index dfb55000..55e232d2 100644 --- a/zork++/src/lib/project_model/sourceset.rs +++ b/zork++/src/lib/project_model/sourceset.rs @@ -1,4 +1,5 @@ use core::fmt; +use std::borrow::Cow; use std::path::{Path, PathBuf}; use crate::bounds::TranslationUnit; @@ -26,56 +27,56 @@ impl File for PathBuf { } } +// TODO: All the trait File impl as well as the trait aren't required anymore + #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] -pub struct SourceFile { +pub struct SourceFile<'a> { pub path: PathBuf, - pub file_stem: String, - pub extension: String, + pub file_stem: Cow<'a, str>, + pub extension: Cow<'a, str>, } -impl TranslationUnit for SourceFile { +impl<'a> TranslationUnit for SourceFile<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); // TODO: use the correct PATH APIs - tmp.push(&self.extension); - PathBuf::from(tmp) + self.path + .join::<&str>(&self.file_stem) + .with_extension::<&str>(&self.extension) } fn path(&self) -> PathBuf { self.path.clone() } - fn file_stem(&self) -> String { + fn file_stem(&self) -> Cow<'_, str> { self.file_stem.clone() } - fn extension(&self) -> String { + fn extension(&self) -> Cow<'_, str> { self.extension.clone() } } -impl TranslationUnit for &SourceFile { +impl<'a> TranslationUnit for &'a SourceFile<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.join(&self.file_stem).into_os_string(); - tmp.push("."); - tmp.push(&self.extension); - PathBuf::from(tmp) + self.path + .join::<&str>(&self.file_stem) + .with_extension::<&str>(&self.extension) } fn path(&self) -> PathBuf { self.path.clone() } - fn file_stem(&self) -> String { + fn file_stem(&self) -> Cow<'_, str> { self.file_stem.clone() } - fn extension(&self) -> String { + fn extension(&self) -> Cow<'_, str> { self.extension.clone() } } -impl fmt::Display for SourceFile { +impl<'a> fmt::Display for SourceFile<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -114,11 +115,11 @@ impl GlobPattern { } #[derive(Debug, PartialEq, Eq)] -pub struct SourceSet { - pub sources: Vec, +pub struct SourceSet<'a> { + pub sources: Vec>, } -impl SourceSet { +impl<'a> SourceSet<'a> { pub fn as_args_to(&self, dst: &mut Vec) -> Result<()> { let args = self.sources.iter().map(|sf| sf.file()).map(Argument::from); diff --git a/zork++/src/lib/project_model/tests.rs b/zork++/src/lib/project_model/tests.rs index e8dd7afb..0261991b 100644 --- a/zork++/src/lib/project_model/tests.rs +++ b/zork++/src/lib/project_model/tests.rs @@ -2,23 +2,24 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; +use std::borrow::Cow; use super::sourceset::SourceSet; #[derive(Debug, PartialEq, Eq)] -pub struct TestsModel { - pub test_executable_name: String, - pub sourceset: SourceSet, +pub struct TestsModel<'a> { + pub test_executable_name: Cow<'a, str>, + pub sourceset: SourceSet<'a>, pub extra_args: Vec, } -impl<'a> ExtraArgs<'a> for TestsModel { +impl<'a> ExtraArgs<'a> for TestsModel<'a> { fn extra_args(&'a self) -> &'a [Argument] { &self.extra_args } } -impl<'a> ExecutableTarget<'a> for TestsModel { +impl<'a> ExecutableTarget<'a> for TestsModel<'a> { fn name(&'a self) -> &'a str { &self.test_executable_name } diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 5a7dd66c..b773112b 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -95,14 +95,22 @@ pub fn build_model<'a>( cli_args: &'a CliArgs, absolute_project_root: &Path, ) -> Result> { - let proj_name = config.project.name.clone(); + let proj_name = config.project.name; let project = assemble_project_model(config.project); let compiler = assemble_compiler_model(config.compiler, cli_args); let build = assemble_build_model(config.build, absolute_project_root); - let executable = assemble_executable_model(proj_name, config.executable, absolute_project_root); + let executable = assemble_executable_model( + Cow::Borrowed(proj_name), + config.executable, + absolute_project_root, + ); let modules = assemble_modules_model(config.modules, absolute_project_root); - let tests = assemble_tests_model(project.name.as_ref(), config.tests, absolute_project_root); + let tests = assemble_tests_model( + Cow::Borrowed(proj_name), + config.tests, + absolute_project_root, + ); Ok(ZorkModel { project, @@ -114,26 +122,27 @@ pub fn build_model<'a>( }) } -fn assemble_project_model<'a>(config: ProjectAttribute<'a>) -> ProjectModel<'a> { +fn assemble_project_model(config: ProjectAttribute) -> ProjectModel { ProjectModel { - name: config.name, + name: Cow::Borrowed(config.name), authors: config .authors - // .as_ref() - // .map_or_else(|| &[] as &[Cow<'a, str>], |auths| auths.as_slice()), - .map_or_else(|| &[] as [Cow<'a, str>], |auths| &*auths), + .as_ref() + .map_or_else(Vec::default, |authors| { + authors + .iter() + .map(|auth| Cow::Borrowed(*auth)) + .collect::>() + }), compilation_db: config.compilation_db.unwrap_or_default(), - project_root: config.project_root, + project_root: config.project_root.map(Cow::Borrowed), } } -fn assemble_compiler_model<'a>( - config: CompilerAttribute, - cli_args: &'a CliArgs, -) -> CompilerModel<'a> { +fn assemble_compiler_model(config: CompilerAttribute, cli_args: &CliArgs) -> CompilerModel { let extra_args = config .extra_args - .map(|args| args.into_iter().map(|arg| Argument::from(arg)).collect()) + .map(|args| args.into_iter().map(Argument::from).collect()) .unwrap_or_default(); CompilerModel { @@ -163,15 +172,15 @@ fn assemble_build_model(config: Option, project_root: &Path) -> //noinspection ALL fn assemble_executable_model<'a>( - project_name: &Cow<'a, str>, + project_name: Cow<'a, str>, config: Option>, project_root: &Path, ) -> ExecutableModel<'a> { let config = config.as_ref(); let executable_name = config - .and_then(|exe| -> Option> {exe.executable_name.clone()}) - .unwrap_or_else(|| project_name.clone()); + .and_then(|exe| -> Option> { exe.executable_name.clone() }) + .unwrap_or(project_name); let sources = config .and_then(|exe| exe.sources.clone()) @@ -181,7 +190,7 @@ fn assemble_executable_model<'a>( let extra_args = config .and_then(|exe| exe.extra_args.as_ref()) - .map(|args| args.iter().map(|arg| Argument::from(arg)).collect()) + .map(|args| args.iter().map(Argument::from).collect()) .unwrap_or_default(); ExecutableModel { @@ -194,84 +203,107 @@ fn assemble_executable_model<'a>( fn assemble_modules_model<'a>( config: Option>, project_root: &Path, -) -> ModulesModel<'a> { - let base_ifcs_dir = config - .as_ref() - .and_then(|modules| modules.base_ifcs_dir) - .unwrap_or(Cow::Borrowed(".")); +) -> Option> { + config.as_ref()?; // early guard + let modules = config.unwrap(); + + let base_ifcs_dir = modules + .base_ifcs_dir + .map(Path::new) + .unwrap_or_else(|| Path::new(".")); - let interfaces = config - .and_then(|modules| modules.interfaces.as_ref()) + let interfaces = modules + .interfaces .map(|ifcs| { - ifcs.iter() - .map(|m_ifc| -> ModuleInterfaceModel<'_> {assemble_module_interface_model(m_ifc, base_ifcs_dir, project_root)}) + ifcs.into_iter() + .map(|m_ifc| -> ModuleInterfaceModel<'_> { + assemble_module_interface_model(m_ifc, base_ifcs_dir, project_root) + }) .collect() }) .unwrap_or_default(); - let base_impls_dir = config - .and_then(|modules| modules.base_impls_dir) - .unwrap_or(Cow::Borrowed(".")); + let base_impls_dir = modules + .base_impls_dir + .map(Path::new) + .unwrap_or_else(|| Path::new(".")); - let implementations = config - .and_then(|modules| modules.implementations.as_ref()) + let implementations = modules + .implementations .map(|impls| { impls - .iter() + .into_iter() .map(|m_impl| { - assemble_module_implementation_model(m_impl, &base_impls_dir, project_root) + assemble_module_implementation_model(m_impl, base_impls_dir, project_root) }) .collect() }) .unwrap_or_default(); - let sys_modules = config - .and_then(|modules| modules.sys_modules.as_ref()) - .map_or_else(Default::default, |headers| headers.clone()); + let sys_modules = modules + .sys_modules + .as_ref() + .map_or_else(Default::default, |headers| { + headers + .iter() + .map(|sys_header| Cow::from(*sys_header)) + .collect() + }); - let extra_args = config - .and_then(|mod_attr| mod_attr.extra_args.as_ref()) + let extra_args = modules // TODO: this has to dissappear from the Zork++ build options + .extra_args + .as_ref() .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) .unwrap_or_default(); - ModulesModel { - base_ifcs_dir: Path::new(&base_ifcs_dir), + Some(ModulesModel { + base_ifcs_dir, interfaces, - base_impls_dir: Path::new(&base_impls_dir), + base_impls_dir, implementations, sys_modules, extra_args, - } + }) } fn assemble_module_interface_model<'a>( - config: &'a ModuleInterface, - base_path: &str, + config: ModuleInterface<'a>, + base_path: &Path, project_root: &Path, ) -> ModuleInterfaceModel<'a> { - let file_path = Path::new(project_root).join(base_path).join(config.file.as_ref()); - let module_name = config.module_name.clone().unwrap_or_else(|| { - Cow::Borrowed(Path::new(config.file.as_ref()) - .file_stem() - .unwrap_or_else(|| panic!("Found ill-formed path on: {}", config.file)) - .to_str() - .unwrap()) - }).to_owned(); - - let dependencies = config.dependencies.clone().unwrap_or_else(|| Vec::with_capacity(0)); // TODO + let cfg_file = config.file; + + let file_path = Path::new(project_root).join(base_path).join(cfg_file); + // let module_name = config.module_name.unwrap_or_else(move || { + // Path::new(&cfg_file) + // .file_stem() + // .unwrap_or_else(|| panic!("Found ill-formed path on: {cfg_file}")) + // .to_string_lossy() + // } // TODO: ensure that this one is valid with modules that contains dots in their name + let module_name = Cow::Borrowed( + config + .module_name + .unwrap_or_else(|| panic!("Found ill-formed module name on: {cfg_file}")), + ); + + let dependencies = config + .dependencies + .map(|deps| deps.into_iter().map(Cow::Borrowed).collect()) + .unwrap_or_default(); let partition = if config.partition.is_none() { None } else { - Some(ModulePartitionModel::from(config.partition.as_ref().unwrap(),)) + Some(ModulePartitionModel::from(config.partition.unwrap())) }; let file_details = utils::fs::get_file_details(&file_path).unwrap_or_else(|_| { panic!("An unexpected error happened getting the file details for {file_path:?}") }); + ModuleInterfaceModel { path: file_details.0, - file_stem: file_details.1, - extension: file_details.2, + file_stem: Cow::from(file_details.1), + extension: Cow::from(file_details.2), module_name, partition, dependencies, @@ -280,19 +312,25 @@ fn assemble_module_interface_model<'a>( fn assemble_module_implementation_model<'a>( config: ModuleImplementation<'a>, - base_path: &str, + base_path: &Path, project_root: &Path, ) -> ModuleImplementationModel<'a> { - let mut dependencies = config.dependencies.unwrap_or_default(); + let mut dependencies = config + .dependencies + .unwrap_or_default() + .into_iter() + .map(Cow::Borrowed) + .collect::>>(); - let file_path = Path::new(project_root).join(base_path).join(config.file.as_ref()); + let file_path = Path::new(project_root).join(base_path).join(config.file); if dependencies.is_empty() { - let last_dot_index = config.file.as_ref().rfind('.'); + // TODO: can't recall what's this, so please, debug it and document it + let last_dot_index = config.file.rfind('.'); if let Some(idx) = last_dot_index { let implicit_dependency = config.file.split_at(idx); dependencies.push(Cow::Owned(implicit_dependency.0.to_owned())) } else { - dependencies.push(config.file); + dependencies.push(Cow::Borrowed(config.file)); } } @@ -302,17 +340,17 @@ fn assemble_module_implementation_model<'a>( ModuleImplementationModel { path: file_details.0, - file_stem: file_details.1, - extension: file_details.2, + file_stem: Cow::Owned(file_details.1), + extension: Cow::Owned(file_details.2), dependencies, } } fn assemble_tests_model<'a>( - project_name: &'a str, + project_name: Cow<'_, str>, config: Option, project_root: &Path, -) -> TestsModel { +) -> TestsModel<'a> { let config = config.as_ref(); let test_executable_name = config.and_then(|exe| exe.test_executable_name).map_or_else( @@ -321,7 +359,8 @@ fn assemble_tests_model<'a>( ); let sources = config - .and_then(|exe| exe.sources.clone()) + .and_then(|exe| exe.sources.as_ref()) + .map(|srcs| srcs.iter().map(|src| Cow::Borrowed(*src)).collect()) .unwrap_or_default(); let sourceset = get_sourceset_for(sources, project_root); @@ -331,13 +370,13 @@ fn assemble_tests_model<'a>( .unwrap_or_default(); TestsModel { - test_executable_name, + test_executable_name: Cow::Owned(test_executable_name), sourceset, extra_args, } } -fn get_sourceset_for(srcs: Vec>, project_root: &Path) -> SourceSet { +fn get_sourceset_for<'a>(srcs: Vec>, project_root: &Path) -> SourceSet<'a> { let sources = srcs .iter() .map(|src| { @@ -359,8 +398,8 @@ fn get_sourceset_for(srcs: Vec>, project_root: &Path) -> SourceSet }); SourceFile { path: file_details.0, - file_stem: file_details.1, - extension: file_details.2, + file_stem: Cow::Owned(file_details.1), + extension: Cow::Owned(file_details.2), } }) .collect(); @@ -402,7 +441,7 @@ mod test { let expected = ZorkModel { project: ProjectModel { name: "Zork++".into(), - authors: &["zerodaycode.gz@gmail.com".into()], + authors: vec!["zerodaycode.gz@gmail.com".into()], compilation_db: false, project_root: None, }, @@ -421,14 +460,14 @@ mod test { sourceset: SourceSet { sources: vec![] }, extra_args: vec![], }, - modules: ModulesModel { + modules: Some(ModulesModel { base_ifcs_dir: Path::new("."), interfaces: vec![], base_impls_dir: Path::new("."), implementations: vec![], sys_modules: vec![], extra_args: vec![], - }, + }), tests: TestsModel { test_executable_name: "Zork++_test".into(), sourceset: SourceSet { sources: vec![] }, @@ -452,7 +491,7 @@ mod test { let expected = ZorkModel { project: ProjectModel { name: "Zork++".into(), - authors: &["zerodaycode.gz@gmail.com".into()], + authors: vec!["zerodaycode.gz@gmail.com".into()], compilation_db: true, project_root: None, }, @@ -471,21 +510,21 @@ mod test { sourceset: SourceSet { sources: vec![] }, extra_args: vec![Argument::from("-Werr")], }, - modules: ModulesModel { + modules: Some(ModulesModel { base_ifcs_dir: Path::new("ifcs"), interfaces: vec![ ModuleInterfaceModel { path: abs_path_for_mock.join("ifcs"), - file_stem: String::from("maths"), - extension: String::from("cppm"), + file_stem: Cow::Borrowed("maths"), + extension: Cow::Borrowed("cppm"), module_name: "maths".into(), partition: None, dependencies: vec![], }, ModuleInterfaceModel { path: abs_path_for_mock.join("ifcs"), - file_stem: String::from("some_module"), - extension: String::from("cppm"), + file_stem: Cow::Borrowed("some_module"), + extension: Cow::Borrowed("cppm"), module_name: "maths".into(), partition: None, dependencies: vec![], @@ -495,22 +534,22 @@ mod test { implementations: vec![ ModuleImplementationModel { path: abs_path_for_mock.join("srcs"), - file_stem: String::from("maths"), - extension: String::from("cpp"), + file_stem: Cow::from("maths"), + extension: Cow::from("cpp"), dependencies: vec!["maths".into()], }, ModuleImplementationModel { path: abs_path_for_mock.join("srcs"), - file_stem: String::from("some_module_impl"), - extension: String::from("cpp"), + file_stem: Cow::from("some_module_impl"), + extension: Cow::from("cpp"), dependencies: vec!["iostream".into()], }, ], sys_modules: vec!["iostream".into()], extra_args: vec![Argument::from("-Wall")], - }, + }), tests: TestsModel { - test_executable_name: "zork_check".to_string(), + test_executable_name: "zork_check".into(), sourceset: SourceSet { sources: vec![] }, extra_args: vec![Argument::from("-pedantic")], },