yash_env/input/
fd_reader.rs1use super::{Context, Input, Result};
20use crate::io::Fd;
21use crate::option::State;
22use crate::system::SharedSystem;
23use std::cell::Cell;
24use std::rc::Rc;
25use std::slice::from_mut;
26
27#[derive(Clone, Debug)]
37#[must_use = "FdReader does nothing unless used by a parser"]
38pub struct FdReader {
39 fd: Fd,
41 system: SharedSystem,
43 echo: Option<Rc<Cell<State>>>,
45}
46
47impl FdReader {
48 pub fn new(fd: Fd, system: SharedSystem) -> Self {
54 let echo = None;
55 FdReader { fd, system, echo }
56 }
57
58 #[deprecated = "use Echo instead"]
77 pub fn set_echo(&mut self, echo: Option<Rc<Cell<State>>>) {
78 self.echo = echo;
79 }
80}
81
82impl Input for FdReader {
83 async fn next_line(&mut self, _context: &Context) -> Result {
84 let mut bytes = Vec::new();
87 loop {
88 let mut byte = 0;
89 match self.system.read_async(self.fd, from_mut(&mut byte)).await {
90 Ok(0) => break,
92
93 Ok(count) => {
94 assert_eq!(count, 1);
95 bytes.push(byte);
96 if byte == b'\n' {
97 break;
98 }
99 }
100
101 Err(errno) => return Err(errno.into()),
102 }
103 }
104
105 let line = String::from_utf8(bytes)
107 .unwrap_or_else(|e| String::from_utf8_lossy(&e.into_bytes()).into());
108
109 if let Some(echo) = &self.echo {
110 if echo.get() == State::On {
111 let _ = self.system.write_all(Fd::STDERR, line.as_bytes()).await;
112 }
113 }
114
115 Ok(line)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::System;
123 use crate::system::Errno;
124 use crate::system::Mode;
125 use crate::system::OfdAccess;
126 use crate::system::OpenFlag;
127 use crate::system::r#virtual::FileBody;
128 use crate::system::r#virtual::Inode;
129 use crate::system::r#virtual::VirtualSystem;
130 use assert_matches::assert_matches;
131 use futures_util::FutureExt;
132
133 #[test]
134 fn empty_reader() {
135 let system = VirtualSystem::new();
136 let system = SharedSystem::new(Box::new(system));
137 let mut reader = FdReader::new(Fd::STDIN, system);
138
139 let line = reader
140 .next_line(&Context::default())
141 .now_or_never()
142 .unwrap()
143 .unwrap();
144 assert_eq!(line, "");
145 }
146
147 #[test]
148 fn one_line_reader() {
149 let system = VirtualSystem::new();
150 {
151 let state = system.state.borrow_mut();
152 let file = state.file_system.get("/dev/stdin").unwrap();
153 file.borrow_mut().body = FileBody::new(*b"echo ok\n");
154 }
155 let system = SharedSystem::new(Box::new(system));
156 let mut reader = FdReader::new(Fd::STDIN, system);
157
158 let line = reader
159 .next_line(&Context::default())
160 .now_or_never()
161 .unwrap()
162 .unwrap();
163 assert_eq!(line, "echo ok\n");
164 let line = reader
165 .next_line(&Context::default())
166 .now_or_never()
167 .unwrap()
168 .unwrap();
169 assert_eq!(line, "");
170 }
171
172 #[test]
173 fn reader_with_many_lines() {
174 let system = VirtualSystem::new();
175 {
176 let state = system.state.borrow_mut();
177 let file = state.file_system.get("/dev/stdin").unwrap();
178 file.borrow_mut().body = FileBody::new(*b"#!/bin/sh\necho ok\nexit");
179 }
180 let system = SharedSystem::new(Box::new(system));
181 let mut reader = FdReader::new(Fd::STDIN, system);
182
183 let line = reader
184 .next_line(&Context::default())
185 .now_or_never()
186 .unwrap()
187 .unwrap();
188 assert_eq!(line, "#!/bin/sh\n");
189 let line = reader
190 .next_line(&Context::default())
191 .now_or_never()
192 .unwrap()
193 .unwrap();
194 assert_eq!(line, "echo ok\n");
195 let line = reader
196 .next_line(&Context::default())
197 .now_or_never()
198 .unwrap()
199 .unwrap();
200 assert_eq!(line, "exit");
201 let line = reader
202 .next_line(&Context::default())
203 .now_or_never()
204 .unwrap()
205 .unwrap();
206 assert_eq!(line, "");
207 }
208
209 #[test]
210 fn reading_from_file() {
211 let system = VirtualSystem::new();
212 {
213 let mut state = system.state.borrow_mut();
214 let file = Rc::new(Inode::new("echo file\n").into());
215 state.file_system.save("/foo", file).unwrap();
216 }
217 let mut system = SharedSystem::new(Box::new(system));
218 let path = c"/foo";
219 let fd = system
220 .open(
221 path,
222 OfdAccess::ReadOnly,
223 OpenFlag::CloseOnExec.into(),
224 Mode::empty(),
225 )
226 .unwrap();
227 let mut reader = FdReader::new(fd, system);
228
229 let line = reader
230 .next_line(&Context::default())
231 .now_or_never()
232 .unwrap()
233 .unwrap();
234 assert_eq!(line, "echo file\n");
235 let line = reader
236 .next_line(&Context::default())
237 .now_or_never()
238 .unwrap()
239 .unwrap();
240 assert_eq!(line, "");
241 }
242
243 #[test]
244 fn reader_error() {
245 let mut system = VirtualSystem::new();
246 system.current_process_mut().close_fd(Fd::STDIN);
247 let system = SharedSystem::new(Box::new(system));
248 let mut reader = FdReader::new(Fd::STDIN, system);
249
250 let error = reader
251 .next_line(&Context::default())
252 .now_or_never()
253 .unwrap()
254 .unwrap_err();
255 assert_eq!(error.raw_os_error(), Some(Errno::EBADF.0));
256 }
257
258 #[test]
259 fn echo_off() {
260 let system = VirtualSystem::new();
261 let state = Rc::clone(&system.state);
262 {
263 let state = state.borrow();
264 let file = state.file_system.get("/dev/stdin").unwrap();
265 file.borrow_mut().body = FileBody::new(*b"one\ntwo");
266 }
267 let system = SharedSystem::new(Box::new(system));
268 let mut reader = FdReader::new(Fd::STDIN, system);
269 #[allow(deprecated)]
270 reader.set_echo(Some(Rc::new(Cell::new(State::Off))));
271
272 let _ = reader
273 .next_line(&Context::default())
274 .now_or_never()
275 .unwrap();
276 let state = state.borrow();
277 let file = state.file_system.get("/dev/stderr").unwrap();
278 assert_matches!(&file.borrow().body, FileBody::Regular { content, .. } => {
279 assert_eq!(content, &[]);
280 });
281 }
282
283 #[test]
284 fn echo_on() {
285 let system = VirtualSystem::new();
286 let state = Rc::clone(&system.state);
287 {
288 let state = state.borrow();
289 let file = state.file_system.get("/dev/stdin").unwrap();
290 file.borrow_mut().body = FileBody::new(*b"one\ntwo");
291 }
292 let system = SharedSystem::new(Box::new(system));
293 let mut reader = FdReader::new(Fd::STDIN, system);
294 #[allow(deprecated)]
295 reader.set_echo(Some(Rc::new(Cell::new(State::On))));
296
297 let _ = reader
298 .next_line(&Context::default())
299 .now_or_never()
300 .unwrap();
301 {
302 let state = state.borrow();
303 let file = state.file_system.get("/dev/stderr").unwrap();
304 assert_matches!(&file.borrow().body, FileBody::Regular { content, .. } => {
305 assert_eq!(content, b"one\n");
306 });
307 }
308 let _ = reader
309 .next_line(&Context::default())
310 .now_or_never()
311 .unwrap();
312 {
313 let state = state.borrow();
314 let file = state.file_system.get("/dev/stderr").unwrap();
315 assert_matches!(&file.borrow().body, FileBody::Regular { content, .. } => {
316 assert_eq!(content, b"one\ntwo");
317 });
318 }
319 }
320}