Skip to main content

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<S>: 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, reason = "we don't support Send")]
41    async fn execute(&self, env: &mut Env<S>) -> 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<S>: 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<S>,
58    ) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>>;
59}
60
61impl<S, T: FunctionBody<S> + ?Sized> FunctionBodyObject<S> for T {
62    fn execute<'a>(
63        &'a self,
64        env: &'a mut Env<S>,
65    ) -> Pin<Box<dyn Future<Output = crate::semantics::Result> + 'a>> {
66        Box::pin(self.execute(env))
67    }
68}
69
70/// Definition of a function.
71pub struct Function<S> {
72    /// String that identifies the function.
73    pub name: String,
74
75    /// Command that is executed when the function is called.
76    ///
77    /// This is wrapped in `Rc` so that we don't have to clone the entire
78    /// command when we define a function. The function definition command only
79    /// clones the `Rc` object from the abstract syntax tree to create a
80    /// `Function` object.
81    pub body: Rc<dyn FunctionBodyObject<S>>,
82
83    /// Location of the function definition command that defined this function.
84    pub origin: Location,
85
86    /// Optional location where this function was made read-only.
87    ///
88    /// If this function is not read-only, `read_only_location` is `None`.
89    /// Otherwise, `read_only_location` is the location of the simple command
90    /// that executed the `readonly` built-in that made this function read-only.
91    pub read_only_location: Option<Location>,
92}
93
94impl<S> Function<S> {
95    /// Creates a new function.
96    ///
97    /// This is a convenience function for constructing a `Function` object.
98    /// The `read_only_location` is set to `None`.
99    #[inline]
100    #[must_use]
101    pub fn new<N: Into<String>, B: Into<Rc<dyn FunctionBodyObject<S>>>>(
102        name: N,
103        body: B,
104        origin: Location,
105    ) -> Self {
106        Function {
107            name: name.into(),
108            body: body.into(),
109            origin,
110            read_only_location: None,
111        }
112    }
113
114    /// Makes the function read-only.
115    ///
116    /// This is a convenience function for doing
117    /// `self.read_only_location = Some(location)` in a method chain.
118    #[inline]
119    #[must_use]
120    pub fn make_read_only(mut self, location: Location) -> Self {
121        self.read_only_location = Some(location);
122        self
123    }
124
125    /// Whether this function is read-only or not.
126    #[must_use]
127    pub const fn is_read_only(&self) -> bool {
128        self.read_only_location.is_some()
129    }
130}
131
132// Not derived automatically because S may not implement Clone
133impl<S> Clone for Function<S> {
134    fn clone(&self) -> Self {
135        Self {
136            name: self.name.clone(),
137            body: self.body.clone(),
138            origin: self.origin.clone(),
139            read_only_location: self.read_only_location.clone(),
140        }
141    }
142}
143
144/// Compares two functions for equality.
145///
146/// Two functions are considered equal if all their members are equal.
147/// This includes comparing the `body` members by pointer equality.
148impl<S> PartialEq for Function<S> {
149    fn eq(&self, other: &Self) -> bool {
150        self.name == other.name
151            && Rc::ptr_eq(&self.body, &other.body)
152            && self.origin == other.origin
153            && self.read_only_location == other.read_only_location
154    }
155}
156
157impl<S> Eq for Function<S> {}
158
159// Not derived automatically because S may not implement Debug
160impl<S> Debug for Function<S> {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        f.debug_struct("Function")
163            .field("name", &self.name)
164            .field("body", &self.body)
165            .field("origin", &self.origin)
166            .field("read_only_location", &self.read_only_location)
167            .finish()
168    }
169}
170
171/// Wrapper of [`Function`] for inserting into a hash set.
172///
173/// A `HashEntry` wraps a `Function` in `Rc` so that the `Function` object can
174/// outlive the execution of the function which may redefine or unset the
175/// function itself. A simple command that executes the function clones the
176/// `Rc` object from the function set and retains it until the command
177/// terminates.
178///
179/// The `Hash` and `PartialEq` implementation for `HashEntry` only compares
180/// the names of the functions.
181struct HashEntry<S>(Rc<Function<S>>);
182
183// Not derived automatically because S may not implement Clone
184impl<S> Clone for HashEntry<S> {
185    fn clone(&self) -> Self {
186        HashEntry(Rc::clone(&self.0))
187    }
188}
189
190impl<S> PartialEq for HashEntry<S> {
191    /// Compares the names of two hash entries.
192    ///
193    /// Members of [`Function`] other than `name` are not considered in this
194    /// function.
195    fn eq(&self, other: &HashEntry<S>) -> bool {
196        self.0.name == other.0.name
197    }
198}
199
200// Not derived automatically because S may not implement Eq
201impl<S> Eq for HashEntry<S> {}
202
203impl<S> Hash for HashEntry<S> {
204    /// Hashes the name of the function.
205    ///
206    /// Members of [`Function`] other than `name` are not considered in this
207    /// function.
208    fn hash<H: Hasher>(&self, state: &mut H) {
209        self.0.name.hash(state)
210    }
211}
212
213impl<S> Borrow<str> for HashEntry<S> {
214    fn borrow(&self) -> &str {
215        &self.0.name
216    }
217}
218
219// Not derived automatically because S may not implement Debug
220impl<S> Debug for HashEntry<S> {
221    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222        f.debug_tuple("HashEntry").field(&self.0).finish()
223    }
224}
225
226/// Collection of functions.
227pub struct FunctionSet<S> {
228    entries: HashSet<HashEntry<S>>,
229}
230
231// Not derived automatically because S may not implement Debug
232impl<S> Debug for FunctionSet<S> {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        f.debug_struct("FunctionSet")
235            .field("entries", &self.entries)
236            .finish()
237    }
238}
239
240// Not derived automatically because S may not implement Clone
241impl<S> Clone for FunctionSet<S> {
242    fn clone(&self) -> Self {
243        #[allow(
244            clippy::mutable_key_type,
245            reason = "we use Rc but it's effectively immutable"
246        )]
247        let entries = self.entries.clone();
248        Self { entries }
249    }
250}
251
252// Not derived automatically because S may not implement Default
253impl<S> Default for FunctionSet<S> {
254    fn default() -> Self {
255        #[allow(
256            clippy::mutable_key_type,
257            reason = "we use Rc but it's effectively immutable"
258        )]
259        let entries = HashSet::default();
260        Self { entries }
261    }
262}
263
264/// Error redefining a read-only function.
265#[derive(Error)]
266#[error("cannot redefine read-only function `{}`", .existing.name)]
267#[non_exhaustive]
268pub struct DefineError<S> {
269    /// Existing read-only function
270    pub existing: Rc<Function<S>>,
271    /// New function that tried to redefine the existing function
272    pub new: Rc<Function<S>>,
273}
274
275// Not derived automatically because S may not implement Clone, Debug, or PartialEq
276impl<S> Clone for DefineError<S> {
277    fn clone(&self) -> Self {
278        Self {
279            existing: self.existing.clone(),
280            new: self.new.clone(),
281        }
282    }
283}
284
285impl<S> Debug for DefineError<S> {
286    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287        f.debug_struct("DefineError")
288            .field("existing", &self.existing)
289            .field("new", &self.new)
290            .finish()
291    }
292}
293
294impl<S> PartialEq for DefineError<S> {
295    fn eq(&self, other: &Self) -> bool {
296        self.existing == other.existing && self.new == other.new
297    }
298}
299
300impl<S> Eq for DefineError<S> {}
301
302/// Error unsetting a read-only function.
303#[derive(Error)]
304#[error("cannot unset read-only function `{}`", .existing.name)]
305#[non_exhaustive]
306pub struct UnsetError<S> {
307    /// Existing read-only function
308    pub existing: Rc<Function<S>>,
309}
310
311// Not derived automatically because S may not implement Clone, Debug, or PartialEq
312impl<S> Clone for UnsetError<S> {
313    fn clone(&self) -> Self {
314        Self {
315            existing: self.existing.clone(),
316        }
317    }
318}
319
320impl<S> Debug for UnsetError<S> {
321    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322        f.debug_struct("UnsetError")
323            .field("existing", &self.existing)
324            .finish()
325    }
326}
327
328impl<S> PartialEq for UnsetError<S> {
329    fn eq(&self, other: &Self) -> bool {
330        self.existing == other.existing
331    }
332}
333
334impl<S> Eq for UnsetError<S> {}
335
336/// Unordered iterator over functions in a function set.
337///
338/// This iterator is created by [`FunctionSet::iter`].
339pub struct Iter<'a, S> {
340    inner: std::collections::hash_set::Iter<'a, HashEntry<S>>,
341}
342
343// Not derived automatically because S may not implement Clone or Debug
344impl<S> Clone for Iter<'_, S> {
345    fn clone(&self) -> Self {
346        Self {
347            inner: self.inner.clone(),
348        }
349    }
350}
351
352impl<S> Debug for Iter<'_, S> {
353    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
354        f.debug_struct("Iter").field("inner", &self.inner).finish()
355    }
356}
357
358impl<S> FunctionSet<S> {
359    /// Creates a new empty function set.
360    #[must_use]
361    pub fn new() -> Self {
362        FunctionSet::default()
363    }
364
365    /// Returns the function with the given name.
366    #[must_use]
367    pub fn get(&self, name: &str) -> Option<&Rc<Function<S>>> {
368        self.entries.get(name).map(|entry| &entry.0)
369    }
370
371    /// Returns the number of functions in the set.
372    #[inline]
373    #[must_use]
374    pub fn len(&self) -> usize {
375        self.entries.len()
376    }
377
378    /// Returns `true` if the set contains no functions.
379    #[inline]
380    #[must_use]
381    pub fn is_empty(&self) -> bool {
382        self.entries.is_empty()
383    }
384
385    /// Inserts a function into the set.
386    ///
387    /// If a function with the same name already exists, it is replaced and
388    /// returned unless it is read-only, in which case `DefineError` is
389    /// returned.
390    pub fn define<F: Into<Rc<Function<S>>>>(
391        &mut self,
392        function: F,
393    ) -> Result<Option<Rc<Function<S>>>, DefineError<S>> {
394        #[allow(
395            clippy::mutable_key_type,
396            reason = "we use Rc but it's effectively immutable"
397        )]
398        fn inner<S>(
399            entries: &mut HashSet<HashEntry<S>>,
400            new: Rc<Function<S>>,
401        ) -> Result<Option<Rc<Function<S>>>, DefineError<S>> {
402            match entries.get(new.name.as_str()) {
403                Some(existing) if existing.0.is_read_only() => Err(DefineError {
404                    existing: Rc::clone(&existing.0),
405                    new,
406                }),
407
408                _ => Ok(entries.replace(HashEntry(new)).map(|entry| entry.0)),
409            }
410        }
411        inner(&mut self.entries, function.into())
412    }
413
414    /// Removes a function from the set.
415    ///
416    /// This function returns the previously defined function if it exists.
417    /// However, if the function is read-only, `UnsetError` is returned.
418    pub fn unset(&mut self, name: &str) -> Result<Option<Rc<Function<S>>>, UnsetError<S>> {
419        match self.entries.get(name) {
420            Some(entry) if entry.0.is_read_only() => Err(UnsetError {
421                existing: Rc::clone(&entry.0),
422            }),
423
424            _ => Ok(self.entries.take(name).map(|entry| entry.0)),
425        }
426    }
427
428    /// Returns an iterator over functions in the set.
429    ///
430    /// The order of iteration is not specified.
431    pub fn iter(&self) -> Iter<'_, S> {
432        let inner = self.entries.iter();
433        Iter { inner }
434    }
435}
436
437impl<'a, S> Iterator for Iter<'a, S> {
438    type Item = &'a Rc<Function<S>>;
439
440    fn next(&mut self) -> Option<Self::Item> {
441        self.inner.next().map(|entry| &entry.0)
442    }
443}
444
445impl<S> ExactSizeIterator for Iter<'_, S> {
446    #[inline]
447    fn len(&self) -> usize {
448        self.inner.len()
449    }
450}
451
452impl<S> FusedIterator for Iter<'_, S> {}
453
454impl<'a, S> IntoIterator for &'a FunctionSet<S> {
455    type Item = &'a Rc<Function<S>>;
456    type IntoIter = Iter<'a, S>;
457    fn into_iter(self) -> Self::IntoIter {
458        self.iter()
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465
466    #[derive(Clone, Debug)]
467    struct FunctionBodyStub;
468
469    impl std::fmt::Display for FunctionBodyStub {
470        fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471            unreachable!()
472        }
473    }
474    impl<S> FunctionBody<S> for FunctionBodyStub {
475        async fn execute(&self, _: &mut Env<S>) -> crate::semantics::Result {
476            unreachable!()
477        }
478    }
479
480    fn function_body_stub<S>() -> Rc<dyn FunctionBodyObject<S>> {
481        Rc::new(FunctionBodyStub)
482    }
483
484    #[test]
485    fn defining_new_function() {
486        let mut set = FunctionSet::<()>::new();
487        let function = Rc::new(Function::new(
488            "foo",
489            function_body_stub(),
490            Location::dummy("foo"),
491        ));
492
493        let result = set.define(function.clone());
494        assert_eq!(result, Ok(None));
495        assert_eq!(set.get("foo"), Some(&function));
496    }
497
498    #[test]
499    fn redefining_existing_function() {
500        let mut set = FunctionSet::<()>::new();
501        let function1 = Rc::new(Function::new(
502            "foo",
503            function_body_stub(),
504            Location::dummy("foo 1"),
505        ));
506        let function2 = Rc::new(Function::new(
507            "foo",
508            function_body_stub(),
509            Location::dummy("foo 2"),
510        ));
511        set.define(function1.clone()).unwrap();
512
513        let result = set.define(function2.clone());
514        assert_eq!(result, Ok(Some(function1)));
515        assert_eq!(set.get("foo"), Some(&function2));
516    }
517
518    #[test]
519    fn redefining_readonly_function() {
520        let mut set = FunctionSet::<()>::new();
521        let function1 = Rc::new(
522            Function::new("foo", function_body_stub(), Location::dummy("foo 1"))
523                .make_read_only(Location::dummy("readonly")),
524        );
525        let function2 = Rc::new(Function::new(
526            "foo",
527            function_body_stub(),
528            Location::dummy("foo 2"),
529        ));
530        set.define(function1.clone()).unwrap();
531
532        let error = set.define(function2.clone()).unwrap_err();
533        assert_eq!(error.existing, function1);
534        assert_eq!(error.new, function2);
535        assert_eq!(set.get("foo"), Some(&function1));
536    }
537
538    #[test]
539    fn unsetting_existing_function() {
540        let mut set = FunctionSet::<()>::new();
541        let function = Rc::new(Function::new(
542            "foo",
543            function_body_stub(),
544            Location::dummy("foo"),
545        ));
546        set.define(function.clone()).unwrap();
547
548        let result = set.unset("foo").unwrap();
549        assert_eq!(result, Some(function));
550        assert_eq!(set.get("foo"), None);
551    }
552
553    #[test]
554    fn unsetting_nonexisting_function() {
555        let mut set = FunctionSet::<()>::new();
556
557        let result = set.unset("foo").unwrap();
558        assert_eq!(result, None);
559        assert_eq!(set.get("foo"), None);
560    }
561
562    #[test]
563    fn unsetting_readonly_function() {
564        let mut set = FunctionSet::<()>::new();
565        let function = Rc::new(
566            Function::new("foo", function_body_stub(), Location::dummy("foo"))
567                .make_read_only(Location::dummy("readonly")),
568        );
569        set.define(function.clone()).unwrap();
570
571        let error = set.unset("foo").unwrap_err();
572        assert_eq!(error.existing, function);
573    }
574
575    #[test]
576    fn iteration() {
577        let mut set = FunctionSet::<()>::new();
578        let function1 = Rc::new(Function::new(
579            "foo",
580            function_body_stub(),
581            Location::dummy("foo"),
582        ));
583        let function2 = Rc::new(Function::new(
584            "bar",
585            function_body_stub(),
586            Location::dummy("bar"),
587        ));
588        set.define(function1.clone()).unwrap();
589        set.define(function2.clone()).unwrap();
590
591        let functions = set.iter().collect::<Vec<_>>();
592        assert!(
593            functions[..] == [&function1, &function2] || functions[..] == [&function2, &function1],
594            "{functions:?}"
595        );
596    }
597}