diff --git a/Cargo.lock b/Cargo.lock index 1a1f394..2071b76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,53 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "alchimake" version = "0.1.0" +dependencies = [ + "regex", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" diff --git a/Cargo.toml b/Cargo.toml index 15d9418..df1c848 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1.10.3" diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..e562e8e --- /dev/null +++ b/src/app.rs @@ -0,0 +1,59 @@ +use std::path::PathBuf; +use crate::cli::parse; +use crate::core::conf::Conf; + +pub struct App { + conf: Conf +} + +impl App { + pub fn new() -> Self { + Self { + conf: Conf::new() + } + } + + pub fn init(&mut self) -> Result<(), Box> { + let mut args = parse::Args::new(); + let sys_args: Vec = std::env::args().collect(); + + args.register(&vec!["-h", "--help"]) + .description("show this help") + .callback(Box::new(|_, ctx| { + println!("{}", ctx.help); + std::process::exit(0); + })); + + args.register(&vec!["-v", "--version"]) + .description("show alchimake version") + .callback(Box::new(|_, _| { + println!("Alchimake v0.0.0"); + println!("Copyright (C) 2024 bog"); + println!("Licensed under the GPLv3+ (see LICENSE)"); + std::process::exit(0); + })); + + args.register(&vec!["-s", "--source"]) + .description("set the source directory") + .callback(Box::new(|value, _| { + if let Some(val) = value { + self.conf.src_dir = PathBuf::from(val); + } + })); + + args.register(&vec!["-b", "--build"]) + .description("set the build directory") + .callback(Box::new(|value, _| { + if let Some(val) = value { + self.conf.build_dir = PathBuf::from(val); + } + })); + + args.parse(&sys_args.iter().map(|v| v.as_str()).collect())?; + Ok(()) + } + + pub fn start(&mut self) -> Result<(), Box> { + Ok(()) + } +} diff --git a/src/cli/parse.rs b/src/cli/parse.rs index 4a20143..7bdc71a 100644 --- a/src/cli/parse.rs +++ b/src/cli/parse.rs @@ -23,19 +23,19 @@ pub struct Context { pub help: String } -type Callback = Box, Context)>; +type Callback<'a> = Box, Context) + 'a>; -pub struct Arg { +pub struct Arg<'a> { pub flags: Vec, pub description: Option, - pub callback: Option + pub callback: Option> } -pub struct Args { - args: Vec, +pub struct Args<'a> { + args: Vec>, } -impl Args { +impl<'a> Args<'a> { pub fn new() -> Self { Self { args: vec![] @@ -45,6 +45,7 @@ impl Args { pub fn help(&self) -> String { let mut res = String::from("Usage: alchimake [OPTION]...\n"); res += "OPTIONS:\n"; + for arg in self.args.iter() { if let Some(description) = &arg.description { res += format!( @@ -67,7 +68,7 @@ impl Args { for arg in self.args.iter_mut() { for flag in arg.flags.iter() { if &flag == cli_arg { - if let Some(ref callback) = arg.callback { + if let Some(ref mut callback) = arg.callback { let value = getval(&args, &flag)?; callback(value.clone(), Context { help: help.clone() @@ -99,9 +100,16 @@ impl Args { } pub fn callback(&mut self, - callback: Callback) -> &mut Self{ + callback: Callback<'a>) -> &mut Self{ let idx = self.args.len() - 1; self.args[idx].callback = Some(Box::new(callback)); + self.args.sort_by_cached_key(|a| { + if let Some(val) = a.flags.get(0) { + val.clone() + } else { + String::from("") + } + }); self } } diff --git a/src/core/conf.rs b/src/core/conf.rs new file mode 100644 index 0000000..6f4fb53 --- /dev/null +++ b/src/core/conf.rs @@ -0,0 +1,18 @@ +use std::path::PathBuf; + +#[derive(Debug)] +#[derive(Clone)] +pub struct Conf { + pub src_dir: PathBuf, + pub build_dir: PathBuf +} + +impl Conf { + pub fn new() -> Self { + Self { + src_dir: PathBuf::from("."), + build_dir: PathBuf::from(".") + } + } +} + diff --git a/src/core/eval.rs b/src/core/eval.rs index 6bdca32..ce6944c 100644 --- a/src/core/eval.rs +++ b/src/core/eval.rs @@ -1,27 +1,54 @@ +use regex::Regex; use std::collections::HashMap; use std::error::Error; use crate::core::{ value::Value, expr::Expr, sym::Sym, - cmd::Command + cmd::Command, + conf::Conf }; pub struct Evaluator { sym: Sym, - commands: HashMap> + commands: HashMap>, + conf: Conf } impl Evaluator { - pub fn new(sym: Sym) -> Self { + pub fn new(sym: Sym, conf: Conf) -> Self { Self { sym, - commands: HashMap::new() + commands: HashMap::new(), + conf } } pub fn run(&mut self, expr: &Expr) -> Result> { match expr { + Expr::File(rel_path) => { + fn replace_path(dir: &std::path::Path, prefix: &str, rel_path: &str) + -> Result> { + let re = Regex::new(&format!("{}://([^)]*)", prefix)).unwrap(); + if let Some(path) = re.captures(rel_path) { + Ok(Value::String( + dir.join( + path[1].to_string() + ).to_str().unwrap().to_string(), + )) + } else { + Err(String::from("wrong path").into()) + } + } + + if let Ok(res) = replace_path(&self.conf.src_dir, "src", rel_path) { + Ok(res) + } else if let Ok(res) = replace_path(&self.conf.build_dir, "build", rel_path) { + Ok(res) + } else { + Err(format!("wrong path {}", rel_path).into()) + } + }, Expr::BuiltIn(value) => Ok(value.clone()), Expr::Ident(value) => { if let Some(val) = self.sym.get(value) { @@ -89,14 +116,9 @@ impl Evaluator { } } -// pub struct Evaluator { -// sym: Sym, -// commands: HashMap> -// } -// impl Clone for Evaluator { fn clone(&self) -> Self { - let mut c = Evaluator::new(self.sym.clone()); + let mut c = Evaluator::new(self.sym.clone(), self.conf.clone()); for cmd in self.commands.iter() { c.commands.insert(cmd.0.to_string(), cmd.1.clone_box()); } @@ -112,7 +134,11 @@ mod test { -> TestResult where U: Fn(&mut Evaluator) { let sym = Sym::new(); - let mut eval = Evaluator::new(sym); + let mut conf = Conf::new(); + conf.src_dir = std::path::PathBuf::from("/root/source"); + conf.build_dir = std::path::PathBuf::from("/root/build"); + + let mut eval = Evaluator::new(sym, conf); init(&mut eval); let res = eval.run(&expr)?; @@ -204,4 +230,39 @@ where U: Fn(&mut Evaluator) { Ok(()) } + + #[test] + fn files() -> TestResult { + test_eval( + Expr::File(String::from("build://")), + Value::String(String::from("/root/build/")) + )?; + + test_eval( + Expr::File(String::from("build://hello/world.txt")), + Value::String(String::from("/root/build/hello/world.txt")) + )?; + + test_eval( + Expr::File(String::from("build://../parent")), + Value::String(String::from("/root/build/../parent")) + )?; + + test_eval( + Expr::File(String::from("src://")), + Value::String(String::from("/root/source/")) + )?; + + test_eval( + Expr::File(String::from("src://hello/world.txt")), + Value::String(String::from("/root/source/hello/world.txt")) + )?; + + test_eval( + Expr::File(String::from("src://../parent")), + Value::String(String::from("/root/source/../parent")) + )?; + + Ok(()) + } } diff --git a/src/core/exec.rs b/src/core/exec.rs index dd65390..ec267b2 100644 --- a/src/core/exec.rs +++ b/src/core/exec.rs @@ -5,7 +5,8 @@ use crate::core::{ value::Value, eval::Evaluator, sym::Sym, - cmd::Command + cmd::Command, + conf::Conf }; #[derive(Clone)] @@ -186,7 +187,7 @@ mod test { -> TestResult where T: Fn(&mut Executor) { let sym = Sym::new(); - let eval = Evaluator::new(sym); + let eval = Evaluator::new(sym, Conf::new()); let mut exec = Executor::new(eval); init(&mut exec); diff --git a/src/core/expr.rs b/src/core/expr.rs index a82d925..882e2fd 100644 --- a/src/core/expr.rs +++ b/src/core/expr.rs @@ -6,5 +6,6 @@ use crate::core::value::Value; pub enum Expr { BuiltIn(Value), Ident(String), - Call(String, Vec) + Call(String, Vec), + File(String) } diff --git a/src/core/mod.rs b/src/core/mod.rs index ffe6e6b..1644e51 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -5,3 +5,4 @@ pub mod eval; pub mod sym; pub mod cmd; pub mod exec; +pub mod conf; diff --git a/src/main.rs b/src/main.rs index 7eeb05d..f753c55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,12 @@ mod cli; mod core; +mod app; use crate::cli::parse; +use crate::app::App; fn main() -> Result<(), Box> { - let mut args = parse::Args::new(); - let sys_args: Vec = std::env::args().collect(); - - args.register(&vec!["-h", "--help"]) - .description("show this help") - .callback(Box::new(|_, ctx| { - println!("{}", ctx.help); - std::process::exit(0); - })); - - args.register(&vec!["-v", "--version"]) - .description("show alchimake version") - .callback(Box::new(|_, _| { - println!("Alchimake v0.0.0"); - println!("Copyright (C) 2024 bog"); - println!("Licensed under the GPLv3+ (see LICENSE)"); - std::process::exit(0); - })); - - args.parse(&sys_args.iter().map(|v| v.as_str()).collect())?; - - Ok(()) + let mut app = App::new(); + app.init()?; + app.start() }