Skip to main content

sel/source/
stdin.rs

1//! Stdin-backed Source.
2
3use super::Source;
4use crate::error::SelError;
5use crate::{Line, Result};
6use std::io::{self, BufRead, BufReader, Stdin, StdinLock};
7
8pub struct StdinSource {
9    reader: BufReader<StdinLock<'static>>,
10    line_no: u64,
11}
12
13impl StdinSource {
14    pub fn new() -> Self {
15        let stdin: Stdin = io::stdin();
16        // SAFETY: mirrors StdoutSink — lock lifetime = process lifetime.
17        let lock: StdinLock<'static> = Box::leak(Box::new(stdin)).lock();
18        Self {
19            reader: BufReader::new(lock),
20            line_no: 0,
21        }
22    }
23}
24
25impl Default for StdinSource {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl Source for StdinSource {
32    fn next_line(&mut self) -> Result<Option<Line>> {
33        let mut buf: Vec<u8> = Vec::new();
34        let n = self
35            .reader
36            .read_until(b'\n', &mut buf)
37            .map_err(|source| SelError::Io {
38                path: "-".to_string(),
39                source,
40            })?;
41        if n == 0 {
42            return Ok(None);
43        }
44        if buf.ends_with(b"\n") {
45            buf.pop();
46            if buf.ends_with(b"\r") {
47                buf.pop();
48            }
49        }
50        self.line_no += 1;
51        Ok(Some(Line::new(self.line_no, buf)))
52    }
53
54    fn label(&self) -> &str {
55        "-"
56    }
57
58    fn is_seekable(&self) -> bool {
59        false
60    }
61}