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