diff --git a/docs/hooks.md b/docs/hooks.md new file mode 100644 index 00000000..4ebe1fe0 --- /dev/null +++ b/docs/hooks.md @@ -0,0 +1,34 @@ +# Hooks + +Argc supports two kinds of hooks: + +- `_argc_before`: call before performing any operation +- `_argc_before`: call after running the command function + +## Example + +```sh +# @flag --foo +# @option --bar + +_argc_before() { + echo before +} + +_argc_after() { + echo after +} + +main() { + echo main +} + +eval "$(argc --argc-eval "$0" "$@")" +``` + +``` +$ prog +before +main +after +``` diff --git a/examples/hooks.sh b/examples/hooks.sh new file mode 100644 index 00000000..be8efd16 --- /dev/null +++ b/examples/hooks.sh @@ -0,0 +1,20 @@ +#/usr/bin/env node + +set -e + +# @flag --foo +# @option --bar + +_argc_before() { + echo before +} + +_argc_after() { + echo after +} + +main() { + echo main +} + +eval "$(argc --argc-eval "$0" "$@")" \ No newline at end of file diff --git a/src/argc_value.rs b/src/argc_value.rs index e9516788..27dca735 100644 --- a/src/argc_value.rs +++ b/src/argc_value.rs @@ -1,6 +1,8 @@ use crate::utils::escape_shell_words; pub const VARIABLE_PREFIX: &str = "argc"; +pub const BEFORE_HOOK: &str = "_argc_before"; +pub const AFTER_HOOK: &str = "_argc_after"; #[derive(Debug, PartialEq, Eq)] pub enum ArgcValue { @@ -13,6 +15,7 @@ pub enum ArgcValue { ExtraPositionalMultiple(Vec), CmdFn(String), ParamFn(String), + HookFns((bool, bool)), Error((String, i32)), } @@ -21,6 +24,7 @@ impl ArgcValue { let mut output = vec![]; let mut last = String::new(); let mut positional_args = vec![]; + let (mut before_hook, mut after_hook) = (false, false); for value in values { match value { ArgcValue::Single(name, value) => { @@ -90,6 +94,14 @@ impl ArgcValue { .collect::>(); positional_args.extend(values); } + ArgcValue::HookFns((before, after)) => { + if *before { + before_hook = *before; + } + if *after { + after_hook = *after; + } + } ArgcValue::CmdFn(name) => { if positional_args.is_empty() { last = name.to_string(); @@ -116,11 +128,15 @@ impl ArgcValue { VARIABLE_PREFIX, positional_args.join(" ") )); - + if before_hook { + output.push(BEFORE_HOOK.to_string()) + } if !last.is_empty() { output.push(last); + if after_hook { + output.push(AFTER_HOOK.to_string()) + } } - output.join("\n") } } diff --git a/src/command/mod.rs b/src/command/mod.rs index a5456ce8..d0d6d75e 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -4,7 +4,7 @@ mod root_data; use self::names_checker::NamesChecker; use self::root_data::RootData; -use crate::argc_value::ArgcValue; +use crate::argc_value::{ArgcValue, AFTER_HOOK, BEFORE_HOOK}; use crate::matcher::Matcher; use crate::param::{FlagOptionParam, PositionalParam}; use crate::parser::{parse, parse_symbol, Event, EventData, EventScope, Position}; @@ -537,6 +537,11 @@ impl Command { } } + pub(crate) fn exist_hook_fns(&self) -> (bool, bool) { + let fns = &self.root.borrow().fns; + (fns.contains_key(BEFORE_HOOK), fns.contains_key(AFTER_HOOK)) + } + pub(crate) fn delegated(&self) -> bool { self.subcommands.is_empty() && self.flag_option_params.is_empty() diff --git a/src/matcher.rs b/src/matcher.rs index 4457b0bc..a6234daf 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -330,6 +330,8 @@ impl<'a, 'b> Matcher<'a, 'b> { self.positional_args.iter().map(|v| v.to_string()).collect(), )); } + let (before, after) = cmd.exist_hook_fns(); + output.push(ArgcValue::HookFns((before, after))); if let Some(cmd_fn) = cmd.get_cmd_fn(&cmd_paths) { output.push(ArgcValue::CmdFn(cmd_fn)); } diff --git a/tests/hook_fn.rs b/tests/hook_fn.rs new file mode 100644 index 00000000..29bf042b --- /dev/null +++ b/tests/hook_fn.rs @@ -0,0 +1,41 @@ +use crate::*; + +#[test] +fn hook_without_subcmd() { + let script = r###" +_argc_before() { :; } +_argc_after() { :; } +"###; + snapshot!(script, &["prog"]); +} + +#[test] +fn hook_with_main() { + let script = r###" +_argc_before() { :; } +_argc_after() { :; } +main() { :; } +"###; + snapshot!(script, &["prog"]); +} + +#[test] +fn hook_only_before() { + let script = r###" +_argc_before() { :; } +main() { :; } +"###; + snapshot!(script, &["prog"]); +} + +#[test] +fn hook_with_subcmd() { + let script = r###" +_argc_before() { :; } +_argc_after() { :; } + +# @cmd +cmd() { :; } +"###; + snapshot!(script, &["prog", "cmd"]); +} diff --git a/tests/snapshots/integration__hook_fn__hook_only_before.snap b/tests/snapshots/integration__hook_fn__hook_only_before.snap new file mode 100644 index 00000000..7cd9e617 --- /dev/null +++ b/tests/snapshots/integration__hook_fn__hook_only_before.snap @@ -0,0 +1,15 @@ +--- +source: tests/hook_fn.rs +expression: data +--- +RUN +prog + +OUTPUT +argc__args=( prog ) +argc__cmd_arg_index=0 +argc__fn=main +argc__positionals=( ) +_argc_before +main + diff --git a/tests/snapshots/integration__hook_fn__hook_with_main.snap b/tests/snapshots/integration__hook_fn__hook_with_main.snap new file mode 100644 index 00000000..7a6eeccb --- /dev/null +++ b/tests/snapshots/integration__hook_fn__hook_with_main.snap @@ -0,0 +1,16 @@ +--- +source: tests/hook_fn.rs +expression: data +--- +RUN +prog + +OUTPUT +argc__args=( prog ) +argc__cmd_arg_index=0 +argc__fn=main +argc__positionals=( ) +_argc_before +main +_argc_after + diff --git a/tests/snapshots/integration__hook_fn__hook_with_subcmd.snap b/tests/snapshots/integration__hook_fn__hook_with_subcmd.snap new file mode 100644 index 00000000..a11f32a2 --- /dev/null +++ b/tests/snapshots/integration__hook_fn__hook_with_subcmd.snap @@ -0,0 +1,16 @@ +--- +source: tests/hook_fn.rs +expression: data +--- +RUN +prog cmd + +OUTPUT +argc__args=( prog cmd ) +argc__cmd_arg_index=1 +argc__fn=cmd +argc__positionals=( ) +_argc_before +cmd +_argc_after + diff --git a/tests/snapshots/integration__hook_fn__hook_without_subcmd.snap b/tests/snapshots/integration__hook_fn__hook_without_subcmd.snap new file mode 100644 index 00000000..ea1c59c5 --- /dev/null +++ b/tests/snapshots/integration__hook_fn__hook_without_subcmd.snap @@ -0,0 +1,13 @@ +--- +source: tests/hook_fn.rs +expression: data +--- +RUN +prog + +OUTPUT +argc__args=( prog ) +argc__cmd_arg_index=0 +argc__positionals=( ) +_argc_before + diff --git a/tests/tests.rs b/tests/tests.rs index fb16c1dc..20b4d50d 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -13,6 +13,7 @@ mod compgen; mod create; mod export; mod fail; +mod hook_fn; mod main_fn; mod misc; mod parallel;