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//!
20//! # Input sources
21//!
22//! The [`Input`] trait is the core abstraction for reading source code lines.
23//! These types are leaf inputs that produce lines from a concrete source:
24//!
25//! - [`Memory`]: In-memory input from a pre-loaded string
26//! - [`FdReader2`]: Reads from a file descriptor asynchronously
27//!
28//! # Input decorators
29//!
30//! Several decorators wrap an inner [`Input`] to add behavior:
31//!
32//! - [`Echo`]: Echoes each line read to stderr (for the `verbose` option)
33//! - [`IgnoreEof`]: Retries on EOF when the `ignore-eof` option is on (simple
34//! interactive use)
35//! - [`Reporter`]: Reports job status changes before each prompt
36//! - [`EofGuard`]: Combines suspended-jobs protection with `ignore-eof` retry;
37//! prefer over [`IgnoreEof`] for the interactive read loop
38//!
39//! ## Configuration stored in `env.any`
40//!
41//! [`EofGuard`] reads its behavior configuration from
42//! [`SuspendedJobsGuardConfig`] and [`IgnoreEofConfig`] stored in
43//! [`Env::any`](crate::Env::any):
44//!
45//! - [`SuspendedJobsGuardConfig`]: enables the suspended-jobs protection in
46//! [`EofGuard`] (and any other component that opts in). If absent, the
47//! protection is disabled.
48//! - [`IgnoreEofConfig`]: enables the `ignore-eof` retry behavior in
49//! [`EofGuard`]. If absent, EOF is not retried even when the `ignore-eof`
50//! option is on.
51
52use std::ops::DerefMut;
53use std::pin::Pin;
54
55/// Parameter passed to the input function
56///
57/// The context is passed to the [input function](Input::next_line) so that it
58/// can read the input in a context-dependent way.
59#[derive(Debug)]
60#[non_exhaustive]
61pub struct Context {
62 is_first_line: bool,
63}
64
65impl Default for Context {
66 fn default() -> Self {
67 Self {
68 is_first_line: true,
69 }
70 }
71}
72
73impl Context {
74 /// Whether the current line is the first line of the input
75 #[inline]
76 #[must_use]
77 pub fn is_first_line(&self) -> bool {
78 self.is_first_line
79 }
80
81 /// Sets whether the current line is the first line of the input
82 ///
83 /// This method is used by the lexer to set the flag. It can also be used in
84 /// tests to simulate a non-first line. The default value is `true`.
85 #[inline]
86 pub fn set_is_first_line(&mut self, is_first_line: bool) {
87 self.is_first_line = is_first_line;
88 }
89}
90
91/// Error returned by the [Input] function
92pub type Error = std::io::Error;
93
94/// Result of the [Input] function
95pub type Result = std::result::Result<String, Error>;
96
97/// Line-oriented source code reader
98///
99/// An `Input` implementor provides the parser with source code by reading from underlying source.
100///
101/// [`InputObject`] is an object-safe version of this trait.
102#[must_use = "Input instances should be used by a parser"]
103pub trait Input {
104 /// Reads a next line of the source code.
105 ///
106 /// The input function is line-oriented; that is, this function returns a string that is
107 /// terminated by a newline unless the end of input (EOF) is reached, in which case the
108 /// remaining characters up to the EOF must be returned without a trailing newline. If there
109 /// are no more characters at all, the returned line is empty.
110 ///
111 /// Errors returned from this function are considered unrecoverable. Once an error is returned,
112 /// this function should not be called any more.
113 fn next_line(&mut self, context: &Context) -> impl Future<Output = Result>;
114}
115
116impl<T> Input for T
117where
118 T: DerefMut,
119 T::Target: Input,
120{
121 fn next_line(&mut self, context: &Context) -> impl Future<Output = Result> {
122 self.deref_mut().next_line(context)
123 }
124}
125
126/// Object-safe adapter for the [`Input`] trait
127///
128/// `InputObject` is an object-safe version of the [`Input`] trait. It allows
129/// the trait to be used as a trait object, which is necessary for dynamic
130/// dispatch.
131///
132/// The umbrella implementation is provided for all types that implement the
133/// [`Input`] trait.
134pub trait InputObject {
135 fn next_line<'a>(
136 &'a mut self,
137 context: &'a Context,
138 ) -> Pin<Box<dyn Future<Output = Result> + 'a>>;
139}
140
141impl<T: Input> InputObject for T {
142 fn next_line<'a>(
143 &'a mut self,
144 context: &'a Context,
145 ) -> Pin<Box<dyn Future<Output = Result> + 'a>> {
146 Box::pin(Input::next_line(self, context))
147 }
148}
149
150mod memory;
151pub use memory::Memory;
152
153mod fd_reader_2;
154pub use fd_reader_2::FdReader2;
155
156mod echo;
157pub use echo::Echo;
158
159mod ignore_eof;
160pub use ignore_eof::IgnoreEof;
161
162mod reporter;
163pub use reporter::Reporter;
164
165mod eof_guard;
166pub use eof_guard::{EofGuard, IgnoreEofConfig, SuspendedJobsGuardConfig};