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