yash_semantics/expansion/initial/
slice.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2022 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//! Implementation of [`Expand`] for slices.
18
19use super::super::Error;
20use super::Env;
21use super::Expand;
22use super::Phrase;
23
24/// Expands a slice of expandable items.
25///
26/// This implementation is typically used for expanding a word or text. Each
27/// item in the slice is recursively expanded, and the results are merged into
28/// one phrase by [`Phrase::append`].
29///
30/// If the slice has no item, the result is [one empty
31/// field](Phrase::one_empty_field).
32impl<T: Expand> Expand for [T] {
33    async fn expand(&self, env: &mut Env<'_>) -> Result<Phrase, Error> {
34        if self.is_empty() {
35            return Ok(Phrase::one_empty_field());
36        }
37
38        let mut phrase = Phrase::zero_fields();
39        for item in self {
40            phrase += item.expand(env).await?;
41        }
42        Ok(phrase)
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::expansion::Error;
50    use crate::expansion::attr::AttrChar;
51    use crate::expansion::attr::Origin;
52    use futures_util::FutureExt as _;
53    use std::cell::Cell;
54
55    fn dummy_attr_char(value: char) -> AttrChar {
56        AttrChar {
57            value,
58            origin: Origin::Literal,
59            is_quoted: false,
60            is_quoting: false,
61        }
62    }
63
64    struct Stub(Cell<Option<Result<Phrase, Error>>>);
65
66    impl Stub {
67        fn new(result: Result<Phrase, Error>) -> Self {
68            Stub(Cell::new(Some(result)))
69        }
70    }
71
72    impl Expand for Stub {
73        async fn expand(&self, _: &mut Env<'_>) -> Result<Phrase, Error> {
74            self.0.take().expect("expand should be called only once")
75        }
76    }
77
78    #[test]
79    fn empty_slice() {
80        let mut env = yash_env::Env::new_virtual();
81        let mut env = Env::new(&mut env);
82        let stubs: [Stub; 0] = [];
83        let result = stubs.expand(&mut env).now_or_never().unwrap();
84        assert_eq!(result, Ok(Phrase::one_empty_field()));
85    }
86
87    #[test]
88    fn slice_of_one_item_returning_zero_fields() {
89        let mut env = yash_env::Env::new_virtual();
90        let mut env = Env::new(&mut env);
91        let stubs = [Stub::new(Ok(Phrase::zero_fields()))];
92        let result = stubs.expand(&mut env).now_or_never().unwrap();
93        assert_eq!(result, Ok(Phrase::zero_fields()));
94    }
95
96    #[test]
97    fn slice_of_many_items_returning_zero_fields() {
98        let mut env = yash_env::Env::new_virtual();
99        let mut env = Env::new(&mut env);
100        let stubs = [
101            Stub::new(Ok(Phrase::zero_fields())),
102            Stub::new(Ok(Phrase::zero_fields())),
103            Stub::new(Ok(Phrase::zero_fields())),
104        ];
105        let result = stubs.expand(&mut env).now_or_never().unwrap();
106        assert_eq!(result, Ok(Phrase::zero_fields()));
107    }
108
109    #[test]
110    fn slice_of_many_items_each_returning_one_empty_field() {
111        let mut env = yash_env::Env::new_virtual();
112        let mut env = Env::new(&mut env);
113        let stubs = [
114            Stub::new(Ok(Phrase::one_empty_field())),
115            Stub::new(Ok(Phrase::one_empty_field())),
116            Stub::new(Ok(Phrase::one_empty_field())),
117        ];
118        let result = stubs.expand(&mut env).now_or_never().unwrap();
119        assert_eq!(result, Ok(Phrase::one_empty_field()));
120    }
121
122    #[test]
123    fn slice_of_many_items_each_returning_one_non_empty_field() {
124        let mut env = yash_env::Env::new_virtual();
125        let mut env = Env::new(&mut env);
126        let a = dummy_attr_char('a');
127        let b = dummy_attr_char('b');
128        let c = dummy_attr_char('c');
129        let stubs = [
130            Stub::new(Ok(Phrase::Char(a))),
131            Stub::new(Ok(Phrase::Char(b))),
132            Stub::new(Ok(Phrase::Char(c))),
133        ];
134        let result = stubs.expand(&mut env).now_or_never().unwrap();
135        assert_eq!(result, Ok(Phrase::Field(vec![a, b, c])));
136    }
137}