Skip to main content

shrub_core/shell/
funcs.rs

1//! Function support for shells.
2
3use crate::results::ExecutionWaitResult;
4use crate::{ExecutionParameters, ExecutionResult, commands, error, extensions, functions};
5
6impl<SE: extensions::ShellExtensions> crate::Shell<SE> {
7    /// Returns the function definition environment for this shell.
8    pub const fn funcs(&self) -> &functions::FunctionEnv {
9        &self.funcs
10    }
11
12    /// Returns a mutable reference to the function definition environment for
13    /// this shell.
14    pub const fn funcs_mut(&mut self) -> &mut functions::FunctionEnv {
15        &mut self.funcs
16    }
17
18    /// Tries to undefine a function in the shell's environment. Returns whether
19    /// or not a definition was removed.
20    ///
21    /// # Arguments
22    ///
23    /// * `name` - The name of the function to undefine.
24    pub fn undefine_func(&mut self, name: &str) -> bool {
25        self.funcs.remove(name).is_some()
26    }
27
28    /// Defines a function in the shell's environment. If a function already
29    /// exists with the given name, it is replaced with the new definition.
30    ///
31    /// # Arguments
32    ///
33    /// * `name` - The name of the function to define.
34    /// * `definition` - The function's definition.
35    /// * `source_info` - Source information for the function definition.
36    pub fn define_func(
37        &mut self,
38        name: impl Into<String>,
39        definition: shrub_parser::ast::FunctionDefinition,
40        source_info: &crate::SourceInfo,
41    ) {
42        let reg = functions::Registration::new(definition, source_info);
43        self.funcs.update(name.into(), reg);
44    }
45
46    /// Tries to return a mutable reference to the registration for a named
47    /// function. Returns `None` if no such function was found.
48    ///
49    /// # Arguments
50    ///
51    /// * `name` - The name of the function to lookup
52    pub fn func_mut(&mut self, name: &str) -> Option<&mut functions::Registration> {
53        self.funcs.get_mut(name)
54    }
55
56    /// Tries to define a function in the shell's environment using the given
57    /// string as its body.
58    ///
59    /// # Arguments
60    ///
61    /// * `name` - The name of the function
62    /// * `body_text` - The body of the function, expected to start with "()".
63    ///
64    /// # Errors
65    ///
66    /// Returns [`crate::error::ErrorKind::FunctionParseError`] if `body_text`
67    /// is not a valid function body (`()` followed by a compound command).
68    pub fn define_func_from_str(
69        &mut self,
70        name: impl Into<String>,
71        body_text: &str,
72    ) -> Result<(), error::Error> {
73        let name = name.into();
74
75        let mut parser =
76            super::parsing::create_parser(body_text.as_bytes(), &self.parser_options());
77        let func_body = parser.parse_function_parens_and_body().map_err(|e| {
78            error::Error::from(error::ErrorKind::FunctionParseError(name.clone(), e))
79        })?;
80
81        let def = shrub_parser::ast::FunctionDefinition {
82            fname: name.clone().into(),
83            body: func_body,
84        };
85
86        self.define_func(name, def, &crate::SourceInfo::default());
87
88        Ok(())
89    }
90
91    /// Invokes a function defined in this shell, returning its execution
92    /// result.
93    ///
94    /// # Arguments
95    ///
96    /// * `name` - The name of the function to invoke.
97    /// * `args` - The arguments to pass to the function.
98    /// * `params` - Execution parameters to use for the invocation.
99    ///
100    /// # Errors
101    ///
102    /// - [`crate::error::ErrorKind::FunctionNotFound`] if no function has the
103    ///   given name.
104    /// - [`crate::error::ErrorKind::Unimplemented`] if the function's execution
105    ///   is stopped (job control).
106    /// - Any error raised while running the function body, such as
107    ///   [`crate::error::ErrorKind::MaxFunctionCallDepthExceeded`].
108    pub async fn invoke_function<N: AsRef<str>, I: IntoIterator<Item = A>, A: AsRef<str>>(
109        &mut self,
110        name: N,
111        args: I,
112        params: ExecutionParameters,
113    ) -> Result<ExecutionResult, error::Error> {
114        let name = name.as_ref();
115        let command_name = String::from(name);
116
117        let func_registration = self
118            .funcs
119            .get(name)
120            .ok_or_else(|| error::ErrorKind::FunctionNotFound(name.to_owned()))?
121            .to_owned();
122
123        let context = commands::ExecutionContext {
124            shell: self,
125            command_name,
126            params,
127        };
128
129        let command_args = args
130            .into_iter()
131            .map(|s| commands::CommandArg::String(String::from(s.as_ref())))
132            .collect::<Vec<_>>();
133
134        let result =
135            commands::invoke_shell_function(func_registration, context, &command_args).await?;
136
137        match result.wait().await? {
138            ExecutionWaitResult::Completed(result) => Ok(result),
139            ExecutionWaitResult::Stopped(..) => {
140                error::unimp("stopped child from function invocation")
141            }
142        }
143    }
144}