• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 14550230066

19 Apr 2025 02:53PM UTC coverage: 44.351%. Remained the same
14550230066

push

github

fasterthanlime
Fix facet-dev not compiling on windows

Termion does not support the platform

4966 of 11197 relevant lines covered (44.35%)

50.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/facet-dev/src/menu.rs
1
#![cfg_attr(windows, allow(dead_code))]
2

3
use yansi::Style;
4

5
/// A menu item
6
pub struct MenuItem {
7
    /// may contain `[]`, like `[Y]es` — that's the shortcut key. pressing it immediately executes the action
8
    pub label: String,
9

10
    /// style for the action
11
    pub style: Style,
12

13
    /// returned by show_menu
14
    pub action: String,
15
}
16

17
#[cfg(windows)]
18
pub fn show_menu(question: &str, items: &[MenuItem]) -> Option<String> {
19
    _ = (question, items);
20
    None
21
}
22

23
#[cfg(not(windows))]
24
pub fn show_menu(question: &str, items: &[MenuItem]) -> Option<String> {
×
25
    // Requires the 'termion' crate for raw input handling
26
    use std::io::{self, Write};
27
    use termion::event::{Event, Key};
28
    use termion::input::TermRead;
29
    use termion::raw::IntoRawMode;
30
    use yansi::Paint as _;
31

32
    use termion::color;
33
    println!("{}", question);
×
34
    for item in items.iter() {
×
35
        let label = &item.label;
×
36
        let mut chars = label.chars().peekable();
×
37
        let mut after_colon = false;
×
38
        while let Some(ch) = chars.next() {
×
39
            // Check for shortcut
40
            if ch == '[' {
×
41
                let mut shortcut = String::new();
×
42
                while let Some(&next_ch) = chars.peek() {
×
43
                    if next_ch == ']' {
×
44
                        chars.next(); // consume ']'
×
45
                        print!("[{}]", shortcut.paint(item.style));
×
46
                        break;
×
47
                    } else {
×
48
                        shortcut.push(next_ch);
×
49
                        chars.next();
×
50
                    }
×
51
                }
52
                continue;
×
53
            }
×
54
            // Check for ':'
×
55
            if !after_colon && ch == ':' {
×
56
                after_colon = true;
×
57
                print!("{}", color::Fg(color::LightBlack));
×
58
                print!("{}", ch);
×
59
                continue;
×
60
            }
×
61
            // After colon? Dim color, otherwise normal
×
62
            if after_colon {
×
63
                print!("{}", ch.to_string().dim());
×
64
            } else {
×
65
                print!("{}", ch);
×
66
            }
×
67
        }
68
        if after_colon {
×
69
            print!("{}", color::Fg(color::Reset));
×
70
        }
×
71
        println!();
×
72
    }
73
    print!("Enter your choice: ");
×
74
    io::stdout().flush().unwrap();
×
75

×
76
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
77
    let tty = std::fs::OpenOptions::new()
×
78
        .read(true)
×
79
        .write(true)
×
80
        .open("/dev/tty")
×
81
        .unwrap();
×
82
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
83
    let mut stdout = tty.try_clone().unwrap().into_raw_mode().unwrap();
×
84
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
85
    let stdin = tty;
×
86
    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
×
87
    let mut stdout = stdout().into_raw_mode().unwrap();
×
88
    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
×
89
    let stdin = stdin();
×
90
    let mut result = String::new();
×
91
    let mut selected_action = String::new();
×
92

93
    for evt in stdin.events() {
×
94
        let evt = evt.unwrap();
×
95
        match evt {
×
96
            Event::Key(Key::Char(c)) => {
×
97
                if c.is_ascii_digit() {
×
98
                    result.push(c);
×
99
                    write!(stdout, "{}", c).unwrap();
×
100
                    stdout.flush().unwrap();
×
101

102
                    if let Ok(idx) = result.parse::<usize>() {
×
103
                        if idx >= 1 && idx <= items.len() {
×
104
                            selected_action = items[idx - 1].action.clone();
×
105
                            break;
×
106
                        }
×
107
                    }
×
108
                } else {
109
                    // Check for shortcut key in label. Look for e.g. [Y]
110
                    let mut found = false;
×
111
                    for item in items {
×
112
                        if let Some(start) = item.label.find('[') {
×
113
                            if let Some(end) = item.label[start + 1..].find(']') {
×
114
                                let shortcut = &item.label[start + 1..start + 1 + end];
×
115
                                if shortcut.eq_ignore_ascii_case(&c.to_string()) {
×
116
                                    selected_action = item.action.clone();
×
117
                                    found = true;
×
118
                                    break;
×
119
                                }
×
120
                            }
×
121
                        }
×
122
                    }
123
                    if found {
×
124
                        break;
×
125
                    }
×
126
                }
127
            }
128
            Event::Key(Key::Backspace) => {
129
                if !result.is_empty() {
×
130
                    result.pop();
×
131
                    write!(stdout, "\x08 \x08").unwrap(); // Erase last character
×
132
                    stdout.flush().unwrap();
×
133
                }
×
134
            }
135
            Event::Key(Key::Esc) => {
136
                // Allow cancelling with ESC
137
                break;
×
138
            }
139
            Event::Key(Key::Ctrl('c')) | Event::Key(Key::Ctrl('d')) => {
140
                // Allow cancelling with Ctrl-C or Ctrl-D
141
                break;
×
142
            }
143
            _ => {}
×
144
        }
145
    }
146

147
    if !selected_action.is_empty() {
×
148
        Some(selected_action)
×
149
    } else {
150
        None
×
151
    }
152
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc