diff --git a/configs/crystal/config.toml b/configs/crystal/distro.toml similarity index 100% rename from configs/crystal/config.toml rename to configs/crystal/distro.toml diff --git a/src/distro/config.rs b/src/distro/distro_config.rs similarity index 71% rename from src/distro/config.rs rename to src/distro/distro_config.rs index 59c2800..d16d5aa 100644 --- a/src/distro/config.rs +++ b/src/distro/distro_config.rs @@ -1,11 +1,14 @@ use std::{collections::HashMap, path::PathBuf}; use serde::Deserialize; +use tokio::fs; + +use crate::{error::AppResult, utils::CFG_PATH}; /// The config file of a distro that defines /// how that distro should be installed #[derive(Clone, Debug, Deserialize)] -pub struct Config { +pub struct DistroConfig { /// Metadata about the distro pub distro: DistroMetadata, @@ -36,9 +39,19 @@ pub struct OSConfigMetadata { #[derive(Clone, Debug, Deserialize)] pub struct TaskConfig { /// The name of the config field - pub config_field: String, + pub config_key: String, /// If the task should be skipped if the /// config value of that task is null - pub skip_on_null_config: bool, + pub skip_on_false: bool, +} + +impl DistroConfig { + pub async fn load() -> AppResult { + let path = CFG_PATH.join("distro.toml"); + let contents = fs::read_to_string(path).await?; + let cfg = serde_json::from_str::(&contents)?; + + Ok(cfg) + } } diff --git a/src/distro/mod.rs b/src/distro/mod.rs index cae9435..c9d10a4 100644 --- a/src/distro/mod.rs +++ b/src/distro/mod.rs @@ -1,4 +1,4 @@ -pub mod config; +pub mod distro_config; mod os_config; pub use os_config::*; diff --git a/src/distro/os_config/loader.rs b/src/distro/os_config/loader.rs new file mode 100644 index 0000000..96d50f6 --- /dev/null +++ b/src/distro/os_config/loader.rs @@ -0,0 +1,78 @@ +use std::path::PathBuf; + +use tokio::fs; +use valico::json_schema::Scope; + +use crate::{ + distro::distro_config::DistroConfig, + error::{AppError, AppResult}, + utils::CFG_PATH, +}; + +use super::OSConfig; + +pub struct OSConfigLoader<'a> { + distro_cfg: &'a DistroConfig, + cfg_path: PathBuf, +} + +impl<'a> OSConfigLoader<'a> { + pub fn new(path: PathBuf, distro_cfg: &'a DistroConfig) -> Self { + Self { + distro_cfg, + cfg_path: path, + } + } + + pub async fn load(&self) -> AppResult { + let schema = self.load_extension_schema().await?; + let os_config = OSConfig::load(&self.cfg_path).await?; + Self::validate_config(schema, &os_config)?; + + Ok(os_config) + } + + async fn load_extension_schema(&self) -> AppResult { + let schema_path = self + .distro_cfg + .config + .schema + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| CFG_PATH.join("config.schema.json")); + let contents = fs::read_to_string(schema_path).await?; + let schema = serde_json::from_str(&contents)?; + + Ok(schema) + } + + fn validate_config(schema: serde_json::Value, config: &OSConfig) -> AppResult<()> { + let mut scope = Scope::new(); + let schema = scope.compile_and_return(schema, true)?; + let mut errors = Vec::new(); + + for (key, value) in config.extended.iter() { + let result = schema.validate_in(value, key); + + for error in result.errors { + tracing::error!( + "ConfigError: {} ({}) at {}", + error.get_title(), + error.get_code(), + error.get_path(), + ); + errors.push(error); + } + } + if errors.is_empty() { + Ok(()) + } else { + let msg = errors + .into_iter() + .map(|e| format!("{} ({}) at {}", e.get_title(), e.get_code(), e.get_path())) + .collect::>() + .join("\n"); + Err(AppError::InvalidConfig(msg)) + } + } +} diff --git a/src/distro/os_config/mod.rs b/src/distro/os_config/mod.rs index 69686c6..2608f67 100644 --- a/src/distro/os_config/mod.rs +++ b/src/distro/os_config/mod.rs @@ -1,7 +1,8 @@ mod base_config; -use std::collections::HashMap; +use std::{collections::HashMap, path::Path}; pub use base_config::BaseConfig; +pub mod loader; use embed_nu::{ rusty_value::{ Fields, Float, HashablePrimitive, HashableValue, Integer, Primitive, RustyValue, Struct, @@ -10,6 +11,7 @@ use embed_nu::{ RustyIntoValue, }; use serde::Deserialize; +use tokio::fs; use crate::error::{AppError, AppResult}; @@ -25,7 +27,7 @@ pub struct OSConfig { impl OSConfig { pub fn get_nu_value>(&self, key: K) -> AppResult { - let value = self.into_rusty_value(); + let value = self.clone().into_rusty_value(); let mut fields = if let Value::Struct(Struct { fields, .. }) = value { if let Fields::Named(named) = fields { named @@ -110,3 +112,12 @@ fn json_to_rusty_value(val: serde_json::Value) -> embed_nu::rusty_value::Value { } } } + +impl OSConfig { + pub(crate) async fn load(path: &Path) -> AppResult { + let contents = fs::read_to_string(path).await?; + let cfg = serde_json::from_str::(&contents)?; + + Ok(cfg) + } +} diff --git a/src/error.rs b/src/error.rs index f90ef2e..acf6012 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,9 +24,18 @@ pub enum AppError { #[error("Missing config key {0}")] MissingConfigKey(String), + #[error("The os config is invalid: {0}")] + InvalidConfig(String), + #[error("IO Error: {0}")] Io(#[from] io::Error), #[error("JSON deserialization error {0}")] JSON(#[from] serde_json::Error), + + #[error("The task has been skipped")] + Skipped, + + #[error("Failed to process config schema: {0}")] + ConfigSchema(#[from] valico::json_schema::SchemaError), } diff --git a/src/lib.rs b/src/lib.rs index ddf7a2e..0285a26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,128 +1,21 @@ -use distro::OSConfig; -use error::{AppError, AppResult}; -use scripting::{ - loader::ScriptLoader, - script::{NuScript, Script}, -}; - pub mod error; -pub(crate) mod scripting; pub(crate) mod utils; +use std::path::PathBuf; + +use distro::{distro_config::DistroConfig, loader::OSConfigLoader}; +use error::AppResult; +use task::task_executor::TaskExecutor; pub use utils::generate_script_files; pub mod distro; pub mod task; -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] -pub enum TaskOperation { - Up, - Down, -} - -pub struct TaskExecutor { - config: Option, - loader: ScriptLoader, -} - -macro_rules! task_executors { - ($($function:ident => $task:ident),+) => { - $( - #[tracing::instrument(level = "trace", skip(self))] - pub async fn $function(&self, operation: TaskOperation, cfg: <$task as crate::tasks::Task>::Config) -> AppResult<()> { - match operation { - TaskOperation::Up => self.execute_task::<<$task as crate::tasks::Task>::UpScript>(cfg).await, - TaskOperation::Down => self.execute_task::<<$task as crate::tasks::Task>::DownScript>(cfg).await, - } - } - )+ - } -} - -impl TaskExecutor { - /// Creates a new task executor with a given config - pub fn with_config(config: OSConfig) -> Self { - Self { - config: Some(config), - loader: ScriptLoader::new(), - } - } - - task_executors!( - setup_users => SetupUsersTask, - configure_network => ConfigureNetworkTask, - configure_unakite => ConfigureUnakiteTask, - create_partitions => CreatePartitionsTask, - install_base => InstallBaseTask, - install_bootloader => InstallBootloaderTask, - install_desktop => InstallDesktopTask, - install_extra_packages => InstallExtraPackagesTask, - install_flatpak => InstallFlatpakTask, - install_kernels => InstallKernelsTask, - install_timeshift => InstallTimeshiftTask, - install_zramd => InstallZRamDTask, - setup_root_user => SetupRootUserTask, - configure_locale => ConfigureLocaleTask - ); - - /// Installs the system from the given system configuration - #[tracing::instrument(level = "trace", skip(self))] - pub async fn install_from_config(&self) -> AppResult<()> { - let config = self.config.clone().ok_or(AppError::MissingConfig)?; - let base_config = config.base; - - self.create_partitions(TaskOperation::Up, base_config.partitions) - .await?; - self.install_base(TaskOperation::Up, ()).await?; - self.install_kernels(TaskOperation::Up, base_config.kernels) - .await?; - self.install_bootloader(TaskOperation::Up, base_config.bootloader) - .await?; - self.configure_locale(TaskOperation::Up, base_config.locale) - .await?; - self.configure_network(TaskOperation::Up, base_config.network) - .await?; - - if base_config.enable_zramd { - self.install_zramd(TaskOperation::Up, ()).await?; - } - if base_config.enable_timeshift { - self.install_timeshift(TaskOperation::Up, ()).await?; - } - if base_config.enable_flatpak { - self.install_flatpak(TaskOperation::Up, ()).await?; - } - self.setup_users(TaskOperation::Up, base_config.users) - .await?; - self.setup_root_user(TaskOperation::Up, base_config.root_user) - .await?; - self.install_desktop(TaskOperation::Up, base_config.desktop) - .await?; - self.install_extra_packages(TaskOperation::Up, base_config.extra_packages) - .await?; - - if let Some(unakite) = base_config.unakite { - self.configure_unakite(TaskOperation::Up, unakite).await?; - } - - Ok(()) - } - - async fn execute_task(&self, args: S::Args) -> AppResult<()> { - let script = self.loader.load::()?; - self.execute(script, args.clone()).await?; - - Ok(()) - } +/// Creates a new executor with the given os config for the current distro +pub async fn create_executor(os_cfg_path: PathBuf) -> AppResult { + let distro_config = DistroConfig::load().await?; + let os_config = OSConfigLoader::new(os_cfg_path, &distro_config) + .load() + .await?; - #[inline] - async fn execute(&self, mut script: NuScript, args: S::Args) -> AppResult<()> { - if let Some(cfg) = self.config.as_ref() { - script.set_global_var("TRM_CONFIG", cfg.to_owned()) - } else { - script.set_global_var("TRM_CONFIG", OSConfig::empty()) - } - .set_global_var("TRM_VERSION", env!("CARGO_PKG_VERSION")) - .execute(args) - .await - } + Ok(TaskExecutor::new(os_config, distro_config)) } diff --git a/src/main.rs b/src/main.rs index 02cd85a..813b3cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,8 @@ use args::{Args, Command, GenerateEmptyConfigArgs, GenerateScriptsArgs, InstallFromConfigArgs}; use clap::Parser; use rusty_value::into_json::{EnumRepr, IntoJson, IntoJsonOptions}; -use tokio::{ - fs::{self, OpenOptions}, - io::AsyncReadExt, -}; -use tourmaline::{distro::OSConfig, error::AppResult, generate_script_files, TaskExecutor}; +use tokio::fs; +use tourmaline::{distro::OSConfig, error::AppResult, generate_script_files}; mod args; @@ -23,14 +20,13 @@ async fn main() { .unwrap(); } +/// Installs the distro from a given configuration file async fn install_from_config(args: InstallFromConfigArgs) -> AppResult<()> { - let mut file = OpenOptions::new().read(true).open(args.path).await?; - let mut cfg_contents = String::new(); - file.read_to_string(&mut cfg_contents).await?; - let config: OSConfig = serde_json::from_str(&cfg_contents)?; - - TaskExecutor::with_config(config) - .install_from_config() + tourmaline::create_executor(args.path) + .await? + .with_base_tasks() + .with_custom_tasks() + .execute() .await } diff --git a/src/scripting/loader.rs b/src/scripting/loader.rs deleted file mode 100644 index 6a06305..0000000 --- a/src/scripting/loader.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::path::PathBuf; - -use crate::error::{AppError, AppResult}; - -use super::script::{NuScript, Script}; - -/// A loader for nu script files -pub struct ScriptLoader { - script_dir: PathBuf, -} - -impl ScriptLoader { - /// Creates a new script loader with the default config dir - pub fn new() -> Self { - Self { - script_dir: crate::utils::CFG_PATH.to_owned(), - } - } - - /// Loads the given script file - #[tracing::instrument(level = "trace", skip_all)] - pub fn load(&self) -> AppResult> { - let script_path = self.script_dir.join(S::name()); - - if !script_path.exists() { - Err(AppError::ScriptNotFound(script_path)) - } else { - Ok(NuScript::new(script_path)) - } - } -} diff --git a/src/scripting/mod.rs b/src/scripting/mod.rs deleted file mode 100644 index 27a381d..0000000 --- a/src/scripting/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod loader; -pub mod script; diff --git a/src/scripting/script.rs b/src/scripting/script.rs deleted file mode 100644 index c78f56e..0000000 --- a/src/scripting/script.rs +++ /dev/null @@ -1,84 +0,0 @@ -use core::fmt; -use std::{collections::HashMap, marker::PhantomData, path::PathBuf}; - -use embed_nu::{ - rusty_value::RustyValue, Argument, CommandGroupConfig, ContextBuilder, IntoArgument, IntoValue, - RawValue, Value, -}; -use tokio::fs; - -use crate::error::{AppError, AppResult}; - -/// A trait implemented for a given nu script type to -/// associate arguments -pub trait Script { - type Args: ScriptArgs + fmt::Debug + Clone; - - /// Returns the (expected) name of the script file - /// This function is used by the loader to load the associated file - /// The name needs to include the file extension - fn name() -> &'static str; -} - -/// Script arguments that can be collected in a Vec to -/// be passed to the script -pub trait ScriptArgs: RustyValue { - fn get_args(self) -> Vec; -} - -impl ScriptArgs for T { - fn get_args(self) -> Vec { - match self.into_value() { - Value::List { vals, .. } => vals - .into_iter() - .map(|v| RawValue(v).into_argument()) - .collect(), - val => vec![RawValue(val).into_argument()], - } - } -} - -/// A nu script instance that can be executed -pub struct NuScript { - path: PathBuf, - vars: HashMap, - __phantom: PhantomData, -} - -impl NuScript { - pub(crate) fn new(path: PathBuf) -> Self { - Self { - path, - vars: HashMap::new(), - __phantom: PhantomData, - } - } - - /// Adds a global variable - pub fn set_global_var(&mut self, key: S1, value: V) -> &mut Self { - self.vars.insert(key.to_string(), value.into_value()); - - self - } - - /// Executes the script with the given args - #[tracing::instrument(level = "trace", skip(self))] - pub async fn execute(&self, args: S::Args) -> AppResult<()> { - let mut ctx = ContextBuilder::default() - .with_command_groups(CommandGroupConfig::default().all_groups(true))? - .add_script(self.read_file().await?)? - .build()?; - - if ctx.has_fn("main") { - let pipeline = ctx.call_fn("main", args.get_args())?; - ctx.print_pipeline(pipeline)?; - Ok(()) - } else { - Err(AppError::MissingMain(self.path.clone())) - } - } - - async fn read_file(&self) -> AppResult { - fs::read_to_string(&self.path).await.map_err(AppError::from) - } -} diff --git a/src/task/base_task.rs b/src/task/base_task.rs index d8d8de8..35741ef 100644 --- a/src/task/base_task.rs +++ b/src/task/base_task.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use crate::{distro::OSConfig, error::AppResult}; -use super::{exec_builder::ExecBuilder, Task, TaskTrait}; +use super::{exec_builder::ExecBuilder, TaskTrait}; use embed_nu::IntoValue; use lazy_static::lazy_static; @@ -19,84 +19,104 @@ pub enum BaseTask { SetupUsers, } -impl BaseTask { - fn config_key(&self) -> Option<&'static str> { - let field = match self { - BaseTask::ConfigureLocale => "locale", - BaseTask::ConfigureNetwork => "network", - BaseTask::CreatePartitions => "partitions", - BaseTask::InstallBootloader => "bootloader", - BaseTask::InstallDesktop => "desktop", - BaseTask::InstallExtraPackages => "extra_packages", - BaseTask::SetupRootUser => "root_user", - BaseTask::SetupUsers => "users", - _ => return None, - }; - Some(field) - } +#[derive(Clone, Copy)] +pub struct BaseTaskKeydata { + pub task_name: &'static str, + pub config_key: Option<&'static str>, +} - fn task_name(&self) -> &'static str { +impl BaseTask { + pub fn key_data(&self) -> BaseTaskKeydata { match self { - BaseTask::ConfigureLocale => "configure-locale", - BaseTask::ConfigureNetwork => "configure-network", - BaseTask::CreatePartitions => "create-partitions", - BaseTask::InstallBase => "install-base", - BaseTask::InstallBootloader => "install-bootloader", - BaseTask::InstallDesktop => "install-desktop", - BaseTask::InstallExtraPackages => "install-extra-packages", - BaseTask::SetupRootUser => "setup-root-user", - BaseTask::SetupUsers => "setup-users", + BaseTask::ConfigureLocale => BaseTaskKeydata { + task_name: "configure-locale", + config_key: Some("locale"), + }, + BaseTask::ConfigureNetwork => BaseTaskKeydata { + task_name: "configure-network", + config_key: Some("network"), + }, + BaseTask::CreatePartitions => BaseTaskKeydata { + task_name: "create-partitions", + config_key: Some("partitions"), + }, + BaseTask::InstallBase => BaseTaskKeydata { + task_name: "install-base", + config_key: None, + }, + BaseTask::InstallBootloader => BaseTaskKeydata { + task_name: "install-bootloader", + config_key: Some("bootloader"), + }, + BaseTask::InstallDesktop => BaseTaskKeydata { + task_name: "install-desktop", + config_key: Some("desktop"), + }, + BaseTask::InstallExtraPackages => BaseTaskKeydata { + task_name: "install-extra-packages", + config_key: Some("extra_packages"), + }, + BaseTask::SetupRootUser => BaseTaskKeydata { + task_name: "setup-root-user", + config_key: Some("root_user"), + }, + BaseTask::SetupUsers => BaseTaskKeydata { + task_name: "setup-users", + config_key: Some("users"), + }, } } } impl TaskTrait for BaseTask { - fn up(&self, config: &OSConfig) -> AppResult { - let script = PathBuf::from(self.task_name()).join("up.nu"); + fn up(&self, config: &OSConfig) -> AppResult> { + let key_data = self.key_data(); + let script = PathBuf::from(key_data.task_name).join("up.nu"); - let task_config = if let Some(key) = self.config_key() { + let task_config = if let Some(key) = key_data.config_key { config.get_nu_value(key)? } else { Option::<()>::None.into_value() }; - Ok(ExecBuilder { + Ok(Some(ExecBuilder { script, os_config: config.to_owned(), task_config, - }) + })) } - fn down(&self, config: &OSConfig) -> AppResult { - let script = PathBuf::from(self.task_name()).join("down.nu"); - let task_config = if let Some(key) = self.config_key() { + fn down(&self, config: &OSConfig) -> AppResult> { + let key_data = self.key_data(); + let script = PathBuf::from(key_data.task_name).join("down.nu"); + let task_config = if let Some(key) = key_data.config_key { config.get_nu_value(key)? } else { Option::<()>::None.into_value() }; - Ok(ExecBuilder { + Ok(Some(ExecBuilder { script, os_config: config.to_owned(), task_config, - }) + })) } } lazy_static! { - pub static ref ALL_BASE_TASKS: Vec = get_all_base_tasks(); + pub static ref ALL_BASE_TASKS: Vec = get_all_base_tasks(); } -fn get_all_base_tasks() -> Vec { +fn get_all_base_tasks() -> Vec { vec![ - Task::Base(BaseTask::ConfigureLocale), - Task::Base(BaseTask::ConfigureNetwork), - Task::Base(BaseTask::CreatePartitions), - Task::Base(BaseTask::InstallBase), - Task::Base(BaseTask::InstallBootloader), - Task::Base(BaseTask::InstallDesktop), - Task::Base(BaseTask::InstallExtraPackages), - Task::Base(BaseTask::SetupRootUser), - Task::Base(BaseTask::SetupUsers), + BaseTask::ConfigureLocale, + BaseTask::ConfigureNetwork, + BaseTask::CreatePartitions, + BaseTask::InstallBase, + BaseTask::InstallBootloader, + BaseTask::InstallDesktop, + BaseTask::InstallExtraPackages, + BaseTask::SetupRootUser, + BaseTask::SetupUsers, ] } diff --git a/src/task/custom_task.rs b/src/task/custom_task.rs index b02b93f..e090984 100644 --- a/src/task/custom_task.rs +++ b/src/task/custom_task.rs @@ -9,37 +9,47 @@ pub struct CustomTask { config_key: String, up_script: PathBuf, down_script: PathBuf, + skip_on_false: bool, } impl CustomTask { - pub fn from_name_and_key(name: String, config_key: String) -> Self { + pub fn from_name_and_key(name: String, config_key: String, skip_on_false: bool) -> Self { let base_path = PathBuf::from(name); Self { config_key, up_script: base_path.join("up.nu"), down_script: base_path.join("down.nu"), + skip_on_false, } } } impl TaskTrait for CustomTask { - fn up(&self, config: &OSConfig) -> AppResult { + fn up(&self, config: &OSConfig) -> AppResult> { let task_config = config.get_nu_value(&self.config_key)?; - Ok(ExecBuilder { - script: self.up_script.to_owned(), - os_config: config.to_owned(), - task_config, - }) + if self.skip_on_false && task_config.is_nothing() { + Ok(None) + } else { + Ok(Some(ExecBuilder { + script: self.up_script.to_owned(), + os_config: config.to_owned(), + task_config, + })) + } } - fn down(&self, config: &OSConfig) -> AppResult { + fn down(&self, config: &OSConfig) -> AppResult> { let task_config = config.get_nu_value(&self.config_key)?; - Ok(ExecBuilder { - script: self.down_script.to_owned(), - os_config: config.to_owned(), - task_config, - }) + if self.skip_on_false && task_config.is_nothing() { + Ok(None) + } else { + Ok(Some(ExecBuilder { + script: self.down_script.to_owned(), + os_config: config.to_owned(), + task_config, + })) + } } } diff --git a/src/task/mod.rs b/src/task/mod.rs index 862d5c1..7677d4b 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -7,8 +7,8 @@ pub mod exec_builder; pub mod task_executor; pub trait TaskTrait { - fn up(&self, config: &OSConfig) -> AppResult; - fn down(&self, config: &OSConfig) -> AppResult; + fn up(&self, config: &OSConfig) -> AppResult>; + fn down(&self, config: &OSConfig) -> AppResult>; } #[derive(Clone, Debug)] @@ -19,7 +19,7 @@ pub enum Task { impl TaskTrait for Task { #[inline] - fn up(&self, config: &OSConfig) -> AppResult { + fn up(&self, config: &OSConfig) -> AppResult> { match self { Task::Base(b) => b.up(config), Task::Custom(c) => c.up(config), @@ -27,7 +27,7 @@ impl TaskTrait for Task { } #[inline] - fn down(&self, config: &OSConfig) -> AppResult { + fn down(&self, config: &OSConfig) -> AppResult> { match self { Task::Base(b) => b.down(config), Task::Custom(c) => c.down(config), diff --git a/src/task/task_executor.rs b/src/task/task_executor.rs index 3a39afc..965668e 100644 --- a/src/task/task_executor.rs +++ b/src/task/task_executor.rs @@ -1,20 +1,21 @@ use crate::{ - distro::{config::Config, OSConfig}, + distro::{distro_config::DistroConfig, OSConfig}, error::AppResult, }; use super::{base_task::ALL_BASE_TASKS, custom_task::CustomTask, Task, TaskTrait}; pub struct TaskExecutor { - config: Config, + distro_config: DistroConfig, os_config: OSConfig, tasks: Vec, } impl TaskExecutor { - pub fn new(os_config: OSConfig, config: Config) -> Self { + /// Creates a new task executor with the given OSConfig and Distro Config + pub fn new(os_config: OSConfig, distro_config: DistroConfig) -> Self { Self { - config, + distro_config, os_config, tasks: Vec::new(), } @@ -22,7 +23,11 @@ impl TaskExecutor { /// Adds all base tasks to the executor pub fn with_base_tasks(&mut self) -> &mut Self { - let mut base_tasks = (*ALL_BASE_TASKS).clone(); + let mut base_tasks = (*ALL_BASE_TASKS) + .iter() + .cloned() + .map(Task::Base) + .collect::>(); self.tasks.append(&mut base_tasks); self @@ -31,11 +36,15 @@ impl TaskExecutor { /// Adds all custom tasks to the executor pub fn with_custom_tasks(&mut self) -> &mut Self { let mut custom_tasks = self - .config + .distro_config .tasks .iter() .map(|(name, task)| { - CustomTask::from_name_and_key(name.to_owned(), task.config_field.to_owned()) + CustomTask::from_name_and_key( + name.to_owned(), + task.config_key.to_owned(), + task.skip_on_false, + ) }) .map(Task::Custom) .collect::>(); @@ -47,7 +56,9 @@ impl TaskExecutor { /// Executes all tasks pub async fn execute(&mut self) -> AppResult<()> { for task in &self.tasks { - task.up(&self.os_config)?.exec().await?; + if let Some(up_task) = task.up(&self.os_config)? { + up_task.exec().await? + } } Ok(()) diff --git a/src/utils.rs b/src/utils.rs index de90d28..9a5347c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -16,14 +16,15 @@ pub async fn generate_script_files>(output: P) -> AppResult<()> { let output = output.as_ref(); for task in &*ALL_BASE_TASKS { - task.up() - let script_dir = output.join(&name); + let key_data = task.key_data(); + let name = key_data.task_name; + let script_dir = output.join(name); if !script_dir.exists() { fs::create_dir_all(&script_dir).await?; } - let up_path = output.join(&up_script); - let down_path = output.join(down_script); + let up_path = output.join("up.nu"); + let down_path = output.join("down.nu"); fs::write( &up_path,