1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! Port of `_call_function` from
//! `Completion/Base/Utility/_call_function`.
//!
//! Full upstream body (32 lines verbatim):
//! ```text
//! sh: 1 #autoload
//! sh: 5 # Usage: _call_function <return> <name> [ <args> ... ]
//! sh:15 local _name _ret
//! sh:17 [[ "$1" != (|-) ]] && _name="$1"
//! sh:19 shift
//! sh:21 if (( $+functions[$1] )); then
//! sh:22 "$@"
//! sh:23 _ret="$?"
//! sh:25 [[ -n "$_name" ]] && eval "${_name}=${_ret}"
//! sh:27 compstate[restore]=''
//! sh:29 return 0
//! sh:30 fi
//! sh:32 return 1
//! ```
//!
//! Calls a shell function if it exists, captures its return into
//! the param named by `$1` (when not empty/`-`), and clears
//! `$compstate[restore]`. Returns 0 if the fn was called, 1
//! otherwise.
use crate::ported::exec_hooks::dispatch_function_call;
use crate::ported::params::setsparam;
use crate::ported::utils::getshfunc;
use crate::ported::zle::compcore::set_compstate_str;
/// `_call_function` — invoke a named shell function with the rest
/// of the args, storing its exit status under the param named by
/// `$1`. Returns 0 if invoked, 1 if the named fn doesn't exist.
pub fn _call_function(args: &[String]) -> i32 {
// sh:17 $1 is the return-storage param name (or `-`/empty = no store)
let result_name = args.first().cloned().unwrap_or_default();
let store_into: Option<String> = if result_name.is_empty() || result_name == "-" {
None
} else {
Some(result_name)
};
// sh:19 shift
let rest: &[String] = if args.is_empty() { &[] } else { &args[1..] };
// sh:21 test fn existence
let fn_name = match rest.first() {
Some(n) => n.clone(),
None => return 1,
};
if getshfunc(&fn_name).is_none() {
// sh:32
return 1;
}
// sh:22-23 invoke and capture exit
let call_args: &[String] = if rest.len() > 1 { &rest[1..] } else { &[] };
let ret = dispatch_function_call(&fn_name, call_args).unwrap_or(1);
// sh:25
if let Some(name) = store_into {
let _ = setsparam(&name, &ret.to_string());
}
// sh:27
set_compstate_str("restore", "");
// sh:29
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn missing_fn_returns_one() {
// sh:32 — the named fn doesn't exist → return 1.
let _g = crate::test_util::global_state_lock();
assert_eq!(
_call_function(&["ret".to_string(), "nonexistent_fn".to_string()]),
1
);
}
#[test]
fn empty_args_returns_one() {
let _g = crate::test_util::global_state_lock();
assert_eq!(_call_function(&[]), 1);
}
}