diff --git a/Cargo.lock b/Cargo.lock index 07f693d..9ddab2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3277,6 +3277,8 @@ version = "0.1.0" dependencies = [ "clap", "color-eyre", + "lazy_static", + "miette 5.3.0", "nu-cli", "nu-command", "nu-protocol", diff --git a/Cargo.toml b/Cargo.toml index faf6391..5fde61c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" name = "tourmaline" [[bin]] -name = "tml" +name = "trl" path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,6 +15,8 @@ path = "src/main.rs" [dependencies] clap = "3.2.22" color-eyre = "0.6.2" +lazy_static = "1.4.0" +miette = "5.3.0" nu-cli = "0.68.1" nu-command = "0.68.1" nu-protocol = "0.68.1" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..a59eace --- /dev/null +++ b/src/error.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; + +use miette::Diagnostic; +use thiserror::Error; + +pub type AppResult = std::result::Result; + +#[derive(Error, Diagnostic, Debug)] +pub enum AppError { + #[error("Error while evaluating nu script")] + #[diagnostic()] + Nu(miette::Error), + + #[error("Could not find the script file {0}")] + ScriptNotFound(PathBuf), +} + +impl From for AppError { + fn from(e: miette::Error) -> Self { + Self::Nu(e) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4cb1b02..0956fdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,38 @@ -use scripting::executor::NuExecutor; +use error::AppResult; +use scripting::{ + loader::ScriptLoader, + script::{Script, ScriptArgs}, +}; -mod scripting; +pub mod error; +pub(crate) mod scripting; +pub(crate) mod utils; -pub fn test_execute(script: String) { - NuExecutor::new(script).execute(); +pub struct TestScript; + +impl Script for TestScript { + type Args = TestScriptArgs; + + fn get_name() -> &'static str { + "test.nu" + } +} + +pub struct TestScriptArgs { + pub msg: String, +} + +impl ScriptArgs for TestScriptArgs { + fn get_args(self) -> Vec { + vec![self.msg] + } +} + +pub fn test_execute() -> AppResult<()> { + let loader = ScriptLoader::new(); + let test_script = loader.load::()?; + + test_script.execute(TestScriptArgs { + msg: "'Hello World'".to_string(), + }) } diff --git a/src/main.rs b/src/main.rs index 85dec60..4d5de59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,4 @@ -use std::env::args; - fn main() { - let script_path = args().skip(1).next().unwrap(); - tourmaline::test_execute(script_path); + color_eyre::install().unwrap(); + tourmaline::test_execute().unwrap(); } diff --git a/src/scripting/executor.rs b/src/scripting/executor.rs index 32b65e4..e059765 100644 --- a/src/scripting/executor.rs +++ b/src/scripting/executor.rs @@ -1,6 +1,10 @@ #![allow(unused)] +use std::path::Path; + use nu_protocol::{PipelineData, Span}; +use crate::error::{AppError, AppResult}; + /// An executor for nu scripts pub struct NuExecutor { script_path: String, @@ -8,9 +12,9 @@ pub struct NuExecutor { } impl NuExecutor { - pub fn new(script_path: String) -> Self { + pub fn new>(script_path: P) -> Self { Self { - script_path, + script_path: script_path.as_ref().to_string_lossy().into_owned(), args: Vec::new(), } } @@ -28,7 +32,7 @@ impl NuExecutor { self } - pub fn execute(&mut self) { + pub fn execute(&mut self) -> AppResult<()> { let mut engine_state = nu_command::create_default_context(); let mut stack = nu_protocol::engine::Stack::new(); let input = PipelineData::new(Span::new(0, 0)); @@ -43,6 +47,6 @@ impl NuExecutor { input, false, ) - .unwrap(); + .map_err(AppError::from) } } diff --git a/src/scripting/loader.rs b/src/scripting/loader.rs index e69de29..73f939e 100644 --- a/src/scripting/loader.rs +++ b/src/scripting/loader.rs @@ -0,0 +1,30 @@ +use std::path::PathBuf; + +use crate::error::{AppError, AppResult}; + +use super::script::{NuScript, Script}; + +/// A loader for nu script files +pub struct ScriptLoader { + base_dir: PathBuf, +} + +impl ScriptLoader { + /// Creates a new script loader with the default config dir + pub fn new() -> Self { + Self { + base_dir: crate::utils::SCRIPT_PATH.to_owned(), + } + } + + /// Loads the given script file + pub fn load(&self) -> AppResult> { + let script_path = self.base_dir.join(S::get_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 index bf2a2fc..d2b7930 100644 --- a/src/scripting/mod.rs +++ b/src/scripting/mod.rs @@ -1,2 +1,3 @@ pub mod executor; -mod loader; +pub mod loader; +pub mod script; diff --git a/src/scripting/script.rs b/src/scripting/script.rs new file mode 100644 index 0000000..4ffab57 --- /dev/null +++ b/src/scripting/script.rs @@ -0,0 +1,44 @@ +use std::{marker::PhantomData, path::PathBuf}; + +use crate::error::AppResult; + +use super::executor::NuExecutor; + +/// A trait implemented for a given nu script type to +/// associate arguments +pub trait Script { + type Args: ScriptArgs; + + /// 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 get_name() -> &'static str; +} + +/// Script arguments that can be collected in a Vec to +/// be passed to the script +pub trait ScriptArgs { + fn get_args(self) -> Vec; +} + +/// A nu script instance that can be executed +pub struct NuScript { + path: PathBuf, + __phantom: PhantomData, +} + +impl NuScript { + pub(crate) fn new(path: PathBuf) -> Self { + Self { + path, + __phantom: PhantomData, + } + } + + /// Executes the script with the given args + pub fn execute(&self, args: S::Args) -> AppResult<()> { + NuExecutor::new(&self.path) + .add_args(args.get_args()) + .execute() + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..e4359e1 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,8 @@ +use std::path::PathBuf; + +const CONFIG_DIR: &str = "/etc"; + +lazy_static::lazy_static! { + pub static ref CFG_PATH: PathBuf = PathBuf::from(CONFIG_DIR).join("tourmaline"); + pub static ref SCRIPT_PATH: PathBuf = CFG_PATH.join("scripts"); +}