From da4e618e122bae3d3ce33e8ca6b76ba2aee1ff7a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 18:24:59 -0700 Subject: [PATCH 01/14] Remove dist folder causing issues --- homepage/dist | 1 - 1 file changed, 1 deletion(-) delete mode 160000 homepage/dist diff --git a/homepage/dist b/homepage/dist deleted file mode 160000 index 9a8c9b8..0000000 --- a/homepage/dist +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9a8c9b82f90d74d97b41fd855a6fc615786d7107 From de71edeb0f77c8e0fe632868fc723dff361ce47f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 18:35:58 -0700 Subject: [PATCH 02/14] Update crate / repo names --- docs/README_CN.md | 6 +++--- homepage/public/readme.html | 2 +- homepage/public/readme.md | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/README_CN.md b/docs/README_CN.md index ab77441..d26c809 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -45,8 +45,8 @@ ##### 从`Github`下载 ```toml -[dependencies.commander_rust] -git = "https://github.com/MSDimos/commander_rust" +[dependencies.commander-rust] +git = "https://github.com/MSDimos/commander-rust" branch = "master" ``` @@ -54,7 +54,7 @@ branch = "master" ```toml [dependencies] -commander_rust = "1.1.3" # 指定其他任意你需要的版本 +commander-rust = "1.1.3" # 指定其他任意你需要的版本 ``` #### 使用它 diff --git a/homepage/public/readme.html b/homepage/public/readme.html index f2fd190..f1bc553 100644 --- a/homepage/public/readme.html +++ b/homepage/public/readme.html @@ -303,7 +303,7 @@

other languages

中文文档

why this ?

For a long time, developing cli in Rust is difficult. The community offers a wide range of solutions. Yes, they're excellent, but they're not very simple.

Inspired by commander.js & rocket.rs, the crate was born.

features

  • API friendly
  • easy to use
  • support for approximate dynamic language
  • low performance loss
  • automatically support for --version & --help
  • automatically run corresponding commands

limit

If you want to use this crate, please guarantee that you have follow rules below:

  • using Rust 2018 (full proc macro support is required, including [proc_macro] & [proc_macro_attribute])
  • using cargo (cargo will produce some environment variable according to Cargo.toml, we need that)
  • be familiar with Rust (because it's developed for Rust )

As a reference, my versions are:

  • cargo: cargo 1.35.0-nightly (95b45eca1 2019-03-06)
  • rustc: rustc 1.35.0-nightly (e68bf8ae1 2019-03-11)
  • Linux kernal: 4.15.0-47-generic
  • Ubuntu: 16.04

usage

install commander-rust

Two ways supported: from Github or crates.io. -The difference between them is that Github is latest but unstable and crates.io is stable but might not be latest.

install from Github

install from crates.io

using it

We offer a simple but complete example, you can learn all through it. +The difference between them is that Github is latest but unstable and crates.io is stable but might not be latest.

install from Github

install from crates.io

using it

We offer a simple but complete example, you can learn all through it. Yes, That's all. Very easy!

try it

try to input [pkg-name] --help.

version & description & cli-name?

version, description, cli-name of application are from Cargo.toml.

For instance:

direct

If you don't want to define a sub-command, you can use #[direct]. What's direct? In some situations, for instance, if you want to develop a CLI which could be called like rm ./* -rf. diff --git a/homepage/public/readme.md b/homepage/public/readme.md index 7a8432c..e1d2c36 100644 --- a/homepage/public/readme.md +++ b/homepage/public/readme.md @@ -41,8 +41,8 @@ The difference between them is that `Github` is latest but unstable and `crates. ##### install from `Github` ```toml -[dependencies.commander_rust] -git = "https://github.com/MSDimos/commander_rust" +[dependencies.commander-rust] +git = "https://github.com/MSDimos/commander-rust" branch = "master" ``` @@ -50,7 +50,7 @@ branch = "master" ```toml [dependencies] -commander_rust = "1.1.3" # or other version you want to install +commander-rust = "1.1.3" # or other version you want to install ``` #### using it From 1c588b2dbe48fff7a27b690f8fa4d529b5f2b2b2 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 18:54:29 -0700 Subject: [PATCH 03/14] Import BorrowMut trait so borrow_mut() can resolve --- crates/commander-macros/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 965589e..a633e80 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -263,6 +263,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { #func fn #direct_get_fn() -> Vec<#argument_ident> { + use std::borrow::BorrowMut; let direct_fn: &mut Option, app: _commander_rust_Cli)> = &mut (*DIRECT_FN.lock().unwrap()); *direct_fn.borrow_mut() = Some(#call_fn_name); @@ -482,4 +483,4 @@ pub fn run(_: TokenStream) -> TokenStream { app } }) -} \ No newline at end of file +} From 21037e13d7a72d84e5d552f687450b06543cdc55 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 22:14:04 -0700 Subject: [PATCH 04/14] Update dependencies --- .gitignore | 3 ++- crates/commander-macros/Cargo.toml | 6 +++--- crates/commander-macros/src/lib.rs | 30 ++++++++++------------------ crates/commander-macros/src/tools.rs | 17 ++++++++++------ 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index f8f4c9d..48b0c22 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,8 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk - +# vscode +.vscode .config.bp scripts/ \ No newline at end of file diff --git a/crates/commander-macros/Cargo.toml b/crates/commander-macros/Cargo.toml index f9e8549..808253b 100644 --- a/crates/commander-macros/Cargo.toml +++ b/crates/commander-macros/Cargo.toml @@ -10,10 +10,10 @@ license = "MIT" proc-macro = true [dependencies] -proc-macro2 = "0.4.24" -quote = "0.6.11" +proc-macro2 = "1.0" +quote = "1.0" lazy_static = "1.2.0" [dependencies.syn] -version = "0.15.26" +version = "1" features = ["full", "extra-traits", "parsing"] diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index a633e80..6f4398f 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -76,11 +76,9 @@ lazy_static! { #[proc_macro_attribute] pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let method: ItemFn = parse_macro_input!(method as ItemFn); - let ItemFn { - ident, - decl, - .. - } = &method; + let args = &method.sig.inputs; + let ret = &method.sig.output; + let ident = &method.sig.ident; let name = format!("{}", ident); let get_fn = Ident::new(&prefix!(name), ident.span()); let cmd_token = Ident::new(&prefix!("Command"), ident.span()); @@ -104,7 +102,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let command: CommandToken = parse_macro_input!(cmd as CommandToken); // generating call function, because we can't call unstable (uncertain quantity parameters) function let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&decl.inputs, &call_fn_name, &ident); + let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); let mut error_info = check_arguments(&command.args); if format!("{}", command.name) != "main" && format!("{}", command.name) != name { @@ -162,10 +160,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { let option: OptionsToken = parse_macro_input!(opt as OptionsToken); let method: ItemFn = parse_macro_input!(method as ItemFn); - let ItemFn { - ident, - .. - } = &method; + let ident = &method.sig.ident; let name = format!("{}", ident); let opt_name = format!("{}", option.long); let fn_name = prefix!(name, opt_name); @@ -236,18 +231,16 @@ pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { let func: ItemFn = parse_macro_input!(func as ItemFn); - let ItemFn { - ident, - decl, - .. - } = &func; + let ident = &func.sig.ident; + let ret = &func.sig.output; + let args = &func.sig.inputs; let name = format!("{}", ident); let pure_args: PureArguments = parse_macro_input!(pure_args as PureArguments); let direct_fn: &mut Option = &mut (*DIRECT_NAME.lock().unwrap()); let direct_get_fn = Ident::new(&prefix!(name), ident.span()); let argument_ident = Ident::new(&prefix!("Argument"), ident.span()); let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&decl.inputs, &call_fn_name, &ident); + let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); let mut error_info: TokenStream2 = check_arguments(&pure_args.0); @@ -287,10 +280,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let pure_args: PureArguments = parse_macro_input!(pure_arguments as PureArguments); let main: ItemFn = parse_macro_input!(main as ItemFn); - let ItemFn { - ident, - .. - } = &main; + let ident = &main.sig.ident; let target = format!("{}", ident); let opts = COMMAND_OPTIONS.lock().unwrap(); let imports = vec![ diff --git a/crates/commander-macros/src/tools.rs b/crates/commander-macros/src/tools.rs index e529e37..e7d07c2 100644 --- a/crates/commander-macros/src/tools.rs +++ b/crates/commander-macros/src/tools.rs @@ -1,8 +1,8 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use syn::punctuated::Punctuated; -use syn::{ FnArg, Ident }; use syn::token; -use quote::quote; -use proc_macro2::{ TokenStream as TokenStream2 }; +use syn::{ReturnType, FnArg, Ident}; /// Generate inputs of command processing function. /// @@ -10,11 +10,16 @@ use proc_macro2::{ TokenStream as TokenStream2 }; /// But we need a common way to call it, so we need to generate inputs tokens needed. #[doc(hidden)] -pub fn generate_call_fn(inputs: &Punctuated, call_fn_name: &Ident, fn_name: &Ident) -> TokenStream2 { +pub fn generate_call_fn( + inputs: &Punctuated, + call_fn_name: &Ident, + fn_name: &Ident, + ret: &ReturnType, +) -> TokenStream2 { let mut tokens: Vec = vec![]; for (idx, arg) in inputs.iter().enumerate() { - if let FnArg::Captured(cap) = arg { + if let FnArg::Typed( cap) = arg { let ty = &cap.ty; if idx < inputs.len() - 1 { @@ -49,7 +54,7 @@ pub fn generate_call_fn(inputs: &Punctuated, call_fn_name: } (quote! { - fn #call_fn_name(raws: &Vec<_commander_rust_Raw>, cli: _commander_rust_Cli) { + fn #call_fn_name(raws: &Vec<_commander_rust_Raw>, cli: _commander_rust_Cli) #ret { #fn_name(#(#tokens,)*) } }).into() From ea1c227e35e371853a6e3b976d89c9af87be903d Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 22:14:04 -0700 Subject: [PATCH 05/14] Update dependencies --- .gitignore | 3 ++- crates/commander-macros/Cargo.toml | 6 +++--- crates/commander-macros/src/lib.rs | 30 ++++++++++------------------ crates/commander-macros/src/tools.rs | 17 ++++++++++------ 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index f8f4c9d..48b0c22 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,8 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk - +# vscode +.vscode .config.bp scripts/ \ No newline at end of file diff --git a/crates/commander-macros/Cargo.toml b/crates/commander-macros/Cargo.toml index f9e8549..808253b 100644 --- a/crates/commander-macros/Cargo.toml +++ b/crates/commander-macros/Cargo.toml @@ -10,10 +10,10 @@ license = "MIT" proc-macro = true [dependencies] -proc-macro2 = "0.4.24" -quote = "0.6.11" +proc-macro2 = "1.0" +quote = "1.0" lazy_static = "1.2.0" [dependencies.syn] -version = "0.15.26" +version = "1" features = ["full", "extra-traits", "parsing"] diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 965589e..97f8eb1 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -76,11 +76,9 @@ lazy_static! { #[proc_macro_attribute] pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let method: ItemFn = parse_macro_input!(method as ItemFn); - let ItemFn { - ident, - decl, - .. - } = &method; + let args = &method.sig.inputs; + let ret = &method.sig.output; + let ident = &method.sig.ident; let name = format!("{}", ident); let get_fn = Ident::new(&prefix!(name), ident.span()); let cmd_token = Ident::new(&prefix!("Command"), ident.span()); @@ -104,7 +102,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let command: CommandToken = parse_macro_input!(cmd as CommandToken); // generating call function, because we can't call unstable (uncertain quantity parameters) function let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&decl.inputs, &call_fn_name, &ident); + let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); let mut error_info = check_arguments(&command.args); if format!("{}", command.name) != "main" && format!("{}", command.name) != name { @@ -162,10 +160,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { let option: OptionsToken = parse_macro_input!(opt as OptionsToken); let method: ItemFn = parse_macro_input!(method as ItemFn); - let ItemFn { - ident, - .. - } = &method; + let ident = &method.sig.ident; let name = format!("{}", ident); let opt_name = format!("{}", option.long); let fn_name = prefix!(name, opt_name); @@ -236,18 +231,16 @@ pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { let func: ItemFn = parse_macro_input!(func as ItemFn); - let ItemFn { - ident, - decl, - .. - } = &func; + let ident = &func.sig.ident; + let ret = &func.sig.output; + let args = &func.sig.inputs; let name = format!("{}", ident); let pure_args: PureArguments = parse_macro_input!(pure_args as PureArguments); let direct_fn: &mut Option = &mut (*DIRECT_NAME.lock().unwrap()); let direct_get_fn = Ident::new(&prefix!(name), ident.span()); let argument_ident = Ident::new(&prefix!("Argument"), ident.span()); let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&decl.inputs, &call_fn_name, &ident); + let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); let mut error_info: TokenStream2 = check_arguments(&pure_args.0); @@ -286,10 +279,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let pure_args: PureArguments = parse_macro_input!(pure_arguments as PureArguments); let main: ItemFn = parse_macro_input!(main as ItemFn); - let ItemFn { - ident, - .. - } = &main; + let ident = &main.sig.ident; let target = format!("{}", ident); let opts = COMMAND_OPTIONS.lock().unwrap(); let imports = vec![ diff --git a/crates/commander-macros/src/tools.rs b/crates/commander-macros/src/tools.rs index e529e37..e7d07c2 100644 --- a/crates/commander-macros/src/tools.rs +++ b/crates/commander-macros/src/tools.rs @@ -1,8 +1,8 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use syn::punctuated::Punctuated; -use syn::{ FnArg, Ident }; use syn::token; -use quote::quote; -use proc_macro2::{ TokenStream as TokenStream2 }; +use syn::{ReturnType, FnArg, Ident}; /// Generate inputs of command processing function. /// @@ -10,11 +10,16 @@ use proc_macro2::{ TokenStream as TokenStream2 }; /// But we need a common way to call it, so we need to generate inputs tokens needed. #[doc(hidden)] -pub fn generate_call_fn(inputs: &Punctuated, call_fn_name: &Ident, fn_name: &Ident) -> TokenStream2 { +pub fn generate_call_fn( + inputs: &Punctuated, + call_fn_name: &Ident, + fn_name: &Ident, + ret: &ReturnType, +) -> TokenStream2 { let mut tokens: Vec = vec![]; for (idx, arg) in inputs.iter().enumerate() { - if let FnArg::Captured(cap) = arg { + if let FnArg::Typed( cap) = arg { let ty = &cap.ty; if idx < inputs.len() - 1 { @@ -49,7 +54,7 @@ pub fn generate_call_fn(inputs: &Punctuated, call_fn_name: } (quote! { - fn #call_fn_name(raws: &Vec<_commander_rust_Raw>, cli: _commander_rust_Cli) { + fn #call_fn_name(raws: &Vec<_commander_rust_Raw>, cli: _commander_rust_Cli) #ret { #fn_name(#(#tokens,)*) } }).into() From d1dd8045c635a4ea53ff2c89f40add9a95801b5b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 31 Mar 2020 22:32:58 -0700 Subject: [PATCH 06/14] no need to use & for refs --- crates/commander-macros/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 97f8eb1..e6636f8 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -102,7 +102,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let command: CommandToken = parse_macro_input!(cmd as CommandToken); // generating call function, because we can't call unstable (uncertain quantity parameters) function let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); + let call_fn = generate_call_fn(args, &call_fn_name, ident, ret); let mut error_info = check_arguments(&command.args); if format!("{}", command.name) != "main" && format!("{}", command.name) != name { @@ -240,7 +240,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { let direct_get_fn = Ident::new(&prefix!(name), ident.span()); let argument_ident = Ident::new(&prefix!("Argument"), ident.span()); let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); - let call_fn = generate_call_fn(&args, &call_fn_name, &ident, &ret); + let call_fn = generate_call_fn(args, &call_fn_name, ident, ret); let mut error_info: TokenStream2 = check_arguments(&pure_args.0); From 81215a318d74ec260932ae48f7f9f46d6f79b994 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 00:25:19 -0700 Subject: [PATCH 07/14] just enforce return types to be the same as main --- crates/commander-macros/src/lib.rs | 43 +++++++++++++----------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index b5090f9..a143178 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -257,7 +257,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { fn #direct_get_fn() -> Vec<#argument_ident> { use std::borrow::BorrowMut; - let direct_fn: &mut Option, app: _commander_rust_Cli)> = &mut (*DIRECT_FN.lock().unwrap()); + let direct_fn: &mut Option, app: _commander_rust_Cli) #ret> = &mut (*DIRECT_FN.lock().unwrap()); *direct_fn.borrow_mut() = Some(#call_fn_name); #pure_args @@ -281,6 +281,7 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let pure_args: PureArguments = parse_macro_input!(pure_arguments as PureArguments); let main: ItemFn = parse_macro_input!(main as ItemFn); let ident = &main.sig.ident; + let ret = &main.sig.output; let target = format!("{}", ident); let opts = COMMAND_OPTIONS.lock().unwrap(); let imports = vec![ @@ -320,27 +321,20 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let needed = quote! { #error_info - mod _commander_rust_Inner { - use crate::_commander_rust_ls; - use crate::_commander_rust_Raw; - use crate::_commander_rust_Cli; - - type Raw = _commander_rust_Raw; - type Map = std::collections::HashMap, app: _commander_rust_Cli)>; - type Mutex = std::sync::Mutex; - - _commander_rust_ls! { - pub static ref CALL_FNS: Mutex = Mutex::new(Map::new()); - pub static ref DIRECT_FN: std::sync::Mutex, app: _commander_rust_Cli)>> = std::sync::Mutex::new(None); - } + #(#imports)* - pub const APP_NAME: &'static str = env!("CARGO_PKG_NAME"); - pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - pub const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); + type COMMANDER_Raw = _commander_rust_Raw; + type COMMANDER_Map = std::collections::HashMap, app: _commander_rust_Cli) #ret>; + type COMMANDER_Mutex = std::sync::Mutex; + + _commander_rust_ls! { + pub static ref CALL_FNS: COMMANDER_Mutex = COMMANDER_Mutex::new(COMMANDER_Map::new()); + pub static ref DIRECT_FN: std::sync::Mutex, app: _commander_rust_Cli) #ret>> = std::sync::Mutex::new(None); } - use _commander_rust_Inner::{ CALL_FNS, DIRECT_FN, VERSION, DESCRIPTION, APP_NAME }; - #(#imports)* + pub const APP_NAME: &'static str = env!("CARGO_PKG_NAME"); + pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); + pub const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); #main }; @@ -422,7 +416,7 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { /// #[proc_macro] pub fn run(_: TokenStream) -> TokenStream { - TokenStream::from(quote! { + let result = quote! { { // _commander_rust_main is generated by `entry` // @@ -453,12 +447,12 @@ pub fn run(_: TokenStream) -> TokenStream { println!("version: {}", VERSION); } else { if let Some(callback) = fns.get(&cli.get_name()) { - callback(&cli.get_raws(), cli); + return callback(&cli.get_raws(), cli); } else if !cli.direct_args.is_empty() { let df = *DIRECT_FN.lock().unwrap(); if let Some(f) = &df { - f(&cli.direct_args.clone(), cli) + return f(&cli.direct_args.clone(), cli) } else { println!("ERRRRR"); } @@ -470,7 +464,8 @@ pub fn run(_: TokenStream) -> TokenStream { println!("Using `{} --help` for more help information.", APP_NAME); } - app + app; } - }) + }; + result.into() } From 27d34b3ed94b1f20975ba31ea08dca8e510ad14c Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 13:20:31 -0700 Subject: [PATCH 08/14] Make `run()!` return (app, Option) pair. --- crates/commander-macros/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index a143178..3ebd482 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -428,6 +428,7 @@ pub fn run(_: TokenStream) -> TokenStream { let cli = _commander_rust_Cli::from(&ins, &app); let fns = CALL_FNS.lock().unwrap(); + let mut out = None; if let Some(cli) = cli { if cli.has("help") || cli.has("h") { @@ -447,12 +448,12 @@ pub fn run(_: TokenStream) -> TokenStream { println!("version: {}", VERSION); } else { if let Some(callback) = fns.get(&cli.get_name()) { - return callback(&cli.get_raws(), cli); + out = Some(callback(&cli.get_raws(), cli)); } else if !cli.direct_args.is_empty() { let df = *DIRECT_FN.lock().unwrap(); if let Some(f) = &df { - return f(&cli.direct_args.clone(), cli) + out = Some(f(&cli.direct_args.clone(), cli)); } else { println!("ERRRRR"); } @@ -464,7 +465,7 @@ pub fn run(_: TokenStream) -> TokenStream { println!("Using `{} --help` for more help information.", APP_NAME); } - app; + (app, out) } }; result.into() From cec2cd06bea2c13dc3d0dea2e46a9127a0458846 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 14:14:49 -0700 Subject: [PATCH 09/14] Make return value of the command part of app --- crates/commander-core/src/fmt.rs | 2 +- crates/commander-core/src/lib.rs | 9 +++++---- crates/commander-macros/src/lib.rs | 17 +++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/commander-core/src/fmt.rs b/crates/commander-core/src/fmt.rs index 1fef4a8..f8f39f3 100644 --- a/crates/commander-core/src/fmt.rs +++ b/crates/commander-core/src/fmt.rs @@ -60,7 +60,7 @@ impl Debug for Command { } -impl Debug for Application { +impl Debug for Application { fn fmt(&self, f: &mut Formatter) -> Result { let mut max_len = 0; let mut lens = vec![]; diff --git a/crates/commander-core/src/lib.rs b/crates/commander-core/src/lib.rs index 26ce95a..7c74cff 100644 --- a/crates/commander-core/src/lib.rs +++ b/crates/commander-core/src/lib.rs @@ -91,12 +91,13 @@ pub struct Argument { /// # Note /// It's generated by `commander_rust`, and it should be readonly. /// -pub struct Application { +pub struct Application { pub name: String, pub desc: String, pub cmds: Vec, pub opts: Vec, pub direct_args: Vec, + pub out: Option } /// Represents a instance defined by `#[command]`. @@ -240,7 +241,7 @@ pub struct Cli { pub direct_args: Vec, } -impl Application { +impl Application { /// Deriving `#[option(-h, --help, "output usage information")]` /// and `#[option(-V, --version, "output the version number")]` for all `Command` and `Application`. /// Dont use it! @@ -360,7 +361,7 @@ impl Cli { /// Inner function, dont use it. #[doc(hidden)] - pub fn from(instances: &Vec, app: &Application) -> Option { + pub fn from (instances: &Vec, app: &Application) -> Option { if instances.is_empty() { None } else { @@ -558,7 +559,7 @@ impl Instance { } } -pub fn normalize(args: Vec, app: &Application) -> Vec { +pub fn normalize (args: Vec, app: &Application) -> Vec { let mut instances = vec![]; let mut head = Instance::empty(); let mut args = args.into_iter().skip(1); diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 3ebd482..396ae76 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -346,13 +346,14 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { TokenStream::from(quote! { #needed - fn #get_fn() -> #app_token { + fn #get_fn (out:Option) -> #app_token { let mut application = #app_token { name: String::from(APP_NAME), desc: String::from(DESCRIPTION), opts: vec![], cmds: vec![], direct_args: vec![], + out, }; application.opts = { @@ -371,7 +372,7 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { }; // inject direct-fns application.direct_args = #direct_get_fn(); - + application } }) @@ -379,13 +380,14 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { TokenStream::from(quote!{ #needed - fn #get_fn() -> #app_token { + fn #get_fn (out:Option) -> #app_token { let mut application = #app_token { name: String::from(APP_NAME), desc: String::from(DESCRIPTION), opts: vec![], cmds: vec![], direct_args: vec![], + out: out, }; application.opts = { @@ -420,7 +422,7 @@ pub fn run(_: TokenStream) -> TokenStream { { // _commander_rust_main is generated by `entry` // - let mut app = _commander_rust_main(); + let mut app = _commander_rust_main(None); let ins; app.derive(); @@ -428,7 +430,6 @@ pub fn run(_: TokenStream) -> TokenStream { let cli = _commander_rust_Cli::from(&ins, &app); let fns = CALL_FNS.lock().unwrap(); - let mut out = None; if let Some(cli) = cli { if cli.has("help") || cli.has("h") { @@ -448,12 +449,12 @@ pub fn run(_: TokenStream) -> TokenStream { println!("version: {}", VERSION); } else { if let Some(callback) = fns.get(&cli.get_name()) { - out = Some(callback(&cli.get_raws(), cli)); + app.out = Some(callback(&cli.get_raws(), cli)); } else if !cli.direct_args.is_empty() { let df = *DIRECT_FN.lock().unwrap(); if let Some(f) = &df { - out = Some(f(&cli.direct_args.clone(), cli)); + app.out = Some(f(&cli.direct_args.clone(), cli)); } else { println!("ERRRRR"); } @@ -465,7 +466,7 @@ pub fn run(_: TokenStream) -> TokenStream { println!("Using `{} --help` for more help information.", APP_NAME); } - (app, out) + app } }; result.into() From 9b78c79e07ef541f140fe78813a424148f0e966f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 17:15:04 -0700 Subject: [PATCH 10/14] consolidate token stream generation --- crates/commander-macros/src/lib.rs | 128 +++++++++++------------------ 1 file changed, 50 insertions(+), 78 deletions(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 396ae76..0522f7c 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -339,76 +339,46 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { #main }; - // inject direct-functions' arguments or not - if let Some(df) = direct_fn { + let direct_get_fn = if let Some(df) = direct_fn { let direct_get_fn = Ident::new(&prefix!(df), Span2::call_site()); - - TokenStream::from(quote! { - #needed - - fn #get_fn (out:Option) -> #app_token { - let mut application = #app_token { - name: String::from(APP_NAME), - desc: String::from(DESCRIPTION), - opts: vec![], - cmds: vec![], - direct_args: vec![], - out, - }; - - application.opts = { - let mut v = vec![]; - #( - v.push(#get_opts_fns()); - )* - v - }; - application.cmds = { - let mut v = vec![]; - #( - v.push(#get_cmds_fns()); - )* - v - }; - // inject direct-fns - application.direct_args = #direct_get_fn(); - - application - } - }) + quote! { #direct_get_fn(); } } else { - TokenStream::from(quote!{ - #needed - - fn #get_fn (out:Option) -> #app_token { - let mut application = #app_token { - name: String::from(APP_NAME), - desc: String::from(DESCRIPTION), - opts: vec![], - cmds: vec![], - direct_args: vec![], - out: out, - }; - - application.opts = { - let mut v = vec![]; - #( - v.push(#get_opts_fns()); - )* - v - }; - - application.cmds = { - let mut v = vec![]; - #( - v.push(#get_cmds_fns()); - )* - v - }; - application - } - }) - } + quote! { vec![]; } + }; + + + TokenStream::from(quote! { + #needed + + fn #get_fn (out:Option) -> #app_token { + let mut application = #app_token { + name: String::from(APP_NAME), + desc: String::from(DESCRIPTION), + opts: vec![], + cmds: vec![], + direct_args: vec![], + out: out + }; + + application.opts = { + let mut v = vec![]; + #( + v.push(#get_opts_fns()); + )* + v + }; + application.cmds = { + let mut v = vec![]; + #( + v.push(#get_cmds_fns()); + )* + v + }; + application.direct_args = #direct_get_fn; + + application + } + }) } /// Run cli now. @@ -427,7 +397,7 @@ pub fn run(_: TokenStream) -> TokenStream { app.derive(); ins = _commander_rust_normalize(std::env::args().into_iter().collect::>(), &app); - + let cli = _commander_rust_Cli::from(&ins, &app); let fns = CALL_FNS.lock().unwrap(); @@ -437,29 +407,31 @@ pub fn run(_: TokenStream) -> TokenStream { if cli.cmd.is_some() { for cmd in &app.cmds { if cmd.name == cli.get_name() { - println!("{:#?}", cmd); + println!("{:#}", cmd); break; } } } else { // display cli usage - println!("{:#?}", app); + println!("{:#}", app); } } else if cli.has("version") || cli.has("V") { println!("version: {}", VERSION); } else { if let Some(callback) = fns.get(&cli.get_name()) { app.out = Some(callback(&cli.get_raws(), cli)); - } else if !cli.direct_args.is_empty() { - let df = *DIRECT_FN.lock().unwrap(); + } else { + if let Some(direct_args) = cli.direct_args.clone() { + let df = *DIRECT_FN.lock().unwrap(); - if let Some(f) = &df { - app.out = Some(f(&cli.direct_args.clone(), cli)); + if let Some(f) = &df { + app.out = Some(f(&direct_args, cli)); + } else { + println!("ERRRRR"); + } } else { - println!("ERRRRR"); + eprintln!("Unknown usage. Using `{} --help` for more help information.\n", APP_NAME); } - } else { - eprintln!("Unknown usage. Using `{} --help` for more help information.\n", APP_NAME); } } } else { From adc4d1b9d56f42ba218674099bc439dd1f513e97 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 17:15:35 -0700 Subject: [PATCH 11/14] Switch to using display instead of debug --- crates/commander-core/src/fmt.rs | 24 ++++++++++++------------ crates/commander-core/src/lib.rs | 23 ++++++++++++++++++----- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/crates/commander-core/src/fmt.rs b/crates/commander-core/src/fmt.rs index f8f39f3..1b91b3c 100644 --- a/crates/commander-core/src/fmt.rs +++ b/crates/commander-core/src/fmt.rs @@ -1,9 +1,9 @@ #![allow(unused_mut, dead_code)] -use std::fmt::{ Debug, Formatter, Result }; +use std::fmt::{ Formatter, Result, Display }; use crate::{ Command, Argument, ArgumentType, Application }; -impl Debug for Argument { +impl Display for Argument { fn fmt(&self, f: &mut Formatter) -> Result { match self.ty { ArgumentType::RequiredSingle => write!(f, "<{}>", self.name), @@ -14,14 +14,14 @@ impl Debug for Argument { } } -impl Debug for Command { +impl Display for Command { fn fmt(&self, f: &mut Formatter) -> Result { let mut max_len = 0; let mut arg_formats = String::new(); let mut lens = vec![]; for opt in &self.opts { - let arg_len = opt.arg.as_ref().map_or(0, |a| format!("{:?}", a).len()); + let arg_len = opt.arg.as_ref().map_or(0, |a| format!("{:}", a).len()); let used_space = opt.long.len() + opt.short.len() + arg_len; if used_space > max_len { @@ -32,7 +32,7 @@ impl Debug for Command { } for arg in &self.args { - arg_formats.push_str(&format!("{:?} ", arg)); + arg_formats.push_str(&format!("{:} ", arg)); } if self.opts.len() > 0 { @@ -48,7 +48,7 @@ impl Debug for Command { for opt in &self.opts { let used_space = lens.pop().unwrap_or_default(); - let arg_format = opt.arg.as_ref().map_or(String::new(), |a| format!("{:?}", a)); + let arg_format = opt.arg.as_ref().map_or(String::new(), |a| format!("{:}", a)); write!(f, " {}", format!("-{}, --{} {} {}", opt.short, opt.long, arg_format, " ".repeat(max_len - used_space)))?; write!(f, " {}\n", opt.desc.clone().unwrap_or_default())?; @@ -60,13 +60,13 @@ impl Debug for Command { } -impl Debug for Application { +impl Display for Application { fn fmt(&self, f: &mut Formatter) -> Result { let mut max_len = 0; let mut lens = vec![]; for opt in &self.opts { - let arg_len = opt.arg.as_ref().map_or(0, |a| format!("{:?}", a).len()); + let arg_len = opt.arg.as_ref().map_or(0, |a| format!("{:}", a).len()); let used_space = opt.long.len() + opt.short.len() + arg_len; if used_space > max_len { @@ -90,7 +90,7 @@ impl Debug for Application { write!(f, " OR {} ", self.name)?; for arg in self.direct_args.iter() { - write!(f, "{:?} ", arg)?; + write!(f, "{:} ", arg)?; } write!(f, "[options]")?; } @@ -102,7 +102,7 @@ impl Debug for Application { for opt in &self.opts { let used_space = lens.pop().unwrap_or_default(); - let arg_format = opt.arg.as_ref().map_or(String::new(), |a| format!("{:?}", a)); + let arg_format = opt.arg.as_ref().map_or(String::new(), |a| format!("{:}", a)); write!(f, " {}", format!("-{}, --{} {} {}", opt.short, opt.long, arg_format, " ".repeat(max_len - used_space)))?; write!(f, " {}\n", opt.desc.clone().unwrap_or_default())?; @@ -117,7 +117,7 @@ impl Debug for Application { let mut used_space = cmd.name.len() + 13; for arg in &cmd.args { - used_space += format!("{:?}", arg).len() + 1; + used_space += format!("{:}", arg).len() + 1; } if used_space > max_len { @@ -133,7 +133,7 @@ impl Debug for Application { write!(f, " {} ", cmd.name)?; for arg in &cmd.args { - write!(f, "{:?} ", arg)?; + write!(f, "{:} ", arg)?; } if cmd.opts.len() > 0 { diff --git a/crates/commander-core/src/lib.rs b/crates/commander-core/src/lib.rs index 7c74cff..79fdd8d 100644 --- a/crates/commander-core/src/lib.rs +++ b/crates/commander-core/src/lib.rs @@ -69,7 +69,7 @@ pub enum ArgumentType { /// For most of the time, you will not use it. #[doc(hidden)] #[derive(PartialEq, Eq)] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Argument { pub name: String, pub ty: ArgumentType, @@ -91,6 +91,7 @@ pub struct Argument { /// # Note /// It's generated by `commander_rust`, and it should be readonly. /// +#[derive(Debug)] pub struct Application { pub name: String, pub desc: String, @@ -142,6 +143,7 @@ pub struct Application { /// For most of the time, you will not use it. /// #[doc(hidden)] +#[derive(Debug)] pub struct Command { pub name: String, pub args: Vec, @@ -238,10 +240,21 @@ pub struct Cmd { pub struct Cli { pub cmd: Option, pub global_raws: HashMap, - pub direct_args: Vec, + pub direct_args: Option>, } impl Application { + pub fn new (name:String, desc:String, out:Option) -> Self { + Application { + name, + desc, + opts: vec![], + cmds: vec![], + direct_args: vec![], + out + } + } + /// Deriving `#[option(-h, --help, "output usage information")]` /// and `#[option(-V, --version, "output the version number")]` for all `Command` and `Application`. /// Dont use it! @@ -313,7 +326,7 @@ impl Cli { Cli { cmd: None, global_raws: HashMap::new(), - direct_args: vec![], + direct_args: None, } } @@ -397,9 +410,9 @@ impl Cli { global_raws, direct_args: { if instances[0].is_empty() && !instances[0].args.is_empty() { - Raw::divide_cmd(&instances[0], &app.direct_args) + Some(Raw::divide_cmd(&instances[0], &app.direct_args)) } else { - vec![] + None } } }) From 68993b11de78e934fb99becf96225c6a6b56ba26 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 1 Apr 2020 17:34:14 -0700 Subject: [PATCH 12/14] Simplify run! by moving some logic into Application --- crates/commander-core/src/lib.rs | 14 ++++++++++++-- crates/commander-macros/src/lib.rs | 7 +------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/crates/commander-core/src/lib.rs b/crates/commander-core/src/lib.rs index 79fdd8d..76306f0 100644 --- a/crates/commander-core/src/lib.rs +++ b/crates/commander-core/src/lib.rs @@ -243,7 +243,7 @@ pub struct Cli { pub direct_args: Option>, } -impl Application { +impl Application { pub fn new (name:String, desc:String, out:Option) -> Self { Application { name, @@ -255,6 +255,16 @@ impl Application { } } + pub fn parse(&self) -> Vec { + normalize(std::env::args().into_iter().collect::>(), self) + } + + pub fn cli(&mut self) -> Option { + self.derive(); + let instances = self.parse(); + Cli::from(&instances, &self) + } + /// Deriving `#[option(-h, --help, "output usage information")]` /// and `#[option(-V, --version, "output the version number")]` for all `Command` and `Application`. /// Dont use it! @@ -661,4 +671,4 @@ pub fn normalize (args: Vec, app: &Application) -> Vec TokenStream { // _commander_rust_main is generated by `entry` // let mut app = _commander_rust_main(None); - let ins; - - app.derive(); - ins = _commander_rust_normalize(std::env::args().into_iter().collect::>(), &app); - - let cli = _commander_rust_Cli::from(&ins, &app); + let cli = app.cli(); let fns = CALL_FNS.lock().unwrap(); if let Some(cli) = cli { From 957788be18b00de6913a354b7650596b1be123f3 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 2 Apr 2020 03:47:55 -0700 Subject: [PATCH 13/14] Delete redundunt macro code --- crates/commander-core/src/fmt.rs | 2 +- crates/commander-core/src/lib.rs | 144 ++++++++++++++++---- crates/commander-macros/src/lib.rs | 205 ++++++++++------------------- src/lib.rs | 2 +- 4 files changed, 190 insertions(+), 163 deletions(-) diff --git a/crates/commander-core/src/fmt.rs b/crates/commander-core/src/fmt.rs index 1b91b3c..944c3ff 100644 --- a/crates/commander-core/src/fmt.rs +++ b/crates/commander-core/src/fmt.rs @@ -95,7 +95,7 @@ impl Display for Application { write!(f, "[options]")?; } - write!(f, "\n\n{}\n\n", self.desc)?; + write!(f, "\n\n{}\n\n", self.description)?; if !self.opts.is_empty() { write!(f, "Public options: \n")?; diff --git a/crates/commander-core/src/lib.rs b/crates/commander-core/src/lib.rs index 76306f0..d1cc87b 100644 --- a/crates/commander-core/src/lib.rs +++ b/crates/commander-core/src/lib.rs @@ -30,10 +30,11 @@ mod pattern; use std::ops::Index; use std::collections::HashMap; -use pattern::{ Pattern, PatternType }; +use pattern::{Pattern, PatternType}; pub use raw::Raw; +use std::borrow::BorrowMut; use std::process::exit; - +use std::sync::Mutex; /// The type of argument. /// /// they are: @@ -93,8 +94,9 @@ pub struct Argument { /// #[derive(Debug)] pub struct Application { - pub name: String, - pub desc: String, + name: &'static str, + version: &'static str, + description: &'static str, pub cmds: Vec, pub opts: Vec, pub direct_args: Vec, @@ -244,27 +246,26 @@ pub struct Cli { } impl Application { - pub fn new (name:String, desc:String, out:Option) -> Self { - Application { + fn new( + name: &'static str, + version: &'static str, + description: &'static str, + cmds: Vec, + opts: Vec, + direct_args: Vec, + ) -> Self { + let mut application = Application { name, - desc, - opts: vec![], - cmds: vec![], - direct_args: vec![], - out - } - } - - pub fn parse(&self) -> Vec { - normalize(std::env::args().into_iter().collect::>(), self) + description, + version, + cmds, + opts, + direct_args, + out: None + }; + application.derive(); + application } - - pub fn cli(&mut self) -> Option { - self.derive(); - let instances = self.parse(); - Cli::from(&instances, &self) - } - /// Deriving `#[option(-h, --help, "output usage information")]` /// and `#[option(-V, --version, "output the version number")]` for all `Command` and `Application`. /// Dont use it! @@ -582,6 +583,103 @@ impl Instance { } } +type Handler = fn(&Vec, Cli) -> Out; + + +pub struct Commander { + call_fns: Mutex>>, + direct_fn: Mutex>>, + name: &'static str, + version: &'static str, + description: &'static str +} +impl Commander { + pub fn new(name: &'static str, version: &'static str, description: &'static str) -> Self { + Commander { + call_fns: Mutex::new(HashMap::new()), + direct_fn:Mutex::new(None), + name, + version, + description + } + } + + pub fn register_command_handler(&self, name: String, handler: Handler) { + let mut fns = self.call_fns.lock().unwrap(); + if !fns.contains_key(&name) { + fns.insert(name, handler); + } + } + pub fn register_direct_handler(&self, handler:Handler) { + let mut direct_fn = *self.direct_fn.lock().unwrap(); + *direct_fn.borrow_mut() = Some(handler); + } + + pub fn run( + &self, + cmds: Vec, + opts: Vec, + args: Vec, + ) -> Application { + let mut application = Application::new( + self.name, + self.description, + self.version, + cmds, + opts, + args, + ); + + + let args = std::env::args().into_iter().collect::>(); + let instances = normalize(args, &application); + let cli = Cli::from(&instances, &application); + + if let Some(cli) = cli { + if cli.has("help") || cli.has("h") { + // display sub-command usage + if cli.cmd.is_some() { + for cmd in &application.cmds { + if cmd.name == cli.get_name() { + println!("{:#}", cmd); + break; + } + } + } else { + // display cli usage + println!("{:#}", application); + } + } else if cli.has("version") || cli.has("V") { + println!("version: {}", self.version); + } else { + let fns = self.call_fns.lock().unwrap(); + if let Some(callback) = fns.get(&cli.get_name()) { + application.out = Some(callback(&cli.get_raws(), cli)); + } else { + if let Some(direct_args) = cli.direct_args.clone() { + let df = *self.direct_fn.lock().unwrap(); + + if let Some(f) = &df { + application.out = Some(f(&direct_args, cli)); + } else { + println!("ERRRRR"); + } + } else { + eprintln!( + "Unknown usage. Using `{} --help` for more help information.\n", + self.name + ); + } + } + } + } else { + println!("Using `{} --help` for more help information.", self.name); + } + + application + } +} + pub fn normalize (args: Vec, app: &Application) -> Vec { let mut instances = vec![]; let mut head = Instance::empty(); diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 43ed0b6..6ded781 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -12,14 +12,18 @@ use std::collections::HashMap; use lazy_static::lazy_static; use quote::quote; -use syn::{ Ident, ItemFn, parse_macro_input }; +use syn::{ Ident, ItemFn, parse_macro_input, ReturnType }; use tokens::{ CommandToken, OptionsToken}; +use std::sync::{Mutex}; use crate::errors::{DON_NOT_MATCH, ENTRY_ONLY_MAIN, NO_SUB_CMD_NAMES_MAIN, OPT_DUPLICATE_DEFINITION, compile_error_info, DIRECT_ONLY_ONCE}; use crate::tools::generate_call_fn; use crate::tokens::{PureArguments, check_arguments}; use syn::spanned::Spanned; +/// adds _commander_rust prefix to the name e.g. +/// prefix!("main") -> _commander_rs_main +/// prefix!("main", "silent") -> _commander_rs_main_silent macro_rules! prefix { ($($i: tt),*) => { { @@ -54,10 +58,12 @@ macro_rules! import { } lazy_static! { - static ref COMMAND_OPTIONS: std::sync::Mutex>> = std::sync::Mutex::new(HashMap::new()); - static ref OPTIONS: std::sync::Mutex> = std::sync::Mutex::new(vec![]); - static ref GET_FN_NAMES: std::sync::Mutex> = std::sync::Mutex::new(vec![]); - static ref DIRECT_NAME: std::sync::Mutex> = std::sync::Mutex::new(None); + static ref COMMAND_OPTIONS: Mutex>> = Mutex::new(HashMap::new()); + static ref OPTIONS: Mutex> = Mutex::new(vec![]); + + // Ever declared command name will is pushed into here (compile time). + static ref GET_FN_NAMES: Mutex> = Mutex::new(vec![]); + static ref DIRECT_NAME: Mutex> = Mutex::new(None); } @@ -117,20 +123,11 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { #error_info fn #get_fn() -> #cmd_token { - let mut command = #command; - let mut fns = CALL_FNS.lock().unwrap(); + COMMANDER.register_command_handler(String::from(#name), #call_fn_name); - if !fns.contains_key(#name) { - fns.insert(String::from(#name), #call_fn_name); - } + let mut command = #command; + command.opts = vec![#(#get_fns(),)*]; - command.opts = { - let mut v = vec![]; - #( - v.push(#get_fns()); - )* - v - }; command } @@ -256,10 +253,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { #func fn #direct_get_fn() -> Vec<#argument_ident> { - use std::borrow::BorrowMut; - let direct_fn: &mut Option, app: _commander_rust_Cli) #ret> = &mut (*DIRECT_FN.lock().unwrap()); - - *direct_fn.borrow_mut() = Some(#call_fn_name); + COMMANDER.register_direct_handler(#call_fn_name); #pure_args } @@ -282,8 +276,11 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let main: ItemFn = parse_macro_input!(main as ItemFn); let ident = &main.sig.ident; let ret = &main.sig.output; + let out = match ret { + ReturnType::Default => quote! { () }, + ReturnType::Type(_, t) => quote! { #t }, + }; let target = format!("{}", ident); - let opts = COMMAND_OPTIONS.lock().unwrap(); let imports = vec![ import!(Argument as _commander_rust_Argument), import!(ArgumentType as _commander_rust_ArgumentType), @@ -295,90 +292,33 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { import!(ls as _commander_rust_ls), import!(Application as _commander_rust_Application), import!(Cli as _commander_rust_Cli), + import!(Commander as _commander_rust_Commander), ]; - let get_fn = Ident::new(&prefix!("main"), ident.span()); - let app_token = Ident::new(&prefix!("Application"), ident.span()); - let get_fn_names = GET_FN_NAMES.lock().unwrap(); - let direct_fn = &(*DIRECT_NAME.lock().unwrap()); - let mut get_cmds_fns = vec![]; - let mut get_opts_fns = vec![]; let mut error_info = check_arguments(&pure_args.0); - // init can be used with fn main only + // entry can only be used with fn main. if target != String::from("main") { error_info = compile_error_info(ident.span(), ENTRY_ONLY_MAIN); } - if let Some(v) = opts.get("main") { - for i in v { - get_opts_fns.push(Ident::new(&prefix!("main", i), ident.span())); - } - } - - for i in get_fn_names.iter() { - get_cmds_fns.push(Ident::new(&prefix!(i), ident.span())); - } - let needed = quote! { + let entry = quote! { #error_info + // Import necessary commander modules with prefixed names. #(#imports)* - - type COMMANDER_Raw = _commander_rust_Raw; - type COMMANDER_Map = std::collections::HashMap, app: _commander_rust_Cli) #ret>; - type COMMANDER_Mutex = std::sync::Mutex; - + _commander_rust_ls! { - pub static ref CALL_FNS: COMMANDER_Mutex = COMMANDER_Mutex::new(COMMANDER_Map::new()); - pub static ref DIRECT_FN: std::sync::Mutex, app: _commander_rust_Cli) #ret>> = std::sync::Mutex::new(None); + pub static ref COMMANDER:_commander_rust_Commander<#out> = + _commander_rust_Commander::new( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_DESCRIPTION") + ); } - pub const APP_NAME: &'static str = env!("CARGO_PKG_NAME"); - pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - pub const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); - #main }; - - let direct_get_fn = if let Some(df) = direct_fn { - let direct_get_fn = Ident::new(&prefix!(df), Span2::call_site()); - quote! { #direct_get_fn(); } - } else { - quote! { vec![]; } - }; - - - TokenStream::from(quote! { - #needed - - fn #get_fn (out:Option) -> #app_token { - let mut application = #app_token { - name: String::from(APP_NAME), - desc: String::from(DESCRIPTION), - opts: vec![], - cmds: vec![], - direct_args: vec![], - out: out - }; - - application.opts = { - let mut v = vec![]; - #( - v.push(#get_opts_fns()); - )* - v - }; - application.cmds = { - let mut v = vec![]; - #( - v.push(#get_cmds_fns()); - )* - v - }; - application.direct_args = #direct_get_fn; - - application - } - }) + entry.into() } /// Run cli now. @@ -387,54 +327,43 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { /// See `Application` for more details. /// #[proc_macro] -pub fn run(_: TokenStream) -> TokenStream { - let result = quote! { - { - // _commander_rust_main is generated by `entry` - // - let mut app = _commander_rust_main(None); - let cli = app.cli(); - let fns = CALL_FNS.lock().unwrap(); - - if let Some(cli) = cli { - if cli.has("help") || cli.has("h") { - // display sub-command usage - if cli.cmd.is_some() { - for cmd in &app.cmds { - if cmd.name == cli.get_name() { - println!("{:#}", cmd); - break; - } - } - } else { - // display cli usage - println!("{:#}", app); - } - } else if cli.has("version") || cli.has("V") { - println!("version: {}", VERSION); - } else { - if let Some(callback) = fns.get(&cli.get_name()) { - app.out = Some(callback(&cli.get_raws(), cli)); - } else { - if let Some(direct_args) = cli.direct_args.clone() { - let df = *DIRECT_FN.lock().unwrap(); - - if let Some(f) = &df { - app.out = Some(f(&direct_args, cli)); - } else { - println!("ERRRRR"); - } - } else { - eprintln!("Unknown usage. Using `{} --help` for more help information.\n", APP_NAME); - } - } - } - } else { - println!("Using `{} --help` for more help information.", APP_NAME); - } +pub fn run(input: TokenStream) -> TokenStream { + println!("{:#?}", input); + // let app_token = Ident::new(&prefix!("Application"), ident.span()); + let mut get_cmds_fns:Vec = vec![]; + let mut get_opts_fns:Vec = vec![]; + let direct_fn = &(*DIRECT_NAME.lock().unwrap()); - app + // Options for main are global. For each option e.g. `--foo` we will collect + // identifier (in this example `_commander_rs_main_foo`) into + // `get_opts_fns`. + let opts = COMMAND_OPTIONS.lock().unwrap(); + if let Some(v) = opts.get("main") { + for i in v { + get_opts_fns.push(Ident::new(&prefix!("main", i), Span2::call_site())); } + } + + // For each registered command e.g. `find` we collect identifier + // (in this example `_commander_rs_find`) into `get_cmds_fns`. + let get_fn_names = GET_FN_NAMES.lock().unwrap(); + for i in get_fn_names.iter() { + get_cmds_fns.push(Ident::new(&prefix!(i), Span2::call_site())); + } + + let direct_get_fn = if let Some(df) = direct_fn { + let direct_get = Ident::new(&prefix!(df), Span2::call_site()); + quote! { #direct_get() } + } else { + quote! { vec![] } + }; + + let run = quote! { + COMMANDER.run( + vec![#(#get_cmds_fns(),)*], + vec![#(#get_opts_fns(),)*], + #direct_get_fn + ) }; - result.into() + run.into() } diff --git a/src/lib.rs b/src/lib.rs index 67c07dc..51d0100 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -pub use commander_core::{ Argument, ArgumentType, Command, Options, normalize, Raw, Application, Cli, Cmd, Instance }; +pub use commander_core::{ Argument, ArgumentType, Command, Options, normalize, Raw, Application, Cli, Cmd, Instance, Commander }; pub use commander_macros::{ command, option, run, entry, direct }; pub use lazy_static::{ lazy_static as ls }; \ No newline at end of file From 1a56d4b2d311eda916eb71b7e59a39af820c23dc Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Thu, 2 Apr 2020 04:07:43 -0700 Subject: [PATCH 14/14] Use root names to remove need for imports & aliasing --- crates/commander-macros/src/lib.rs | 45 +++++---------------------- crates/commander-macros/src/tokens.rs | 14 ++++----- crates/commander-macros/src/tools.rs | 2 +- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/crates/commander-macros/src/lib.rs b/crates/commander-macros/src/lib.rs index 6ded781..e415b99 100644 --- a/crates/commander-macros/src/lib.rs +++ b/crates/commander-macros/src/lib.rs @@ -44,18 +44,6 @@ macro_rules! prefix { } } -macro_rules! import { - ($o: ident as $r: ident) => { - quote! { - use commander_rust::{ $o as $r }; - } - }; - ($o: ident as $r: ident from $f: path) => { - quote! { - use $f::{ $o as $r }; - } - } -} lazy_static! { static ref COMMAND_OPTIONS: Mutex>> = Mutex::new(HashMap::new()); @@ -87,7 +75,6 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { let ident = &method.sig.ident; let name = format!("{}", ident); let get_fn = Ident::new(&prefix!(name), ident.span()); - let cmd_token = Ident::new(&prefix!("Command"), ident.span()); let opts = COMMAND_OPTIONS.lock().unwrap(); let mut get_fn_names = GET_FN_NAMES.lock().unwrap(); let mut get_fns = vec![]; @@ -122,7 +109,7 @@ pub fn command(cmd: TokenStream, method: TokenStream) -> TokenStream { TokenStream::from(quote! { #error_info - fn #get_fn() -> #cmd_token { + fn #get_fn() -> ::commander_rust::Command { COMMANDER.register_command_handler(String::from(#name), #call_fn_name); let mut command = #command; @@ -162,7 +149,6 @@ pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { let opt_name = format!("{}", option.long); let fn_name = prefix!(name, opt_name); let get_fn = Ident::new(&fn_name, option.long.span()); - let opt_token = Ident::new(&prefix!("Options"), ident.span()); let mut opts = COMMAND_OPTIONS.lock().unwrap(); let mut error_info = TokenStream2::new(); let mut all_opts = OPTIONS.lock().unwrap(); @@ -190,7 +176,7 @@ pub fn option(opt: TokenStream, method: TokenStream) -> TokenStream { TokenStream::from(quote! { #error_info - fn #get_fn() -> #opt_token { + fn #get_fn() -> ::commander_rust::Options { #option } @@ -235,7 +221,6 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { let pure_args: PureArguments = parse_macro_input!(pure_args as PureArguments); let direct_fn: &mut Option = &mut (*DIRECT_NAME.lock().unwrap()); let direct_get_fn = Ident::new(&prefix!(name), ident.span()); - let argument_ident = Ident::new(&prefix!("Argument"), ident.span()); let call_fn_name = Ident::new(&prefix!(name, "call"), ident.span()); let call_fn = generate_call_fn(args, &call_fn_name, ident, ret); let mut error_info: TokenStream2 = check_arguments(&pure_args.0); @@ -252,7 +237,7 @@ pub fn direct(pure_args: TokenStream, func: TokenStream) -> TokenStream { #func - fn #direct_get_fn() -> Vec<#argument_ident> { + fn #direct_get_fn() -> Vec<::commander_rust::Argument> { COMMANDER.register_direct_handler(#call_fn_name); #pure_args } @@ -281,19 +266,6 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { ReturnType::Type(_, t) => quote! { #t }, }; let target = format!("{}", ident); - let imports = vec![ - import!(Argument as _commander_rust_Argument), - import!(ArgumentType as _commander_rust_ArgumentType), - import!(Command as _commander_rust_Command), - import!(Options as _commander_rust_Options), - import!(Raw as _commander_rust_Raw), - import!(normalize as _commander_rust_normalize), - import!(Instance as _commander_rust_Instance), - import!(ls as _commander_rust_ls), - import!(Application as _commander_rust_Application), - import!(Cli as _commander_rust_Cli), - import!(Commander as _commander_rust_Commander), - ]; let mut error_info = check_arguments(&pure_args.0); // entry can only be used with fn main. @@ -304,12 +276,10 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { let entry = quote! { #error_info - // Import necessary commander modules with prefixed names. - #(#imports)* - - _commander_rust_ls! { - pub static ref COMMANDER:_commander_rust_Commander<#out> = - _commander_rust_Commander::new( + + commander_rust::ls! { + pub static ref COMMANDER: ::commander_rust::Commander<#out> = + ::commander_rust::Commander::new( env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_DESCRIPTION") @@ -329,7 +299,6 @@ pub fn entry(pure_arguments: TokenStream, main: TokenStream) -> TokenStream { #[proc_macro] pub fn run(input: TokenStream) -> TokenStream { println!("{:#?}", input); - // let app_token = Ident::new(&prefix!("Application"), ident.span()); let mut get_cmds_fns:Vec = vec![]; let mut get_opts_fns:Vec = vec![]; let direct_fn = &(*DIRECT_NAME.lock().unwrap()); diff --git a/crates/commander-macros/src/tokens.rs b/crates/commander-macros/src/tokens.rs index dac18ed..44bb086 100644 --- a/crates/commander-macros/src/tokens.rs +++ b/crates/commander-macros/src/tokens.rs @@ -23,22 +23,22 @@ impl ToTokens for ArgumentType { match self { &ArgumentType::RequiredSingle => { (quote! { - _commander_rust_ArgumentType::RequiredSingle + ::commander_rust::ArgumentType::RequiredSingle }).to_tokens(tokens); }, &ArgumentType::RequiredMultiple => { (quote! { - _commander_rust_ArgumentType::RequiredMultiple + ::commander_rust::ArgumentType::RequiredMultiple }).to_tokens(tokens); }, &ArgumentType::OptionalSingle => { (quote! { - _commander_rust_ArgumentType::OptionalSingle + ::commander_rust::ArgumentType::OptionalSingle }).to_tokens(tokens); }, &ArgumentType::OptionalMultiple => { (quote! { - _commander_rust_ArgumentType::OptionalMultiple + ::commander_rust::ArgumentType::OptionalMultiple }).to_tokens(tokens); } } @@ -60,7 +60,7 @@ impl ToTokens for Argument { } = self; let name = format!("{}", name); let expand = quote! { - _commander_rust_Argument { + ::commander_rust::Argument { name: String::from(#name), ty: #ty, } @@ -154,7 +154,7 @@ impl ToTokens for CommandToken { quote!(None) }; let expand = quote! { - _commander_rust_Command { + ::commander_rust::Command { name: String::from(#name), args: vec![#( #args ),*], desc: #desc, @@ -279,7 +279,7 @@ impl ToTokens for OptionsToken { }; let expand = quote! { - _commander_rust_Options { + ::commander_rust::Options { short: String::from(#short), long: String::from(#long), arg: #arg, diff --git a/crates/commander-macros/src/tools.rs b/crates/commander-macros/src/tools.rs index e7d07c2..43df924 100644 --- a/crates/commander-macros/src/tools.rs +++ b/crates/commander-macros/src/tools.rs @@ -54,7 +54,7 @@ pub fn generate_call_fn( } (quote! { - fn #call_fn_name(raws: &Vec<_commander_rust_Raw>, cli: _commander_rust_Cli) #ret { + fn #call_fn_name(raws: &Vec<::commander_rust::Raw>, cli: ::commander_rust::Cli) #ret { #fn_name(#(#tokens,)*) } }).into()