1use std::{
2 io::{stdout, Error, Result, Write},
3 process::Command,
4 time::{Duration, Instant},
5};
6
7use crossterm::{
8 cursor::*,
9 event::{poll, read, Event, KeyCode},
10 execute, queue,
11 style::*,
12 terminal::*,
13};
14
15pub fn watch(command: String, args: Vec<String>, interval: u64) -> Result<()> {
40 let interval_duration: Duration = Duration::from_secs(interval);
41
42 let mut full_watch_command: String = command.to_owned();
43 full_watch_command.push_str(" ");
44 full_watch_command.push_str(args.join(" ").as_str());
45
46 let (program, command_arg): (&str, &str);
47 if cfg!(windows) {
48 program = "powershell";
49 command_arg = "-Command";
50 } else {
51 program = "sh";
52 command_arg = "-c";
53 }
54
55 const QUIT_MSG: &str = "Press 'q' or 'Ctrl+C' to exit";
56 let interval_msg = format!("Interval: {}s", interval);
57
58 enable_raw_mode()?;
59 execute!(stdout(), Hide, EnterAlternateScreen, EnableLineWrap)?;
60 'watchLoop: loop {
61 queue!(
63 stdout(),
64 Clear(ClearType::All),
65 MoveTo(0, 0),
66 Print("> "),
67 PrintStyledContent(full_watch_command.to_owned().rapid_blink()),
68 MoveToColumn(size().unwrap().0 - interval_msg.len() as u16),
69 PrintStyledContent(interval_msg.to_owned().bold()),
70 MoveToNextLine(2),
71 )?;
72 let output = Command::new(program)
73 .arg(command_arg)
74 .arg(&full_watch_command)
75 .output()?;
76
77 if !output.status.success() {
78 return Err(Error::other(format!(
79 "Command failed with exitCode: {}",
80 output.status.code().unwrap()
81 )));
82 }
83
84 let to_trim = String::from_utf8(output.stdout).expect("Get stdout");
85 let std_output = to_trim.trim();
86 let to_trim = String::from_utf8(output.stderr).expect("Get stderr");
87 let std_error = to_trim.trim();
88
89 queue!(
91 stdout(),
92 PrintStyledContent("Output:".bold().underlined()),
93 MoveToNextLine(1),
94 Print(std_output),
95 MoveToNextLine(1),
96 )?;
97 if !std_error.is_empty() {
98 queue!(
99 stdout(),
100 PrintStyledContent("StdErr:".bold().underlined()),
101 MoveToNextLine(1),
102 Print(std_error),
103 MoveToNextLine(1),
104 )?;
105 }
106 queue!(
107 stdout(),
108 MoveTo(size().unwrap().0 - QUIT_MSG.len() as u16, size().unwrap().1 - 1),
109 PrintStyledContent(QUIT_MSG.italic()),
110 )?;
111
112 stdout().flush()?;
114
115 let start_time = Instant::now();
117 while start_time.elapsed() < interval_duration {
118 if poll(interval_duration - start_time.elapsed())? {
119 match read()? {
120 Event::Key(event)
121 if event.code == KeyCode::Char('q')
122 || (event.code == KeyCode::Char('c')
123 && event.modifiers == crossterm::event::KeyModifiers::CONTROL) =>
124 {
125 queue!(
127 stdout(),
128 LeaveAlternateScreen,
129 Print("> "),
130 Print(full_watch_command),
131 MoveToNextLine(2),
132 PrintStyledContent("Output:".bold().underlined()),
133 MoveToNextLine(1),
134 Print(std_output),
135 MoveToNextLine(1),
136 )?;
137 if !std_error.is_empty() {
138 queue!(
139 stdout(),
140 PrintStyledContent("StdErr:".bold().underlined()),
141 MoveToNextLine(1),
142 Print(std_error),
143 MoveToNextLine(1),
144 )?;
145 }
146 stdout().flush()?;
147 break 'watchLoop;
148 }
149 _ => {}
150 }
151 }
152 }
153 }
154 execute!(stdout(), Show, DisableLineWrap)?;
155 disable_raw_mode()
156}