1use clap::{load_yaml, App, ArgMatches};
2use std::io;
3use crossterm::event::{Event, KeyCode};
4use tui::backend::{Backend, CrosstermBackend};
5use tui::layout::{Constraint, Direction, Layout, Rect};
6use tui::widgets::{Block, Borders};
7use tui::Terminal;
8use tui_clap::{Events, TuiClap};
9
10fn main() -> Result<(), io::Error> {
11 let yaml = load_yaml!("cli.yaml");
12 let app = App::from(yaml);
13
14 let stdout = io::stdout();
15 let backend = CrosstermBackend::new(stdout);
16 let mut terminal = Terminal::new(backend)?;
17
18 let mut tui = TuiClap::from_app(app);
19 tui.input_widget().prompt("prompt > ");
20
21 terminal.clear().expect("Could not clear terminal");
22
23 let events = Events::default();
24
25 loop {
26 draw(&mut terminal, &mut tui)?;
27 handle_input(&mut tui, &events)
28 }
29}
30
31fn handle_input(tui: &mut TuiClap, events: &Events) {
32 if let Ok(Some(Event::Key(key_event))) = events.next() {
33 match key_event.code {
34 KeyCode::Backspace => {
35 tui.state().del_char()
36 }
37 KeyCode::Enter => {
38 if let Ok(matches) = tui.parse() {
39 match handle_matches(matches) {
40 Ok(output) => {
41 for message in output {
42 tui.write_to_output(message)
43 }
44 }
45 Err(err) => tui.write_to_output(err)
46 }
47 }
48 }
49 KeyCode::Char(char) => {
50 tui.state().add_char(char)
51 },
52 _ => {}
53 }
54 }
55}
56
57fn draw<B: Backend>(terminal: &mut Terminal<B>, tui: &mut TuiClap) -> io::Result<()> {
58 terminal.draw(|f| {
59 let chunks = Layout::default()
60 .direction(Direction::Vertical)
61 .margin(1)
62 .constraints(
63 [
64 Constraint::Percentage(10),
65 Constraint::Percentage(80),
66 Constraint::Percentage(10),
67 ]
68 .as_ref(),
69 )
70 .split(f.size());
71 let block = Block::default().title("Block").borders(Borders::ALL);
72 f.render_widget(block, chunks[0]);
73 let chunks_output = Layout::default()
74 .direction(Direction::Horizontal)
75 .margin(1)
76 .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
77 .split(chunks[1]);
78 let block = Block::default().title("Block 2").borders(Borders::ALL);
79 f.render_widget(block, chunks_output[0]);
80 let inset_area = edge_inset(&chunks_output[0], 1);
81 tui.render_output(f, inset_area);
82 let block = Block::default().title("Command").borders(Borders::ALL);
83 f.render_widget(block, chunks[2]);
84
85 let inset_area = edge_inset(&chunks[2], 1);
86 tui.render_input(f, inset_area);
87 })?;
88 Ok(())
89}
90
91fn edge_inset(area: &Rect, margin: u16) -> Rect {
92 let mut inset_area = *area;
93 inset_area.x += margin;
94 inset_area.y += margin;
95 inset_area.height -= margin;
96 inset_area.width -= margin;
97
98 inset_area
99}
100
101fn handle_matches(matches: ArgMatches) -> Result<Vec<String>, String> {
102 let mut output = vec![];
103
104 let config = matches.value_of("config").unwrap_or("default.conf");
105 let out = format!("Value for config: {}", config);
106 output.push(out);
107
108 let out = format!("Using input file: {}", matches.value_of("INPUT").unwrap());
111 output.push(out);
112
113 let out = match matches.occurrences_of("v") {
116 0 => "No verbose info".to_string(),
117 1 => "Some verbose info".to_string(),
118 2 => "Tons of verbose info".to_string(),
119 _ => "Don't be crazy".to_string(),
120 };
121 output.push(out);
122
123 if let Some(matches) = matches.subcommand_matches("test") {
126 if matches.is_present("debug") {
127 let out = "Printing debug info...".to_string();
128 output.push(out);
129 } else {
130 let out = "Printing normally...".to_string();
131 output.push(out);
132 }
133 };
134
135 Ok(output)
136}