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

facet-rs / facet / 14931971708

09 May 2025 03:08PM UTC coverage: 55.308%. First build
14931971708

Pull #527

github

web-flow
Merge 10005e18e into a46b975f2
Pull Request #527: Rework type information (Def) — rebased #462

1077 of 1985 new or added lines in 50 files covered. (54.26%)

6669 of 12058 relevant lines covered (55.31%)

73.99 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> {
×
NEW
25
    // Check for environment variable to automatically accept
×
NEW
26
    if std::env::var("FACET_PRECOMMIT_ACCEPT_ALL").is_ok() {
×
27
        // If FACET_PRECOMMIT_ACCEPT_ALL is set, automatically return "apply"
NEW
28
        println!("FACET_PRECOMMIT_ACCEPT_ALL is set, automatically accepting changes");
×
NEW
29
        return Some("apply".to_string());
×
NEW
30
    }
×
31

32
    // Requires the 'termion' crate for raw input handling
33
    use std::io::{self, Write};
34
    use termion::event::{Event, Key};
35
    use termion::input::TermRead;
36
    use termion::raw::IntoRawMode;
37
    use yansi::Paint as _;
38

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

×
83
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
84
    let tty = std::fs::OpenOptions::new()
×
85
        .read(true)
×
86
        .write(true)
×
87
        .open("/dev/tty")
×
88
        .unwrap();
×
89
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
90
    let mut stdout = tty.try_clone().unwrap().into_raw_mode().unwrap();
×
91
    #[cfg(any(target_os = "linux", target_os = "macos"))]
×
92
    let stdin = tty;
×
93
    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
×
94
    let mut stdout = stdout().into_raw_mode().unwrap();
×
95
    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
×
96
    let stdin = stdin();
×
97
    let mut result = String::new();
×
98
    let mut selected_action = String::new();
×
99

100
    for evt in stdin.events() {
×
101
        let evt = evt.unwrap();
×
102
        match evt {
×
103
            Event::Key(Key::Char(c)) => {
×
104
                if c.is_ascii_digit() {
×
105
                    result.push(c);
×
106
                    write!(stdout, "{}", c).unwrap();
×
107
                    stdout.flush().unwrap();
×
108

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

154
    if !selected_action.is_empty() {
×
155
        Some(selected_action)
×
156
    } else {
157
        None
×
158
    }
159
}
×
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