Skip to main content

yash_env/input/
echo.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2024 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//! `Echo` definition
18
19use super::{Context, Input, Result};
20use crate::Env;
21use crate::option::Option::Verbose;
22use crate::option::State::On;
23use crate::system::concurrency::WriteAll;
24use std::cell::RefCell;
25
26/// `Input` decorator that echoes the input.
27///
28/// This decorator adds the behavior of the [verbose](crate::option::Verbose)
29/// shell option to the input. If the option is enabled, the input is echoed to
30/// the standard error before it is returned to the caller. Otherwise, the input
31/// is returned as is.
32#[derive(Debug)]
33#[doc(alias = "Verbose")]
34#[must_use = "Echo does nothing unless used by a parser"]
35pub struct Echo<'a, 'b, S, T> {
36    inner: T,
37    env: &'a RefCell<&'b mut Env<S>>,
38}
39
40impl<'a, 'b, S, T> Echo<'a, 'b, S, T> {
41    /// Creates a new `Echo` decorator.
42    ///
43    /// The first argument is the inner `Input` that performs the actual input
44    /// operation. The second argument is the shell environment that contains
45    /// the shell option state and the system interface to print to the standard
46    /// error. It is wrapped in a `RefCell` so that it can be shared with other
47    /// decorators and the parser.
48    pub fn new(inner: T, env: &'a RefCell<&'b mut Env<S>>) -> Self {
49        Self { inner, env }
50    }
51}
52
53// Not derived automatically because S may not implement Clone.
54impl<S, T: Clone> Clone for Echo<'_, '_, S, T> {
55    fn clone(&self) -> Self {
56        Self {
57            inner: self.inner.clone(),
58            env: self.env,
59        }
60    }
61}
62
63impl<S: WriteAll, T: Input> Input for Echo<'_, '_, S, T> {
64    #[allow(
65        clippy::await_holding_refcell_ref,
66        reason = "other decorators, the parser, or the executor do not run concurrently with this method"
67    )]
68    async fn next_line(&mut self, context: &Context) -> Result {
69        let line = self.inner.next_line(context).await?;
70
71        let env = &mut **self.env.borrow_mut();
72        if env.options.get(Verbose) == On {
73            env.system.print_error(&line).await;
74        }
75
76        Ok(line)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::super::Memory;
83    use super::*;
84    use crate::system::Concurrent;
85    use crate::system::r#virtual::VirtualSystem;
86    use crate::test_helper::assert_stderr;
87    use futures_util::FutureExt as _;
88    use std::rc::Rc;
89
90    #[test]
91    fn verbose_off() {
92        let system = VirtualSystem::new();
93        let state = Rc::clone(&system.state);
94        let mut env = Env::with_system(Rc::new(Concurrent::new(system)));
95        let ref_env = RefCell::new(&mut env);
96        let memory = Memory::new("echo test\n");
97        let mut echo = Echo::new(memory, &ref_env);
98
99        let line = echo
100            .next_line(&Context::default())
101            .now_or_never()
102            .unwrap()
103            .unwrap();
104        assert_eq!(line, "echo test\n");
105        assert_stderr(&state, |stderr| assert_eq!(stderr, ""));
106    }
107
108    #[test]
109    fn verbose_on() {
110        let system = VirtualSystem::new();
111        let state = Rc::clone(&system.state);
112        let mut env = Env::with_system(Rc::new(Concurrent::new(system)));
113        env.options.set(Verbose, On);
114        let ref_env = RefCell::new(&mut env);
115        let memory = Memory::new("echo test\nfoo");
116        let mut echo = Echo::new(memory, &ref_env);
117
118        let line = echo
119            .next_line(&Context::default())
120            .now_or_never()
121            .unwrap()
122            .unwrap();
123        assert_eq!(line, "echo test\n");
124        assert_stderr(&state, |stderr| assert_eq!(stderr, "echo test\n"));
125
126        let line = echo
127            .next_line(&Context::default())
128            .now_or_never()
129            .unwrap()
130            .unwrap();
131        assert_eq!(line, "foo");
132        assert_stderr(&state, |stderr| assert_eq!(stderr, "echo test\nfoo"));
133    }
134}