conch_runtime_pshaw/env/
args.rs

1use crate::env::SubEnvironment;
2use std::borrow::Cow;
3use std::collections::VecDeque;
4use std::sync::Arc;
5
6/// An interface for getting shell and function arguments.
7pub trait ArgumentsEnvironment {
8    /// The type of arguments this environment holds.
9    type Arg: Clone;
10
11    /// Get the name of the shell.
12    fn name(&self) -> &Self::Arg;
13    /// Get an argument at any index. Arguments are 1-indexed since the shell variable `$0`
14    /// refers to the shell's name. Thus the first real argument starts at index 1.
15    fn arg(&self, idx: usize) -> Option<&Self::Arg>;
16    /// Get the number of current arguments, NOT including the shell name.
17    fn args_len(&self) -> usize;
18    /// Get all current arguments as a possibly owned slice.
19    fn args(&self) -> Cow<'_, [Self::Arg]>;
20}
21
22impl<'a, T: ?Sized + ArgumentsEnvironment> ArgumentsEnvironment for &'a T {
23    type Arg = T::Arg;
24
25    fn name(&self) -> &Self::Arg {
26        (**self).name()
27    }
28
29    fn arg(&self, idx: usize) -> Option<&Self::Arg> {
30        (**self).arg(idx)
31    }
32
33    fn args_len(&self) -> usize {
34        (**self).args_len()
35    }
36
37    fn args(&self) -> Cow<'_, [Self::Arg]> {
38        (**self).args()
39    }
40}
41
42impl<'a, T: ?Sized + ArgumentsEnvironment> ArgumentsEnvironment for &'a mut T {
43    type Arg = T::Arg;
44
45    fn name(&self) -> &Self::Arg {
46        (**self).name()
47    }
48
49    fn arg(&self, idx: usize) -> Option<&Self::Arg> {
50        (**self).arg(idx)
51    }
52
53    fn args_len(&self) -> usize {
54        (**self).args_len()
55    }
56
57    fn args(&self) -> Cow<'_, [Self::Arg]> {
58        (**self).args()
59    }
60}
61
62/// An interface for setting shell and function arguments.
63pub trait SetArgumentsEnvironment: ArgumentsEnvironment {
64    /// A collection of arguments to set.
65    type Args;
66    /// Changes the environment's arguments to `new_args` and returns the old arguments.
67    fn set_args(&mut self, new_args: Self::Args) -> Self::Args;
68}
69
70impl<'a, T: ?Sized + SetArgumentsEnvironment> SetArgumentsEnvironment for &'a mut T {
71    type Args = T::Args;
72
73    fn set_args(&mut self, new_args: Self::Args) -> Self::Args {
74        (**self).set_args(new_args)
75    }
76}
77
78/// An interface for shifting positional shell and function arguments.
79pub trait ShiftArgumentsEnvironment {
80    /// Shift parameters such that the positional parameter `n` will hold
81    /// the value of the positional parameter `n + amt`.
82    ///
83    /// If `amt == 0`, then no change to the positional parameters
84    /// should be made.
85    fn shift_args(&mut self, amt: usize);
86}
87
88impl<'a, T: ?Sized + ShiftArgumentsEnvironment> ShiftArgumentsEnvironment for &'a mut T {
89    fn shift_args(&mut self, amt: usize) {
90        (**self).shift_args(amt)
91    }
92}
93
94/// An environment module for setting and getting shell and function arguments.
95#[derive(Debug, PartialEq, Eq)]
96pub struct ArgsEnv<T> {
97    name: Arc<T>,
98    args: Arc<VecDeque<T>>,
99}
100
101impl<T> ArgsEnv<T> {
102    /// Constructs a new environment and initializes it with the name of the
103    /// current process as the shell name, and no arguments.
104    pub fn new() -> Self
105    where
106        T: From<String>,
107    {
108        let name = ::std::env::current_exe()
109            .ok()
110            .and_then(|path| {
111                path.file_name()
112                    .and_then(|os_str| os_str.to_str().map(|s| s.to_owned()))
113            })
114            .unwrap_or_default();
115
116        Self::with_name(name.into())
117    }
118
119    /// Constructs a new environment and initializes it with the
120    /// provided name and no arguments.
121    pub fn with_name(name: T) -> Self {
122        ArgsEnv {
123            name: name.into(),
124            args: Arc::new(VecDeque::new()),
125        }
126    }
127
128    /// Constructs a new environment and initializes it with the
129    /// provided name and positional arguments.
130    pub fn with_name_and_args<I: IntoIterator<Item = T>>(name: T, args: I) -> Self {
131        ArgsEnv {
132            name: name.into(),
133            args: Arc::new(args.into_iter().collect()),
134        }
135    }
136}
137
138impl<T: From<String>> Default for ArgsEnv<T> {
139    fn default() -> Self {
140        Self::new()
141    }
142}
143
144impl<T> Clone for ArgsEnv<T> {
145    fn clone(&self) -> Self {
146        ArgsEnv {
147            name: self.name.clone(),
148            args: self.args.clone(),
149        }
150    }
151}
152
153impl<T> SubEnvironment for ArgsEnv<T> {
154    fn sub_env(&self) -> Self {
155        self.clone()
156    }
157}
158
159impl<T: Clone> ArgumentsEnvironment for ArgsEnv<T> {
160    type Arg = T;
161
162    fn name(&self) -> &Self::Arg {
163        &self.name
164    }
165
166    fn arg(&self, idx: usize) -> Option<&Self::Arg> {
167        if idx == 0 {
168            Some(self.name())
169        } else {
170            self.args.get(idx - 1)
171        }
172    }
173
174    fn args_len(&self) -> usize {
175        self.args.len()
176    }
177
178    fn args(&self) -> Cow<'_, [Self::Arg]> {
179        if let (first, []) = self.args.as_slices() {
180            Cow::Borrowed(first)
181        } else {
182            Cow::Owned(self.args.iter().cloned().collect())
183        }
184    }
185}
186
187impl<T: Clone> SetArgumentsEnvironment for ArgsEnv<T> {
188    type Args = Arc<VecDeque<T>>;
189
190    fn set_args(&mut self, new_args: Self::Args) -> Self::Args {
191        ::std::mem::replace(&mut self.args, new_args)
192    }
193}
194
195impl<T: Clone> ShiftArgumentsEnvironment for ArgsEnv<T> {
196    fn shift_args(&mut self, amt: usize) {
197        if amt == 0 {
198            return;
199        }
200
201        if amt >= self.args.len() {
202            // Keep around the already allocated memory if we're the only owner.
203            if let Some(args) = Arc::get_mut(&mut self.args) {
204                args.clear();
205                return;
206            }
207
208            // Otherwise just pretend we no longer have any arguments
209            self.args = Arc::new(VecDeque::new());
210        }
211
212        if let Some(args) = Arc::get_mut(&mut self.args) {
213            args.drain(0..amt);
214            return;
215        }
216
217        // Since we're not the only owner we're forced to copy everything over.
218        self.args = Arc::new(self.args.iter().skip(amt).cloned().collect());
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use crate::env::SubEnvironment;
226    use crate::RefCounted;
227
228    #[test]
229    fn test_name() {
230        let name = "shell";
231        let env = ArgsEnv::with_name(name.to_owned());
232        assert_eq!(env.name(), name);
233        assert_eq!(env.arg(0).unwrap(), name);
234
235        // Name should not change in sub environments
236        let env = env.sub_env();
237        assert_eq!(env.name(), name);
238        assert_eq!(env.arg(0).unwrap(), name);
239    }
240
241    #[test]
242    fn test_sub_env_no_needless_clone() {
243        let name = "shell";
244        let args = vec!["one", "two", "three"];
245        let env = ArgsEnv::with_name_and_args(name, args.clone());
246
247        let mut env = env.sub_env();
248        assert!(env.name.get_mut().is_none());
249        assert!(env.args.get_mut().is_none());
250    }
251
252    #[test]
253    fn test_args() {
254        let name = "shell";
255        let args = vec!["one", "two", "three"];
256        let env = ArgsEnv::with_name_and_args(name, args.clone());
257
258        assert_eq!(env.args_len(), args.len());
259
260        assert_eq!(env.arg(0), Some(&name));
261        assert_eq!(env.arg(1), Some(&args[0]));
262        assert_eq!(env.arg(2), Some(&args[1]));
263        assert_eq!(env.arg(3), Some(&args[2]));
264        assert_eq!(env.arg(4), None);
265
266        assert_eq!(env.args(), args);
267    }
268
269    #[test]
270    fn test_set_args() {
271        let args_old = vec!["1", "2", "3"];
272        let mut env = ArgsEnv::with_name_and_args("shell", args_old.clone());
273
274        {
275            let args_new = vec!["4", "5", "6"];
276            assert_eq!(env.args(), args_old);
277            let prev = env.set_args(VecDeque::from(args_new.clone()).into());
278            assert_eq!(*prev, args_old);
279            assert_eq!(env.args(), args_new);
280
281            env.set_args(prev);
282        }
283
284        assert_eq!(env.args(), args_old);
285    }
286
287    #[test]
288    fn test_shift_args() {
289        let mut env = ArgsEnv::with_name_and_args("shell", vec!["1", "2", "3", "4", "5", "6"]);
290        let _copy = env.sub_env();
291
292        env.shift_args(0);
293        assert_eq!(env.args(), vec!("1", "2", "3", "4", "5", "6"));
294        assert!(env.name.get_mut().is_none()); // No needless clone here
295
296        env.shift_args(1);
297        assert_eq!(env.args(), vec!("2", "3", "4", "5", "6"));
298
299        env.shift_args(1);
300        assert_eq!(env.args(), vec!("3", "4", "5", "6"));
301
302        env.shift_args(2);
303        assert_eq!(env.args(), vec!("5", "6"));
304
305        env.shift_args(100);
306        assert_eq!(env.args(), Vec::<&str>::new());
307    }
308}