Skip to content

Commit

Permalink
feat: support hooks (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden committed Dec 22, 2023
1 parent 9353edc commit fc96394
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 3 deletions.
34 changes: 34 additions & 0 deletions docs/hooks.md
Original file line number Diff line number Diff line change
@@ -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
```
20 changes: 20 additions & 0 deletions examples/hooks.sh
Original file line number Diff line number Diff line change
@@ -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" "$@")"
20 changes: 18 additions & 2 deletions src/argc_value.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -13,6 +15,7 @@ pub enum ArgcValue {
ExtraPositionalMultiple(Vec<String>),
CmdFn(String),
ParamFn(String),
HookFns((bool, bool)),
Error((String, i32)),
}

Expand All @@ -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) => {
Expand Down Expand Up @@ -90,6 +94,14 @@ impl ArgcValue {
.collect::<Vec<String>>();
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();
Expand All @@ -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")
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 2 additions & 0 deletions src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
41 changes: 41 additions & 0 deletions tests/hook_fn.rs
Original file line number Diff line number Diff line change
@@ -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"]);
}
15 changes: 15 additions & 0 deletions tests/snapshots/integration__hook_fn__hook_only_before.snap
Original file line number Diff line number Diff line change
@@ -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

16 changes: 16 additions & 0 deletions tests/snapshots/integration__hook_fn__hook_with_main.snap
Original file line number Diff line number Diff line change
@@ -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

16 changes: 16 additions & 0 deletions tests/snapshots/integration__hook_fn__hook_with_subcmd.snap
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions tests/snapshots/integration__hook_fn__hook_without_subcmd.snap
Original file line number Diff line number Diff line change
@@ -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

1 change: 1 addition & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod compgen;
mod create;
mod export;
mod fail;
mod hook_fn;
mod main_fn;
mod misc;
mod parallel;
Expand Down

0 comments on commit fc96394

Please sign in to comment.