yash_env/input.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//! Methods about passing [source](crate::source) code to the
18//! [parser](crate::parser).
19
20use std::ops::DerefMut;
21use std::pin::Pin;
22
23/// Parameter passed to the input function
24///
25/// The context is passed to the [input function](Input::next_line) so that it
26/// can read the input in a context-dependent way.
27#[derive(Debug)]
28#[non_exhaustive]
29pub struct Context {
30 is_first_line: bool,
31}
32
33impl Default for Context {
34 fn default() -> Self {
35 Self {
36 is_first_line: true,
37 }
38 }
39}
40
41impl Context {
42 /// Whether the current line is the first line of the input
43 #[inline]
44 #[must_use]
45 pub fn is_first_line(&self) -> bool {
46 self.is_first_line
47 }
48
49 /// Sets whether the current line is the first line of the input
50 ///
51 /// This method is used by the lexer to set the flag. It can also be used in
52 /// tests to simulate a non-first line. The default value is `true`.
53 #[inline]
54 pub fn set_is_first_line(&mut self, is_first_line: bool) {
55 self.is_first_line = is_first_line;
56 }
57}
58
59/// Error returned by the [Input] function
60pub type Error = std::io::Error;
61
62/// Result of the [Input] function
63pub type Result = std::result::Result<String, Error>;
64
65/// Line-oriented source code reader
66///
67/// An `Input` implementor provides the parser with source code by reading from underlying source.
68///
69/// [`InputObject`] is an object-safe version of this trait.
70#[must_use = "Input instances should be used by a parser"]
71pub trait Input {
72 /// Reads a next line of the source code.
73 ///
74 /// The input function is line-oriented; that is, this function returns a string that is
75 /// terminated by a newline unless the end of input (EOF) is reached, in which case the
76 /// remaining characters up to the EOF must be returned without a trailing newline. If there
77 /// are no more characters at all, the returned line is empty.
78 ///
79 /// Errors returned from this function are considered unrecoverable. Once an error is returned,
80 /// this function should not be called any more.
81 fn next_line(&mut self, context: &Context) -> impl Future<Output = Result>;
82}
83
84impl<T> Input for T
85where
86 T: DerefMut,
87 T::Target: Input,
88{
89 fn next_line(&mut self, context: &Context) -> impl Future<Output = Result> {
90 self.deref_mut().next_line(context)
91 }
92}
93
94/// Object-safe adapter for the [`Input`] trait
95///
96/// `InputObject` is an object-safe version of the [`Input`] trait. It allows
97/// the trait to be used as a trait object, which is necessary for dynamic
98/// dispatch.
99///
100/// The umbrella implementation is provided for all types that implement the
101/// [`Input`] trait.
102pub trait InputObject {
103 fn next_line<'a>(
104 &'a mut self,
105 context: &'a Context,
106 ) -> Pin<Box<dyn Future<Output = Result> + 'a>>;
107}
108
109impl<T: Input> InputObject for T {
110 fn next_line<'a>(
111 &'a mut self,
112 context: &'a Context,
113 ) -> Pin<Box<dyn Future<Output = Result> + 'a>> {
114 Box::pin(Input::next_line(self, context))
115 }
116}
117
118mod memory;
119pub use memory::Memory;
120
121mod fd_reader;
122pub use fd_reader::FdReader;
123
124mod echo;
125pub use echo::Echo;
126
127mod ignore_eof;
128pub use ignore_eof::IgnoreEof;
129
130mod reporter;
131pub use reporter::Reporter;