yash_env/
function.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Type definitions for functions.
18//!
19//! This module provides data types for defining shell functions.
20
21use crate::Env;
22use crate::source::Location;
23use std::borrow::Borrow;
24use std::collections::HashSet;
25use std::fmt::Debug;
26use std::fmt::Display;
27use std::hash::Hash;
28use std::hash::Hasher;
29use std::iter::FusedIterator;
30use std::pin::Pin;
31use std::rc::Rc;
32use thiserror::Error;
33
34/// Trait for the body of a [`Function`]
35pub trait FunctionBody: Debug + Display {
36    /// Executes the function body in the given environment.
37    ///
38    /// The implementation of this method is expected to update
39    /// `env.exit_status` reflecting the result of the function execution.
40    #[allow(async_fn_in_trait)] // We don't support Send
41    async fn execute(&self, env: &mut Env) -> crate::semantics::Result;
42}
43
44/// Dyn-compatible adapter for the [`FunctionBody`] trait
45///
46/// This is a dyn-compatible version of the [`FunctionBody`] trait.
47///
48/// This trait is automatically implemented for all types that implement
49/// [`FunctionBody`].
50pub trait FunctionBodyObject: Debug + Display {
51    /// Executes the function body in the given environment.
52    ///
53    /// The implementation of this method is expected to update
54    /// `env.exit_status` reflecting the result of the function execution.
55    fn execute<'a>(
56        &'a self,
57        env: &'a mut Env,
58    ) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>>;
59}
60
61impl<T: FunctionBody + ?Sized> FunctionBodyObject for T {
62    fn execute<'a>(
63        &'a self,
64        env: &'a mut Env,
65    ) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>> {
66        Box::pin(self.execute(env))
67    }
68}
69
70/// Definition of a function.
71#[derive(Clone, Debug)]
72pub struct Function {
73    /// String that identifies the function.
74    pub name: String,
75
76    /// Command that is executed when the function is called.
77    ///
78    /// This is wrapped in `Rc` so that we don't have to clone the entire
79    /// command when we define a function. The function definition command only
80    /// clones the `Rc` object from the abstract syntax tree to create a
81    /// `Function` object.
82    pub body: Rc<dyn FunctionBodyObject>,
83
84    /// Location of the function definition command that defined this function.
85    pub origin: Location,
86
87    /// Optional location where this function was made read-only.
88    ///
89    /// If this function is not read-only, `read_only_location` is `None`.
90    /// Otherwise, `read_only_location` is the location of the simple command
91    /// that executed the `readonly` built-in that made this function read-only.
92    pub read_only_location: Option<Location>,
93}
94
95impl Function {
96    /// Creates a new function.
97    ///
98    /// This is a convenience function for constructing a `Function` object.
99    /// The `read_only_location` is set to `None`.
100    #[inline]
101    #[must_use]
102    pub fn new<N: Into<String>, B: Into<Rc<dyn FunctionBodyObject>>>(
103        name: N,
104        body: B,
105        origin: Location,
106    ) -> Self {
107        Function {
108            name: name.into(),
109            body: body.into(),
110            origin,
111            read_only_location: None,
112        }
113    }
114
115    /// Makes the function read-only.
116    ///
117    /// This is a convenience function for doing
118    /// `self.read_only_location = Some(location)` in a method chain.
119    #[inline]
120    #[must_use]
121    pub fn make_read_only(mut self, location: Location) -> Self {
122        self.read_only_location = Some(location);
123        self
124    }
125
126    /// Whether this function is read-only or not.
127    #[must_use]
128    pub const fn is_read_only(&self) -> bool {
129        self.read_only_location.is_some()
130    }
131}
132
133/// Compares two functions for equality.
134///
135/// Two functions are considered equal if all their members are equal.
136/// This includes comparing the `body` members by pointer equality.
137impl PartialEq for Function {
138    fn eq(&self, other: &Self) -> bool {
139        self.name == other.name
140            && Rc::ptr_eq(&self.body, &other.body)
141            && self.origin == other.origin
142            && self.read_only_location == other.read_only_location
143    }
144}
145
146impl Eq for Function {}
147
148/// Wrapper of [`Function`] for inserting into a hash set.
149///
150/// A `HashEntry` wraps a `Function` in `Rc` so that the `Function` object can
151/// outlive the execution of the function which may redefine or unset the
152/// function itself. A simple command that executes the function clones the
153/// `Rc` object from the function set and retains it until the command
154/// terminates.
155///
156/// The `Hash` and `PartialEq` implementation for `HashEntry` only compares
157/// the names of the functions.
158#[derive(Clone, Debug, Eq)]
159struct HashEntry(Rc<Function>);
160
161impl PartialEq for HashEntry {
162    /// Compares the names of two hash entries.
163    ///
164    /// Members of [`Function`] other than `name` are not considered in this
165    /// function.
166    fn eq(&self, other: &HashEntry) -> bool {
167        self.0.name == other.0.name
168    }
169}
170
171impl Hash for HashEntry {
172    /// Hashes the name of the function.
173    ///
174    /// Members of [`Function`] other than `name` are not considered in this
175    /// function.
176    fn hash<H: Hasher>(&self, state: &mut H) {
177        self.0.name.hash(state)
178    }
179}
180
181impl Borrow<str> for HashEntry {
182    fn borrow(&self) -> &str {
183        &self.0.name
184    }
185}
186
187/// Collection of functions.
188#[derive(Clone, Debug, Default)]
189pub struct FunctionSet {
190    entries: HashSet<HashEntry>,
191}
192
193/// Error redefining a read-only function.
194#[derive(Clone, Debug, Eq, Error, PartialEq)]
195#[error("cannot redefine read-only function `{}`", .existing.name)]
196#[non_exhaustive]
197pub struct DefineError {
198    /// Existing read-only function
199    pub existing: Rc<Function>,
200    /// New function that tried to redefine the existing function
201    pub new: Rc<Function>,
202}
203
204/// Error unsetting a read-only function.
205#[derive(Clone, Debug, Eq, Error, PartialEq)]
206#[error("cannot unset read-only function `{}`", .existing.name)]
207#[non_exhaustive]
208pub struct UnsetError {
209    /// Existing read-only function
210    pub existing: Rc<Function>,
211}
212
213/// Unordered iterator over functions in a function set.
214///
215/// This iterator is created by [`FunctionSet::iter`].
216#[derive(Clone, Debug)]
217pub struct Iter<'a> {
218    inner: std::collections::hash_set::Iter<'a, HashEntry>,
219}
220
221impl FunctionSet {
222    /// Creates a new empty function set.
223    #[must_use]
224    pub fn new() -> Self {
225        FunctionSet::default()
226    }
227
228    /// Returns the function with the given name.
229    #[must_use]
230    pub fn get(&self, name: &str) -> Option<&Rc<Function>> {
231        self.entries.get(name).map(|entry| &entry.0)
232    }
233
234    /// Returns the number of functions in the set.
235    #[inline]
236    #[must_use]
237    pub fn len(&self) -> usize {
238        self.entries.len()
239    }
240
241    /// Returns `true` if the set contains no functions.
242    #[inline]
243    #[must_use]
244    pub fn is_empty(&self) -> bool {
245        self.entries.is_empty()
246    }
247
248    /// Inserts a function into the set.
249    ///
250    /// If a function with the same name already exists, it is replaced and
251    /// returned unless it is read-only, in which case `DefineError` is
252    /// returned.
253    pub fn define<F: Into<Rc<Function>>>(
254        &mut self,
255        function: F,
256    ) -> Result<Option<Rc<Function>>, DefineError> {
257        #[allow(clippy::mutable_key_type)]
258        fn inner(
259            entries: &mut HashSet<HashEntry>,
260            new: Rc<Function>,
261        ) -> Result<Option<Rc<Function>>, DefineError> {
262            match entries.get(new.name.as_str()) {
263                Some(existing) if existing.0.is_read_only() => Err(DefineError {
264                    existing: Rc::clone(&existing.0),
265                    new,
266                }),
267
268                _ => Ok(entries.replace(HashEntry(new)).map(|entry| entry.0)),
269            }
270        }
271        inner(&mut self.entries, function.into())
272    }
273
274    /// Removes a function from the set.
275    ///
276    /// This function returns the previously defined function if it exists.
277    /// However, if the function is read-only, `UnsetError` is returned.
278    pub fn unset(&mut self, name: &str) -> Result<Option<Rc<Function>>, UnsetError> {
279        match self.entries.get(name) {
280            Some(entry) if entry.0.is_read_only() => Err(UnsetError {
281                existing: Rc::clone(&entry.0),
282            }),
283
284            _ => Ok(self.entries.take(name).map(|entry| entry.0)),
285        }
286    }
287
288    /// Returns an iterator over functions in the set.
289    ///
290    /// The order of iteration is not specified.
291    pub fn iter(&self) -> Iter<'_> {
292        let inner = self.entries.iter();
293        Iter { inner }
294    }
295}
296
297impl<'a> Iterator for Iter<'a> {
298    type Item = &'a Rc<Function>;
299
300    fn next(&mut self) -> Option<Self::Item> {
301        self.inner.next().map(|entry| &entry.0)
302    }
303}
304
305impl ExactSizeIterator for Iter<'_> {
306    #[inline]
307    fn len(&self) -> usize {
308        self.inner.len()
309    }
310}
311
312impl FusedIterator for Iter<'_> {}
313
314impl<'a> IntoIterator for &'a FunctionSet {
315    type Item = &'a Rc<Function>;
316    type IntoIter = Iter<'a>;
317
318    fn into_iter(self) -> Self::IntoIter {
319        self.iter()
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[derive(Clone, Debug)]
328    struct FunctionBodyStub;
329
330    impl std::fmt::Display for FunctionBodyStub {
331        fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332            unreachable!()
333        }
334    }
335    impl FunctionBody for FunctionBodyStub {
336        async fn execute(&self, _: &mut Env) -> crate::semantics::Result {
337            unreachable!()
338        }
339    }
340
341    fn function_body_stub() -> Rc<dyn FunctionBodyObject> {
342        Rc::new(FunctionBodyStub)
343    }
344
345    #[test]
346    fn defining_new_function() {
347        let mut set = FunctionSet::new();
348        let function = Rc::new(Function::new(
349            "foo",
350            function_body_stub(),
351            Location::dummy("foo"),
352        ));
353
354        let result = set.define(function.clone());
355        assert_eq!(result, Ok(None));
356        assert_eq!(set.get("foo"), Some(&function));
357    }
358
359    #[test]
360    fn redefining_existing_function() {
361        let mut set = FunctionSet::new();
362        let function1 = Rc::new(Function::new(
363            "foo",
364            function_body_stub(),
365            Location::dummy("foo 1"),
366        ));
367        let function2 = Rc::new(Function::new(
368            "foo",
369            function_body_stub(),
370            Location::dummy("foo 2"),
371        ));
372        set.define(function1.clone()).unwrap();
373
374        let result = set.define(function2.clone());
375        assert_eq!(result, Ok(Some(function1)));
376        assert_eq!(set.get("foo"), Some(&function2));
377    }
378
379    #[test]
380    fn redefining_readonly_function() {
381        let mut set = FunctionSet::new();
382        let function1 = Rc::new(
383            Function::new("foo", function_body_stub(), Location::dummy("foo 1"))
384                .make_read_only(Location::dummy("readonly")),
385        );
386        let function2 = Rc::new(Function::new(
387            "foo",
388            function_body_stub(),
389            Location::dummy("foo 2"),
390        ));
391        set.define(function1.clone()).unwrap();
392
393        let error = set.define(function2.clone()).unwrap_err();
394        assert_eq!(error.existing, function1);
395        assert_eq!(error.new, function2);
396        assert_eq!(set.get("foo"), Some(&function1));
397    }
398
399    #[test]
400    fn unsetting_existing_function() {
401        let mut set = FunctionSet::new();
402        let function = Rc::new(Function::new(
403            "foo",
404            function_body_stub(),
405            Location::dummy("foo"),
406        ));
407        set.define(function.clone()).unwrap();
408
409        let result = set.unset("foo").unwrap();
410        assert_eq!(result, Some(function));
411        assert_eq!(set.get("foo"), None);
412    }
413
414    #[test]
415    fn unsetting_nonexisting_function() {
416        let mut set = FunctionSet::new();
417
418        let result = set.unset("foo").unwrap();
419        assert_eq!(result, None);
420        assert_eq!(set.get("foo"), None);
421    }
422
423    #[test]
424    fn unsetting_readonly_function() {
425        let mut set = FunctionSet::new();
426        let function = Rc::new(
427            Function::new("foo", function_body_stub(), Location::dummy("foo"))
428                .make_read_only(Location::dummy("readonly")),
429        );
430        set.define(function.clone()).unwrap();
431
432        let error = set.unset("foo").unwrap_err();
433        assert_eq!(error.existing, function);
434    }
435
436    #[test]
437    fn iteration() {
438        let mut set = FunctionSet::new();
439        let function1 = Rc::new(Function::new(
440            "foo",
441            function_body_stub(),
442            Location::dummy("foo"),
443        ));
444        let function2 = Rc::new(Function::new(
445            "bar",
446            function_body_stub(),
447            Location::dummy("bar"),
448        ));
449        set.define(function1.clone()).unwrap();
450        set.define(function2.clone()).unwrap();
451
452        let functions = set.iter().collect::<Vec<_>>();
453        assert!(
454            functions[..] == [&function1, &function2] || functions[..] == [&function2, &function1],
455            "{functions:?}"
456        );
457    }
458}