diff --git a/.gitignore b/.gitignore index 0d60215..92b4cd8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk +.env diff --git a/Cargo.lock b/Cargo.lock index ef2ddb6..008b46c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -658,6 +658,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dtparse" version = "1.3.0" @@ -3277,6 +3283,7 @@ version = "0.1.0" dependencies = [ "clap", "color-eyre", + "dotenv", "lazy_static", "miette 5.3.0", "nu-cli", @@ -3284,6 +3291,8 @@ dependencies = [ "nu-engine", "nu-parser", "nu-protocol", + "serde", + "serde_json", "thiserror", "tokio", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 49e8d59..74ffdab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ path = "src/main.rs" [dependencies] clap = "3.2.22" color-eyre = "0.6.2" +dotenv = "0.15.0" lazy_static = "1.4.0" miette = "5.3.0" nu-cli = "0.68.1" @@ -22,6 +23,8 @@ nu-command = "0.68.1" nu-engine = "0.68.1" nu-parser = "0.68.1" nu-protocol = "0.68.1" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" thiserror = "1.0.35" tokio = { version = "1.21.1", features = ["rt", "io-std", "io-util", "process", "time", "macros", "tracing", "fs"] } tracing = "0.1.36" diff --git a/configs/crystal/scripts/setup-users.nu b/configs/crystal/scripts/setup-users.nu new file mode 100644 index 0000000..1a89a1a --- /dev/null +++ b/configs/crystal/scripts/setup-users.nu @@ -0,0 +1,9 @@ +def main [cfg] { + $cfg | get users | each {|$it| create_user $it } | ignore +} + +def create_user [user] { + echo "This would create a user with:" + echo $user + echo +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index fafd211..8b855fc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,11 +14,15 @@ pub enum AppError { #[error("Could not find the script file {0}")] ScriptNotFound(PathBuf), + #[diagnostic()] #[error("Could not parse the source file {0}")] ParseError(#[from] nu_parser::ParseError), #[error("Could not find the main mehod in the script file {0}")] MissingMain(PathBuf), + + #[error("Failed to execute script")] + FailedToExecuteScript, } impl From for AppError { diff --git a/src/lib.rs b/src/lib.rs index 2c62f19..6dda5c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,40 +1,29 @@ use error::AppResult; -use scripting::{ - loader::ScriptLoader, - script::{Script, ScriptArgs}, -}; +use scripting::{loader::ScriptLoader, script::JSONArgs}; +use tasks::{SetupUsersScript, UsersConfig}; pub mod error; pub(crate) mod scripting; +pub mod tasks; pub(crate) mod utils; -pub struct TestScript; - -impl Script for TestScript { - type Args = TestScriptArgs; - - fn get_name() -> &'static str { - "test.nu" - } -} - -pub struct TestScriptArgs { - pub msg: String, +pub struct TaskExecutor { + loader: ScriptLoader, } -impl ScriptArgs for TestScriptArgs { - fn get_args(self) -> Vec { - vec![self.msg] +impl TaskExecutor { + pub fn new() -> Self { + Self { + loader: ScriptLoader::new(), + } } -} -pub async fn test_execute() -> AppResult<()> { - let loader = ScriptLoader::new(); - let test_script = loader.load::()?; - - test_script - .execute(TestScriptArgs { - msg: "'Hello World'".to_string(), - }) - .await + /// Sets up user accounts + #[tracing::instrument(level = "debug", skip(self))] + pub async fn setup_users(&self, users_cfg: UsersConfig) -> AppResult<()> { + self.loader + .load::()? + .execute(JSONArgs(users_cfg)) + .await + } } diff --git a/src/main.rs b/src/main.rs index fe0ae21..57c2625 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,28 @@ +use tourmaline::{ + tasks::{User, UsersConfig}, + TaskExecutor, +}; + #[tokio::main(flavor = "current_thread")] async fn main() { color_eyre::install().unwrap(); - tourmaline::test_execute().await.unwrap(); + dotenv::dotenv().unwrap(); + let executor = TaskExecutor::new(); + let user_cfg = UsersConfig { + users: vec![ + User { + name: String::from("test"), + password: String::from("password"), + sudoer: false, + shell: String::from("/bin/zsh"), + }, + User { + name: String::from("test2"), + password: String::from("superpassword"), + sudoer: true, + shell: String::from("/bin/nu"), + }, + ], + }; + executor.setup_users(user_cfg).await.unwrap(); } diff --git a/src/scripting/executor.rs b/src/scripting/executor.rs index e0140b1..663f42c 100644 --- a/src/scripting/executor.rs +++ b/src/scripting/executor.rs @@ -99,15 +99,20 @@ impl NuExecutor { tokio::task::spawn_blocking(move || { // TODO: Create the AST for the call here instead of parsing it from a string let args = format!("main {}", args.join(" ")); - nu_cli::eval_source( + if !nu_cli::eval_source( &mut engine_state, &mut stack, args.as_bytes(), "", PipelineData::new(Span::new(0, 0)), - ); + ) { + Err(AppError::FailedToExecuteScript) + } else { + Ok(()) + } }) - .await; + .await + .unwrap()?; Ok(()) } diff --git a/src/scripting/script.rs b/src/scripting/script.rs index bbef4f9..fbe6d38 100644 --- a/src/scripting/script.rs +++ b/src/scripting/script.rs @@ -1,3 +1,4 @@ +use serde::Serialize; use std::{marker::PhantomData, path::PathBuf}; use crate::error::AppResult; @@ -48,3 +49,17 @@ impl NuScript { .await } } + +pub struct JSONArgs(pub T); + +impl ScriptArgs for JSONArgs { + fn get_args(self) -> Vec { + // TODO: Make this lesss... weird + // Maybe try providing the value directly in the executor + // instead of parsing and wrapping it + vec![format!( + "('{}' | from json)", + serde_json::to_string(&self.0).unwrap() + )] + } +} diff --git a/src/tasks/configure_locale.rs b/src/tasks/configure_locale.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/configure_network.rs b/src/tasks/configure_network.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/create_partitions.rs b/src/tasks/create_partitions.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/install_base.rs b/src/tasks/install_base.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/install_bootloader.rs b/src/tasks/install_bootloader.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/install_desktop.rs b/src/tasks/install_desktop.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs new file mode 100644 index 0000000..50a1c30 --- /dev/null +++ b/src/tasks/mod.rs @@ -0,0 +1,15 @@ +mod configure_locale; +mod configure_network; +mod create_partitions; +mod install_base; +mod install_bootloader; +mod install_desktop; +mod setup_users; + +pub use configure_locale::*; +pub use configure_network::*; +pub use create_partitions::*; +pub use install_base::*; +pub use install_bootloader::*; +pub use install_desktop::*; +pub use setup_users::*; diff --git a/src/tasks/setup_users.rs b/src/tasks/setup_users.rs new file mode 100644 index 0000000..f448455 --- /dev/null +++ b/src/tasks/setup_users.rs @@ -0,0 +1,26 @@ +use serde::Serialize; + +use crate::scripting::script::{JSONArgs, Script}; + +pub struct SetupUsersScript; + +#[derive(Clone, Debug, Serialize)] +pub struct UsersConfig { + pub users: Vec, +} + +#[derive(Clone, Debug, Serialize)] +pub struct User { + pub name: String, + pub password: String, + pub sudoer: bool, + pub shell: String, +} + +impl Script for SetupUsersScript { + type Args = JSONArgs; + + fn get_name() -> &'static str { + "setup-users.nu" + } +} diff --git a/src/utils.rs b/src/utils.rs index e4359e1..67f11dc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,8 @@ -use std::path::PathBuf; +use std::{env, path::PathBuf}; -const CONFIG_DIR: &str = "/etc"; +const DEFAULT_CONFIG_DIR: &str = "/etc"; lazy_static::lazy_static! { - pub static ref CFG_PATH: PathBuf = PathBuf::from(CONFIG_DIR).join("tourmaline"); + pub static ref CFG_PATH: PathBuf = env::var("TRM_CFG_PATH").map(PathBuf::from).unwrap_or_else(|_| PathBuf::from(DEFAULT_CONFIG_DIR).join("tourmaline")); pub static ref SCRIPT_PATH: PathBuf = CFG_PATH.join("scripts"); }