Skip to main content

zql_cli/core/
pager.rs

1use crate::regex;
2use std::collections::VecDeque;
3use std::process::{Child, Command, Stdio};
4
5pub struct Pager {
6    command: String,
7    args: VecDeque<String>,
8    failed: bool,
9}
10
11impl Pager {
12    pub fn new(pager: String) -> Self {
13        let regex = regex!(r"\bless\b");
14        let mut args = pager.split_whitespace().map(String::from).collect::<VecDeque<_>>();
15        let command = args.pop_front().unwrap_or_else(|| String::from("less"));
16        if regex.is_match(&command) {
17            // -e or --quit-at-eof
18            //     Causes less to automatically exit the second time it
19            //     reaches end-of-file.  By default, the only way to exit
20            //     less is via the "q" command.
21            // -F or --quit-if-one-screen
22            //     Causes less to automatically exit if the entire file
23            //     can be displayed on the first screen.
24            // -Q or --QUIET or --SILENT
25            //     Causes totally "quiet" operation: the terminal bell is
26            //     never rung.  If the terminal has a "visual bell", it is
27            //     used in all cases where the terminal bell would have
28            //     been rung.
29            // -R or --RAW-CONTROL-CHARS
30            //     Like -r, but only ANSI "color" escape sequences and OSC 8
31            //     hyperlink sequences are output in "raw" form.  Unlike -r,
32            //     the screen appearance is maintained correctly, provided
33            //     that there are no escape sequences in the file other than
34            //     these types of escape sequences.  Color escape sequences
35            //     are only supported when the color is changed within one
36            //     line, not across lines.  In other words, the beginning
37            //     of each line is assumed to be normal (non-colored),
38            //     regardless of any escape sequences in previous lines.
39            //     For the purpose of keeping track of screen appearance,
40            //     these escape sequences are assumed to not move the cursor.
41            // -X or --no-init
42            //     Disables sending the termcap initialization and
43            //     deinitialization strings to the terminal.  This is
44            //     sometimes desirable if the deinitialization string
45            //     does something unnecessary, like clearing the screen.
46            args.push_back(String::from("-eFQRX"));
47        }
48        Self { command, args, failed: false }
49    }
50
51    pub fn spawn_child(&mut self) -> Option<Child> {
52        if !self.failed {
53            let child = spawn_child(&self.command, &self.args);
54            self.failed = child.is_none();
55            child
56        } else {
57            None
58        }
59    }
60}
61
62fn spawn_child(command: &str, args: &VecDeque<String>) -> Option<Child> {
63    Command::new(command).args(args).stdin(Stdio::piped()).spawn().ok()
64}
65
66#[cfg(test)]
67mod tests {
68    use crate::core::pager::Pager;
69    use pretty_assertions::assert_eq;
70
71    #[test]
72    fn test_empty_command_is_set_to_less() {
73        let pager = Pager::new(String::from("  "));
74        assert_eq!(pager.command, "less");
75        assert_eq!(pager.args, vec!["-eFQRX"]);
76    }
77
78    #[test]
79    fn test_options_are_extended_for_less() {
80        let pager = Pager::new(String::from("  less  "));
81        assert_eq!(pager.command, "less");
82        assert_eq!(pager.args, vec!["-eFQRX"]);
83
84        let pager = Pager::new(String::from("  less  -abc  -def  "));
85        assert_eq!(pager.command, "less");
86        assert_eq!(pager.args, vec!["-abc", "-def", "-eFQRX"]);
87    }
88
89    #[test]
90    fn test_options_not_extended_for_more() {
91        let pager = Pager::new(String::from("  more  "));
92        assert_eq!(pager.command, "more");
93        assert_eq!(pager.args, Vec::<String>::new());
94
95        let pager = Pager::new(String::from("  more  -abc  -def  "));
96        assert_eq!(pager.command, "more");
97        assert_eq!(pager.args, vec!["-abc", "-def"]);
98    }
99}