use anyhow::Context as _;
use colored::Colorize as _;
use structopt::clap::AppSettings;
use std::thread;
use std::time::Duration;
mod avrdude;
mod board;
mod console;
mod ui;
const MIN_VERSION_AVRDUDE: (u8, u8) = (6, 3);
#[derive(structopt::StructOpt, Debug)]
#[structopt(name = "waterman",
setting = AppSettings::ColoredHelp,
setting = AppSettings::DeriveDisplayOrder,
version = git_version::git_version!(
args = ["--always", "--dirty", "--abbrev=12"],
cargo_prefix = "v",
cargo_suffix = " (no git)",
fallback = "unknown"
))]
struct Args {
#[structopt(short = "c", long = "open-console")]
open_console: bool,
#[structopt(short = "b", long = "baudrate")]
baudrate: Option<u32>,
#[structopt(short = "P", long = "port", parse(from_os_str), env = "WATERMAN_PORT")]
port: Option<std::path::PathBuf>,
#[structopt(short = "d", long = "reset-delay_ms")]
reset_delay: Option<u64>,
#[structopt(long = "debug-avrdude")]
debug_avrdude: bool,
#[structopt(name = "BOARD", verbatim_doc_comment)]
board: String,
#[structopt(name = "BINARY", parse(from_os_str))]
bin: Option<std::path::PathBuf>,
}
fn main() {
match waterman() {
Ok(()) => (),
Err(e) => {
ui::print_error(e);
std::process::exit(1);
}
}
}
fn waterman() -> anyhow::Result<()> {
let args: Args = structopt::StructOpt::from_args();
avrdude::Avrdude::require_min_ver(MIN_VERSION_AVRDUDE)?;
let board = board::get_board(&args.board).expect("board not found");
task_message!("Board", "{}", board.display_name());
if let Some(wait_time) = args.reset_delay {
if wait_time > 0 {
println!("Waiting {} ms before proceeding", wait_time);
let wait_time = Duration::from_millis(wait_time);
thread::sleep(wait_time);
} else {
println!("Assuming board has been reset");
}
} else {
if let Some(msg) = board.needs_reset() {
warning!("this board cannot reset itself.");
eprintln!("");
eprintln!(" {}", msg);
eprintln!("");
eprint!("Once reset, press ENTER here: ");
std::io::stdin().read_line(&mut String::new())?;
}
}
let port = match args.port {
Some(port) => Ok(Some(port)),
None => match board.guess_port() {
Some(Ok(port)) => Ok(Some(port)),
p @ Some(Err(_)) => p.transpose().context(
"no matching serial port found, use -P or set WATERMAN_PORT in your environment",
),
None => Ok(None),
},
}?;
if let Some(bin) = args.bin.as_ref() {
if let Some(port) = port.as_ref() {
task_message!(
"Programming",
"{} {} {}",
bin.display(),
"=>".blue().bold(),
port.display()
);
} else {
task_message!("Programming", "{}", bin.display(),);
}
let mut avrdude = avrdude::Avrdude::run(
&board.avrdude_options(),
port.as_ref(),
bin,
args.debug_avrdude,
)?;
avrdude.wait()?;
task_message!("Programmed", "{}", bin.display());
} else {
task_message!(
"",
"{}",
"(Skip flashing because no binary was given)".dimmed()
);
}
if args.open_console {
let baudrate = args
.baudrate
.context("-b/--baudrate is needed for the serial console")?;
let port = port.context("console can only be opened for devices with USB-to-Serial")?;
task_message!("Console", "{} at {} baud", port.display(), baudrate);
task_message!("", "{}", "CTRL+C to exit.".dimmed());
eprintln!();
console::open(&port, baudrate)?;
} else if args.bin.is_none() && port.is_some() {
warning!("you probably meant to add -c/--open-console?");
}
Ok(())
}