From 620a9eed20f997c9a10bcc301cb9144294b8dee1 Mon Sep 17 00:00:00 2001 From: tiye Date: Sun, 7 Jul 2024 00:46:03 +0800 Subject: [PATCH] breaking refactor with argh; tag 0.9.0-a1 --- .github/workflows/publish.yaml | 2 +- .github/workflows/test.yaml | 2 +- .gitignore | 2 + Cargo.lock | 115 +++++------------------- Cargo.toml | 4 +- README.md | 14 +-- package.json | 10 +-- src/bin/bundle_calcit.rs | 71 ++++++--------- src/bin/calcit_deps.rs | 92 +++++++------------ src/bin/cr.rs | 100 ++++++++------------- src/cli_args.rs | 158 ++++++++++++++------------------- 11 files changed, 206 insertions(+), 364 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index e5aef17..685098c 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -33,7 +33,7 @@ jobs: run: > yarn && yarn tsc - && cargo run --bin cr calcit/test.cirru --emit-js --once + && cargo run --bin cr calcit/test.cirru --once js && ln -s ../../ node_modules/@calcit/procs && cp -v scripts/main.mjs js-out/ && node js-out/main.mjs diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2d0b818..46d6d74 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -43,7 +43,7 @@ jobs: run: > yarn && yarn tsc - && cargo run --bin cr calcit/test.cirru --emit-js --once + && cargo run --bin cr calcit/test.cirru --once js && ln -s ../../ node_modules/@calcit/procs && cp -v scripts/main.mjs js-out/ && node js-out/main.mjs diff --git a/.gitignore b/.gitignore index c26708f..113ee4d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ builds/ .DS_Store /profile.json + +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index baf6bbc..807f7df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,62 +3,44 @@ version = 3 [[package]] -name = "anstream" -version = "0.6.14" +name = "archery" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "487955f60962765486ce000015a3492ca45c34a2ebbf12bc0aa2b5110ca6e7d2" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", + "static_assertions", + "triomphe", ] [[package]] -name = "anstyle" -version = "1.0.7" +name = "argh" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" - -[[package]] -name = "anstyle-parse" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ - "utf8parse", + "argh_derive", + "argh_shared", ] [[package]] -name = "anstyle-query" -version = "1.0.3" +name = "argh_derive" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", + "argh_shared", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "archery" -version = "1.1.0" +name = "argh_shared" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487955f60962765486ce000015a3492ca45c34a2ebbf12bc0aa2b5110ca6e7d2" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" dependencies = [ - "static_assertions", - "triomphe", + "serde", ] [[package]] @@ -94,11 +76,11 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "calcit" -version = "0.8.59" +version = "0.9.0-a1" dependencies = [ + "argh", "cirru_edn", "cirru_parser", - "clap", "colored", "ctrlc", "dirs", @@ -160,39 +142,6 @@ dependencies = [ "widestring", ] -[[package]] -name = "clap" -version = "4.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "colorchoice" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" - [[package]] name = "colored" version = "2.1.0" @@ -323,12 +272,6 @@ dependencies = [ "libc", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" - [[package]] name = "kqueue" version = "1.0.8" @@ -534,12 +477,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "strum" version = "0.25.0" @@ -608,12 +545,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "virtue" version = "0.0.13" diff --git a/Cargo.toml b/Cargo.toml index fec6915..1f365fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcit" -version = "0.8.59" +version = "0.9.0-a1" authors = ["jiyinyiyong "] edition = "2021" license = "MIT" @@ -24,7 +24,7 @@ cirru_edn = "0.6.11" # cirru_edn = { path = "/Users/chenyong/repo/cirru/edn.rs" } cirru_parser = "0.1.31" # cirru_parser = { path = "/Users/chenyong/repo/cirru/parser.rs" } -clap = "4.5.7" +argh = "0.1.12" dirs = "5.0.1" lazy_static = "1.4.0" notify = "6.1.1" diff --git a/README.md b/README.md index 02bff21..bb1d98b 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ To use Calcit in GitHub Actions, try [setup-cr](https://github.com/calcit-lang/s Snippets evaling: ```bash -cr -e 'range 100' +cr eval 'range 100' ``` multi-lines snippet: ```bash -cr -e ' +cr eval ' println "|a demo" @@ -85,8 +85,8 @@ cr compact.cirru --entry server It compiles to JavaScript and runs in consistet semantics. However it might require a lot of JavaScript interop. ```bash -cr compact.cirru --emit-js # compile to js -cr compact.cirru --emit-js --emit-path=out/ # compile to js and save in `out/` +cr compact.cirru js # compile to js +cr compact.cirru js --emit-path=out/ # compile to js and save in `out/` ``` By default, js code is generated to `js-out/`. You will need Vite or Node to run it, from an entry file: @@ -139,12 +139,12 @@ I use these commands to run local examples: cargo run --bin cr -- calcit/test.cirru -1 # run tests in Node.js -cargo run --bin cr -- calcit/test.cirru --emit-js -1 && yarn try-js +cargo run --bin cr -- calcit/test.cirru -1 js && yarn try-js # run snippet -cargo run --bin cr -- -e 'range 100' +cargo run --bin cr -- eval 'range 100' -cr compact.cirru --emit-ir # compiles intermediate representation into program-ir.cirru +cr compact.cirru -1 ir # compiles intermediate representation into program-ir.cirru ``` - [Cirru Parser](https://github.com/Cirru/parser.rs) for indentation-based syntax parsing. diff --git a/package.json b/package.json index c23445f..a4fcfae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@calcit/procs", - "version": "0.8.59", + "version": "0.9.0-a1", "main": "./lib/calcit.procs.mjs", "devDependencies": { "@types/node": "^20.11.28", @@ -10,12 +10,12 @@ "compile": "rm -rfv lib/* && tsc", "procs-link": "ln -s ../../ node_modules/@calcit/procs", "cp-mac": "cargo build --release && rm -rfv builds/* && node scripts/cp-version.js && scp builds/* rsync-user@calcit-lang.org:/web-assets/repo/calcit-lang/binaries/macos/", - "eval": "cargo run --bin cr -- -e", + "eval": "cargo run --bin cr -- eval", "check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir", "try-rs": "cargo run --bin cr -- calcit/test.cirru -1", - "try-js-brk": "cargo run --bin cr -- calcit/test.cirru --emit-js -1 && node --inspect-brk js-out/main.mjs", - "try-js": "cargo run --bin cr -- calcit/test.cirru --emit-js -1 && node js-out/main.mjs", - "try-ir": "cargo run --bin cr -- calcit/test.cirru --emit-ir -1" + "try-js-brk": "cargo run --bin cr -- calcit/test.cirru -1 js && node --inspect-brk js-out/main.mjs", + "try-js": "cargo run --bin cr -- calcit/test.cirru -1 js && node js-out/main.mjs", + "try-ir": "cargo run --bin cr -- calcit/test.cirru -1 js" }, "repository": { "type": "git", diff --git a/src/bin/bundle_calcit.rs b/src/bin/bundle_calcit.rs index 231184b..0767210 100644 --- a/src/bin/bundle_calcit.rs +++ b/src/bin/bundle_calcit.rs @@ -8,6 +8,8 @@ use std::{ sync::Arc, }; +use argh::FromArgs; + use calcit::snapshot::{ChangesDict, CodeEntry}; use calcit::snapshot::{FileChangeInfo, FileInSnapShot}; @@ -22,10 +24,13 @@ use cirru_edn::{Edn, EdnMapView, EdnRecordView, EdnTag}; use cirru_parser::Cirru; pub fn main() -> io::Result<()> { - let cli_matches = parse_cli(); - let verbose = cli_matches.get_flag("verbose"); - let base_dir = Path::new(cli_matches.get_one::("src").expect("src")); - let out_path = Path::new(cli_matches.get_one::("out").expect("out")); + let cli_args: TopLevelBundleCalcit = argh::from_env(); + + let verbose = cli_args.verbose; + let src = cli_args.src.as_deref().unwrap_or("src"); + let base_dir = Path::new(&src); + let out = cli_args.out.as_deref().unwrap_or("./"); + let out_path = Path::new(&out); let out_file = match out_path.extension() { Some(ext) => { let ext_str = ext.to_str().expect("ext"); @@ -38,12 +43,9 @@ pub fn main() -> io::Result<()> { None => out_path.join("compact.cirru"), }; let inc_file_path = out_path.join(".compact-inc.cirru"); - let no_watcher = cli_matches.get_flag("once"); + let no_watcher = cli_args.once; - let package_file = Path::new(cli_matches.get_one::("src").expect("src")) - .parent() - .expect("parent path") - .join("package.cirru"); + let package_file = base_dir.parent().expect("parent path").join("package.cirru"); perform_compaction(base_dir, &package_file, &out_file, &inc_file_path, verbose)?; @@ -272,42 +274,21 @@ fn load_files_to_edn(package_file: &Path, base_dir: &Path, verbose: bool) -> Res pub const CALCIT_VERSION: &str = env!("CARGO_PKG_VERSION"); -fn parse_cli() -> clap::ArgMatches { - clap::Command::new("Calcit Bundle") - .version(CALCIT_VERSION) - .author("Jon. ") - .about("Calcit Bundler") - .arg( - clap::Arg::new("src") - .help("source folder") - .default_value("src/") - .short('s') - .long("src") - .num_args(1), - ) - .arg( - clap::Arg::new("out") - .help("output folder") - .default_value("./") // TODO a better default value - .short('o') - .long("out") - .num_args(1), - ) - .arg( - clap::Arg::new("verbose") - .help("verbose mode") - .short('v') - .long("verbose") - .num_args(0), - ) - .arg( - clap::Arg::new("once") - .help("run without watcher") - .short('1') - .long("once") - .num_args(0), - ) - .get_matches() +#[derive(FromArgs, PartialEq, Debug)] +/// Top-level command. +pub struct TopLevelBundleCalcit { + /// source folder + #[argh(option, short = 's')] + pub src: Option, + /// output folder + #[argh(option, short = 'o')] + pub out: Option, + /// verbose mode + #[argh(switch, short = 'v')] + pub verbose: bool, + /// run without watcher + #[argh(switch, short = '1')] + pub once: bool, } // simulate an IO error with String diff --git a/src/bin/calcit_deps.rs b/src/bin/calcit_deps.rs index b630cf2..b06c2ab 100644 --- a/src/bin/calcit_deps.rs +++ b/src/bin/calcit_deps.rs @@ -5,6 +5,8 @@ mod git; +use argh::{self, FromArgs}; + use cirru_edn::Edn; use colored::*; use git::*; @@ -43,39 +45,19 @@ impl TryFrom for PackageDeps { } } -#[derive(Debug, Clone, PartialEq, Eq)] -struct CliArgs { - input: String, - ci: bool, - verbose: bool, - local_debug: bool, - pull_branch: bool, -} - pub fn main() -> Result<(), String> { // parse deps.cirru - let cli_matches = parse_cli(); - - let options: CliArgs = CliArgs { - input: cli_matches - .get_one::("input") - .unwrap_or(&"deps.cirru".to_string()) - .to_string(), - ci: cli_matches.get_flag("ci"), - verbose: cli_matches.get_flag("verbose"), - local_debug: cli_matches.get_flag("local_debug"), - pull_branch: cli_matches.get_flag("pull_branch"), - }; + let cli_args: TopLevelCaps = argh::from_env(); // if file exists - if Path::new(&options.input).exists() { - let content = fs::read_to_string(&options.input).map_err(|e| e.to_string())?; + if Path::new(&cli_args.input).exists() { + let content = fs::read_to_string(&cli_args.input).map_err(|e| e.to_string())?; let parsed = cirru_edn::parse(&content)?; let deps: PackageDeps = parsed.try_into()?; - download_deps(deps.dependencies, &options)?; + download_deps(deps.dependencies, cli_args)?; Ok(()) } else if Path::new("package.cirru").exists() { @@ -84,16 +66,16 @@ pub fn main() -> Result<(), String> { let parsed = cirru_edn::parse(&content)?; let deps: PackageDeps = parsed.try_into()?; - download_deps(deps.dependencies, &options)?; + download_deps(deps.dependencies, cli_args)?; Ok(()) } else { - eprintln!("Error: no deps.cirru found!"); + eprintln!("Error: no {} found!", cli_args.input); std::process::exit(1); } } -fn download_deps(deps: HashMap, Arc>, options: &CliArgs) -> Result<(), String> { +fn download_deps(deps: HashMap, Arc>, options: TopLevelCaps) -> Result<(), String> { // ~/.config/calcit/modules/ let clone_target = if options.local_debug { println!("{}", " [DEBUG] local debug mode, cloning to test-modules/".yellow()); @@ -118,8 +100,9 @@ fn download_deps(deps: HashMap, Arc>, options: &CliArgs) -> Result let modules_dir = modules_dir.clone(); // TODO too many threads do not make it faster though + let options2 = options.clone(); let ret = thread::spawn(move || { - let ret = handle_path(modules_dir, version, options, org_and_folder); + let ret = handle_path(modules_dir, version, &options2, org_and_folder); if let Err(e) = ret { err_println(format!("{}\n", e)); } @@ -133,7 +116,7 @@ fn download_deps(deps: HashMap, Arc>, options: &CliArgs) -> Result Ok(()) } -fn handle_path(modules_dir: PathBuf, version: Arc, options: CliArgs, org_and_folder: Arc) -> Result<(), String> { +fn handle_path(modules_dir: PathBuf, version: Arc, options: &TopLevelCaps, org_and_folder: Arc) -> Result<(), String> { // check if exists let (_org, folder) = org_and_folder.split_once('/').ok_or("invalid name")?; // split with / into (org,folder) @@ -216,38 +199,25 @@ fn handle_path(modules_dir: PathBuf, version: Arc, options: CliArgs, org_an pub const CALCIT_VERSION: &str = env!("CARGO_PKG_VERSION"); -fn parse_cli() -> clap::ArgMatches { - clap::Command::new("Calcit Deps") - .version(CALCIT_VERSION) - .author("Jon. ") - .about("Calcit Deps") - .arg(clap::Arg::new("input").help("entry file path").default_value("deps.cirru").index(1)) - .arg( - clap::Arg::new("verbose") - .help("verbose mode") - .short('v') - .long("verbose") - .num_args(0), - ) - .arg( - clap::Arg::new("pull_branch") - .help("pull branch in the repo") - .long("pull-branch") - .num_args(0), - ) - .arg( - clap::Arg::new("ci") - .help("CI mode loads shallow repo via HTTPS") - .long("ci") - .num_args(0), - ) - .arg( - clap::Arg::new("local_debug") - .help("Debug mode, clone to test-modules/") - .long("local-debug") - .num_args(0), - ) - .get_matches() +#[derive(FromArgs, PartialEq, Debug, Clone)] +/// Top-level command. +struct TopLevelCaps { + /// verbose mode + #[argh(switch, short = 'v')] + verbose: bool, + /// pull branch in the repo + #[argh(switch)] + pull_branch: bool, + /// CI mode loads shallow repo via HTTPS + #[argh(switch)] + ci: bool, + /// debug mode, clone to test-modules/ + #[argh(switch)] + local_debug: bool, + + /// input file + #[argh(positional, default = "\"deps.cirru\".to_owned()")] + input: String, } fn dim_println(msg: String) { diff --git a/src/bin/cr.rs b/src/bin/cr.rs index 53aba2a..c7d7beb 100644 --- a/src/bin/cr.rs +++ b/src/bin/cr.rs @@ -11,6 +11,7 @@ mod injection; use calcit::calcit::LocatedWarning; use calcit::call_stack::CallStackList; +use calcit::cli_args::{CalcitCommand, ToplevelCalcit}; use calcit::snapshot::ChangesDict; use calcit::util::string::strip_shebang; use dirs::home_dir; @@ -22,17 +23,6 @@ use calcit::{ ProgramEntries, }; -#[derive(Debug, Clone)] -pub struct CLIOptions { - entry_path: PathBuf, - emit_path: String, - reload_libs: bool, - emit_js: bool, - emit_ir: bool, - disable_stack: bool, - skip_arity_check: bool, -} - fn main() -> Result<(), String> { builtins::effects::init_effects_states(); @@ -40,23 +30,10 @@ fn main() -> Result<(), String> { #[cfg(not(target_arch = "wasm32"))] injection::inject_platform_apis(); - let cli_matches = cli_args::parse_cli(); - let cli_options = CLIOptions { - // has default value - entry_path: Path::new(cli_matches.get_one::("input").expect("input file")).to_owned(), - emit_path: cli_matches - .get_one::("emit-path") - .unwrap_or(&"js-out".to_owned()) - .to_owned() - .to_owned(), - reload_libs: cli_matches.get_flag("reload-libs"), - emit_js: cli_matches.get_flag("emit-js"), - emit_ir: cli_matches.get_flag("emit-ir"), - disable_stack: cli_matches.get_flag("disable-stack"), - skip_arity_check: cli_matches.get_flag("skip-arity-check"), - }; - let mut eval_once = cli_matches.get_flag("once"); - let assets_watch = cli_matches.get_one::("watch-dir"); + let cli_args: ToplevelCalcit = argh::from_env(); + + let mut eval_once = cli_args.once; + let assets_watch = cli_args.watch_dir.to_owned(); println!("calcit version: {}", cli_args::CALCIT_VERSION); @@ -69,14 +46,16 @@ fn main() -> Result<(), String> { .expect("failed to load $HOME"); println!("module folder: {}", module_folder.to_str().expect("extract path")); - if cli_options.disable_stack { + if cli_args.disable_stack { call_stack::set_using_stack(false); println!("stack trace disabled.") } - let base_dir = cli_options.entry_path.parent().expect("extract parent"); + let input_path = PathBuf::from(&cli_args.input); + let base_dir = input_path.parent().expect("extract parent"); - if let Some(snippet) = cli_matches.get_one::("eval") { + if let Some(CalcitCommand::Eval(ref command)) = cli_args.subcommand { + let snippet = &command.snippet; eval_once = true; match snapshot::create_file_from_snippet(snippet) { Ok(main_file) => { @@ -84,28 +63,26 @@ fn main() -> Result<(), String> { } Err(e) => return Err(e), } - if let Some(cli_deps) = cli_matches.get_many::("dep") { - for module_path in cli_deps { - let module_data = calcit::load_module(module_path, base_dir, &module_folder)?; - for (k, v) in &module_data.files { - snapshot.files.insert(k.to_owned(), v.to_owned()); - } + + for module_path in &command.dep { + let module_data = calcit::load_module(module_path, base_dir, &module_folder)?; + for (k, v) in &module_data.files { + snapshot.files.insert(k.to_owned(), v.to_owned()); } } } else { - if !Path::new(&cli_options.entry_path).exists() { - return Err("compact.cirru does not exist".to_owned()); + if !Path::new(&cli_args.input).exists() { + return Err(format!("{} does not exist", cli_args.input)); } // load entry file - let mut content = - fs::read_to_string(&cli_options.entry_path).unwrap_or_else(|_| panic!("expected Cirru snapshot: {:?}", cli_options.entry_path)); + let mut content = fs::read_to_string(&cli_args.input).unwrap_or_else(|_| panic!("expected Cirru snapshot: {}", cli_args.input)); strip_shebang(&mut content); let data = cirru_edn::parse(&content)?; // println!("reading: {}", content); - snapshot = snapshot::load_snapshot_data(&data, cli_options.entry_path.to_str().expect("extract path"))?; + snapshot = snapshot::load_snapshot_data(&data, &cli_args.input)?; // config in entry will overwrite default configs - if let Some(entry) = cli_matches.get_one::("entry") { + if let Some(entry) = cli_args.entry.to_owned() { if snapshot.entries.contains_key(entry.as_str()) { println!("running entry: {entry}"); snapshot.entries[entry.as_str()].clone_into(&mut snapshot.configs); @@ -128,13 +105,13 @@ fn main() -> Result<(), String> { } let config_init = snapshot.configs.init_fn.to_string(); let config_reload = snapshot.configs.reload_fn.to_string(); - let init_fn = cli_matches.get_one::("init-fn").unwrap_or(&config_init); - let reload_fn = cli_matches.get_one::("reload-fn").unwrap_or(&config_reload); + let init_fn = cli_args.init_fn.as_deref().unwrap_or(&config_init); + let reload_fn = cli_args.reload_fn.as_deref().unwrap_or(&config_reload); let (init_ns, init_def) = util::string::extract_ns_def(init_fn)?; let (reload_ns, reload_def) = util::string::extract_ns_def(reload_fn)?; let entries: ProgramEntries = ProgramEntries { - init_fn: Arc::from(init_fn.as_ref()), - reload_fn: Arc::from(reload_fn.as_ref()), + init_fn: Arc::from(init_fn), + reload_fn: Arc::from(reload_fn), init_def: init_def.into(), init_ns: init_ns.into(), reload_ns: reload_ns.into(), @@ -163,13 +140,13 @@ fn main() -> Result<(), String> { ) .map_err(|e| e.msg)?; - let task = if cli_options.emit_js { - if cli_options.skip_arity_check { + let task = if let Some(CalcitCommand::EmitJs(_)) = cli_args.subcommand { + if cli_args.skip_arity_check { codegen::set_code_gen_skip_arity_check(true); } - run_codegen(&entries, &cli_options.emit_path, false) - } else if cli_options.emit_ir { - run_codegen(&entries, &cli_options.emit_path, true) + run_codegen(&entries, &cli_args.emit_path, false) + } else if let Some(CalcitCommand::EmitIr(_)) = cli_args.subcommand { + run_codegen(&entries, &cli_args.emit_path, true) } else { let started_time = Instant::now(); @@ -197,16 +174,14 @@ fn main() -> Result<(), String> { if !eval_once { runner::track::track_task_add(); - let copied_assets = Arc::new(assets_watch.map(|s| s.to_owned())); - let copied_settings = Arc::new(cli_options); - let copied_entries = Arc::new(entries); - std::thread::spawn(move || watch_files(copied_entries, copied_settings, copied_assets)); + let args = cli_args.clone(); + std::thread::spawn(move || watch_files(entries, args, assets_watch)); } runner::track::exit_when_cleared(); Ok(()) } -pub fn watch_files(entries: Arc, settings: Arc, assets_watch: Arc>) { +pub fn watch_files(entries: ProgramEntries, settings: ToplevelCalcit, assets_watch: Option) { println!("\nRunning: in watch mode...\n"); let (tx, rx) = channel(); let mut debouncer = new_debouncer(Duration::from_millis(200), tx).expect("create watcher"); @@ -216,7 +191,10 @@ pub fn watch_files(entries: Arc, settings: Arc, asse .configure(config.with_compare_contents(true)) .expect("config watcher"); - let inc_path = settings.entry_path.parent().expect("extract parent").join(".compact-inc.cirru"); + let inc_path = PathBuf::from(&settings.input) + .parent() + .expect("extract parent") + .join(".compact-inc.cirru"); if !inc_path.exists() { if let Err(e) = fs::write(&inc_path, "").map_err(|e| -> String { e.to_string() }) { eprintln!("file writing error: {e}"); @@ -256,7 +234,7 @@ pub fn watch_files(entries: Arc, settings: Arc, asse // overwrite previous state -fn recall_program(content: &str, entries: &ProgramEntries, settings: &CLIOptions) -> Result<(), String> { +fn recall_program(content: &str, entries: &ProgramEntries, settings: &ToplevelCalcit) -> Result<(), String> { println!("\n-------- file change --------\n"); // Steps: @@ -276,9 +254,9 @@ fn recall_program(content: &str, entries: &ProgramEntries, settings: &CLIOptions builtins::meta::force_reset_gensym_index()?; println!("cleared evaled states and reset gensym index."); - let task = if settings.emit_js { + let task = if let Some(CalcitCommand::EmitJs(_)) = settings.subcommand { run_codegen(entries, &settings.emit_path, false) - } else if settings.emit_ir { + } else if let Some(CalcitCommand::EmitIr(_)) = settings.subcommand { run_codegen(entries, &settings.emit_path, true) } else { // run from `reload_fn` after reload diff --git a/src/cli_args.rs b/src/cli_args.rs index 683f8a4..030f9dd 100644 --- a/src/cli_args.rs +++ b/src/cli_args.rs @@ -1,93 +1,73 @@ -use clap::ArgAction; +use argh::FromArgs; pub const CALCIT_VERSION: &str = env!("CARGO_PKG_VERSION"); -pub fn parse_cli() -> clap::ArgMatches { - clap::Command::new("Calcit") - .version(CALCIT_VERSION) - .author("Jon Chen. ") - .about("Calcit Scripting Language") - .arg( - clap::Arg::new("once") - .help("skip watching mode, just run once") - .short('1') - .long("once") - .num_args(0), - ) - .arg( - clap::Arg::new("emit-js") - .help("emit JavaScript rather than interpreting") - .long("emit-js") - .num_args(0), - ) - .arg( - clap::Arg::new("emit-ir") - .help("emit Cirru EDN representation of program to program-ir.cirru") - .long("emit-ir") - .num_args(0), - ) - .arg( - clap::Arg::new("disable-stack") - .help("disable stack trace for errors") - .long("disable-stack") - .num_args(0), - ) - .arg( - clap::Arg::new("skip-arity-check") - .help("skip arity check in js codegen") - .long("skip-arity-check") - .num_args(0), - ) - .arg( - clap::Arg::new("eval") - .help("evaluate a snippet") - .short('e') - .long("eval") - .num_args(1), - ) - .arg( - clap::Arg::new("dep") - .help("inject dependency") - .short('d') - .long("dep") - .action(ArgAction::Append), - ) - .arg( - clap::Arg::new("emit-path") - .help("specify another directory for js, rather than `js-out/`") - .long("emit-path") - .num_args(1), - ) - .arg( - clap::Arg::new("init-fn") - .help("specify `init_fn` which is main function") - .long("init-fn") - .num_args(1), - ) - .arg( - clap::Arg::new("reload-fn") - .help("specify `reload_fn` which is called after hot reload") - .long("reload-fn") - .num_args(1), - ) - .arg(clap::Arg::new("entry").help("specify with config entry").long("entry").num_args(1)) - .arg( - clap::Arg::new("watch-dir") - .help("specify a path to watch assets changes") - .long("watch-dir") - .num_args(1), - ) - .arg( - clap::Arg::new("reload-libs") - .help("force reloading libs data during code reload") - .long("reload-libs") - .num_args(0), - ) - .arg( - clap::Arg::new("input") - .help("entry file path") - .default_value("compact.cirru") - .index(1), - ) - .get_matches() +#[derive(FromArgs, PartialEq, Debug, Clone)] +/// Top-level command. +pub struct ToplevelCalcit { + #[argh(subcommand)] + pub subcommand: Option, + /// skip watching mode, just run once + #[argh(switch, short = '1')] + pub once: bool, + /// disable stack trace for errors + #[argh(switch)] + pub disable_stack: bool, + /// skip arity check in js codegen + #[argh(switch)] + pub skip_arity_check: bool, + /// entry file path, defaults to "js-out/" + #[argh(option, default = "String::from(\"js-out/\")")] + pub emit_path: String, + /// specify `init_fn` which is main function + #[argh(option)] + pub init_fn: Option, + /// specify `reload_fn` which is called after hot reload + #[argh(option)] + pub reload_fn: Option, + /// specify with config entry + #[argh(option)] + pub entry: Option, + #[argh(switch)] + /// force reloading libs data during code reload + pub reload_libs: bool, + #[argh(option)] + /// specify a path to watch assets changes + pub watch_dir: Option, + /// input source file, defaults to "compact.cirru" + #[argh(positional, default = "String::from(\"compact.cirru\")")] + pub input: String, +} + +#[derive(FromArgs, PartialEq, Debug, Clone)] +#[argh(subcommand)] +pub enum CalcitCommand { + /// emit JavaScript rather than interpreting + EmitJs(EmitJsCommand), + /// emit Cirru EDN representation of program to program-ir.cirru + EmitIr(EmitIrCommand), + /// evaluate snippet + Eval(EvalCommand), +} + +/// emit JavaScript rather than interpreting +#[derive(FromArgs, PartialEq, Debug, Clone)] +#[argh(subcommand, name = "js")] +pub struct EmitJsCommand {} + +/// emit Cirru EDN representation of program to program-ir.cirru +#[derive(FromArgs, PartialEq, Debug, Clone)] +#[argh(subcommand, name = "ir")] +pub struct EmitIrCommand {} + +/// run program +#[derive(FromArgs, PartialEq, Debug, Clone)] +#[argh(subcommand, name = "eval")] +pub struct EvalCommand { + /// evaluate a snippet + #[argh(positional)] + pub snippet: String, + /// entry file path + #[argh(option)] + pub dep: Vec, }