#[macro_use]
extern crate crossterm;
use cargo_metadata::{CompilerMessage, Message};
use crossterm::cursor;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
use crossterm::style::{Print, Stylize};
use crossterm::terminal::{self, Clear, ClearType};
use std::env::args;
use std::io::{stdout, BufReader, Write};
use std::process::{Command, Stdio};
fn main() {
let mut args = args();
if args.len() == 0 {
return display_help();
}
args.next().unwrap();
let cmd = match args.next() {
Some(cmd) => cmd,
None => return display_help(),
};
match cmd.as_str() {
"build" => (),
"check" => (),
"run" => (),
"clippy" => (),
"-h" | "--help" | "help" => {
display_help();
return;
}
_ => {
println!("{}: no such subcommand: '{}'", "error".red(), cmd);
return;
}
}
let mut filtered_args: Vec<_> = Vec::new();
let mut color = String::from("--color=always");
for arg in args {
if arg == "-h" || arg == "--help" {
display_help();
return;
}
if arg.starts_with("--color") {
color = arg.to_string();
}
if !arg.starts_with("--message-format") {
filtered_args.push(arg);
}
}
let mut cmd_args = vec![
cmd,
color,
"--message-format=json-diagnostic-rendered-ansi".to_string(),
];
cmd_args.append(&mut filtered_args);
let mut cmd = Command::new("cargo");
cmd.stdout(Stdio::piped());
cmd.args(cmd_args);
let mut child = cmd.spawn().unwrap();
let output = BufReader::new(child.stdout.take().unwrap());
let messages: Vec<CompilerMessage> = Message::parse_stream(output)
.filter_map(|msg_r| {
if let Ok(Message::CompilerMessage(msg)) = msg_r {
Some(msg)
} else {
None
}
})
.collect();
if messages.is_empty() {
return;
}
terminal::enable_raw_mode().unwrap();
execute!(stdout(), Clear(ClearType::All), cursor::Hide).unwrap();
let mut displaying: usize = 0;
let mut scroll: usize = 0;
let help_line: String = [
"\n".reset(),
"Esc ".blue(),
"Exit ".green(),
"Left ".blue(),
"Prev Msg ".green(),
"Right ".blue(),
"Next Msg ".green(),
"Up ".blue(),
"Scroll Up ".green(),
"Down ".blue(),
"Scroll Down ".green(),
"Home ".blue(),
"First Msg ".green(),
"End ".blue(),
"Last Msg ".green(),
]
.into_iter()
.map(|content| format!("{}", content))
.collect();
let display_message = |index: usize, scroll: usize| {
let term_h = terminal::size().unwrap().1.max(5);
queue!(
stdout(),
cursor::MoveTo(0, 0),
Clear(ClearType::All),
Print(format!(
"{} {} {} {}\n\n",
"Displaying Message".green(),
format!("{}", index + 1).blue(),
"of".green(),
format!("{}", messages.len()).blue(),
)),
)
.unwrap();
let msg_h = term_h - 4;
let mut msg_lines = messages[index]
.message
.rendered
.as_ref()
.unwrap()
.lines()
.skip(scroll);
for _ in 0..msg_h {
queue!(
stdout(),
match msg_lines.next() {
Some(line) => Print(format!("{}\n", line)),
None => Print("\n".into()),
}
)
.unwrap();
}
queue!(stdout(), Print(&help_line)).unwrap();
stdout().flush().unwrap();
};
display_message(0, 0);
while let Ok(event) = event::read() {
if let Event::Key(KeyEvent { code, modifiers }) = event {
if modifiers.contains(KeyModifiers::CONTROL) {
if let KeyCode::Char('c') = code {
terminal::disable_raw_mode().unwrap();
execute!(stdout(), cursor::Show).unwrap();
return;
}
} else {
match code {
KeyCode::Esc => {
terminal::disable_raw_mode().unwrap();
execute!(stdout(), cursor::Show).unwrap();
return;
},
KeyCode::Left if displaying > 0 => {
displaying -= 1;
scroll = 0;
display_message(displaying, scroll);
}
KeyCode::Right if displaying < messages.len() - 1 => {
displaying += 1;
scroll = 0;
display_message(displaying, scroll);
}
KeyCode::Up => {
if scroll > 0 {
scroll -= 1;
display_message(displaying, scroll);
}
}
KeyCode::Down => {
scroll += 1;
display_message(displaying, scroll);
}
KeyCode::Home => {
displaying = 0;
scroll = 0;
display_message(displaying, scroll);
}
KeyCode::End => {
displaying = messages.len() - 1;
scroll = 0;
display_message(displaying, scroll);
}
_ => (),
}
}
}
}
child.wait().unwrap();
}
fn display_help() {
println!(
r#"
Error/Warning Pager for Cargo
USAGE:
burden [SUBCOMMAND] [SUBCOMMAND OPTIONS]
Supported cargo commands are:
build Compile the current package
check Analyze the current package and report errors, but don't build object files
run Run a binary or example of the local package
clippy Checks a package to catch common mistakes and improve your Rust code."#
);
}