Skip to content

Commit

Permalink
ravedude: add newline_after and newline_on
Browse files Browse the repository at this point in the history
newline_after adds a newline after the n’th byte
newline_on adds a newline after the given byte
  • Loading branch information
tippfehlr committed May 19, 2024
1 parent b82a0d7 commit 464f52f
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 27 deletions.
50 changes: 30 additions & 20 deletions ravedude/src/console.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use anyhow::Context as _;
use std::io::Read as _;
use std::io::Write as _;

Expand Down Expand Up @@ -32,6 +31,7 @@ pub fn open(args: Args) -> anyhow::Result<()> {
task_message!("", "{}", "CTRL+C to exit.".dimmed());
// Empty line for visual consistency
eprintln!();

let mut rx = serialport::new(port.to_string_lossy(), baudrate)
.timeout(std::time::Duration::from_secs(2))
.open_native()
Expand All @@ -43,13 +43,29 @@ pub fn open(args: Args) -> anyhow::Result<()> {

// Set a CTRL+C handler to terminate cleanly instead of with an error.
ctrlc::set_handler(move || {
eprintln!("");
eprintln!();
eprintln!("Exiting.");
std::process::exit(0);
})
.context("failed setting a CTRL+C handler")?;

let newline_after = match args.newline_after {
Some(n) => n,
None => match args.output_mode {
Some(Hex) | Some(Dec) => 16,
Some(Bin) => 8,
_ => 0,
},
};

let (spaces, space_after) = if args.newline_on.is_none() && newline_after % 4 == 0 {
(true, 4)
} else {
(false, 0)
};

let mut byte_count = 0;

// Spawn a thread for the receiving end because stdio is not portably non-blocking...
std::thread::spawn(move || loop {
#[cfg(not(target_os = "windows"))]
Expand Down Expand Up @@ -82,32 +98,26 @@ pub fn open(args: Args) -> anyhow::Result<()> {
Some(Ascii) | None => unreachable!(),
Some(Hex) => {
write!(stdout, "{:02x} ", byte).unwrap();
if byte_count % 4 == 0 {
write!(stdout, " ").unwrap();
}
if byte_count % 16 == 0 {
writeln!(stdout).unwrap();
}
}
Some(Dec) => {
write!(stdout, "{:03} ", byte).unwrap();
if byte_count % 4 == 0 {
write!(stdout, " ").unwrap();
}
if byte_count % 16 == 0 {
writeln!(stdout).unwrap();
}
}
Some(Bin) => {
write!(stdout, "{:08b} ", byte).unwrap();
if byte_count % 4 == 0 {
write!(stdout, " ").unwrap();
}
if byte_count % 8 == 0 {
writeln!(stdout).unwrap();
}
}
}
// don’t execute in ascii mode, ascii is unreachable here
if spaces && byte_count % space_after == 0 {
write!(stdout, " ").unwrap();
}
if args.newline_on.is_none() && byte_count % newline_after == 0 {
writeln!(stdout).unwrap();
}
if args.newline_on.is_some()
&& *byte as char == args.newline_on.unwrap()
{
writeln!(stdout).unwrap();
}
}
}
}
Expand Down
76 changes: 69 additions & 7 deletions ravedude/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Context as _;
use anyhow::{bail, Context as _};
use colored::Colorize as _;
use structopt::clap::AppSettings;

Expand Down Expand Up @@ -35,6 +35,51 @@ impl FromStr for OutputMode {
}
}

// this could have fewer nested if’s, but this produces better error messages
fn parse_newline_on(s: &str) -> Result<char, anyhow::Error> {
if let Ok(c) = s.parse::<char>() {
Ok(c)

// if it starts with 0x then parse the hex byte
} else if &s[0..2] == "0x" {
if s.len() == 4 {
if let Ok(n) = u8::from_str_radix(&s[2..4], 16) {
Ok(n as char)
} else {
bail!("invalid hex byte")
}
} else {
bail!("hex byte must have 2 characters")
}
// if it starts with 0b then parse the binary byte
} else if &s[0..2] == "0b" {
if s.len() == 10 {
if let Ok(n) = u8::from_str_radix(&s[2..10], 2) {
Ok(n as char)
} else {
bail!("invalid binary byte")
}
} else {
bail!("binary byte must have 8 characters")
}
} else {
bail!("must be a single character or a byte in hex or binary notation")
}
}

#[test]
fn test_parse_newline_on() {
assert_eq!(parse_newline_on("a").unwrap(), 'a');
assert_eq!(parse_newline_on("\n").unwrap(), '\n');
assert_eq!(parse_newline_on("0x41").unwrap(), 'A');
assert_eq!(parse_newline_on("0b01000001").unwrap(), 'A');
assert!(parse_newline_on("not a char").is_err());
assert!(parse_newline_on("0x").is_err());
assert!(parse_newline_on("0xzz").is_err());
assert!(parse_newline_on("0b").is_err());
assert!(parse_newline_on("0b0a0a0a0a").is_err());
}

/// ravedude is a rust wrapper around avrdude for providing the smoothest possible development
/// experience with rust on AVR microcontrollers.
///
Expand Down Expand Up @@ -74,6 +119,25 @@ struct Args {
#[structopt(long = "debug-avrdude")]
debug_avrdude: bool,

/// Output mode.
///
/// Can be ascii, hex, dec or bin
#[structopt(short = "o")]
output_mode: Option<OutputMode>,

/// Print a newline after this byte
/// not used with output_mode ascii
/// hex (0x) and bin (0b) notations are supported.
#[structopt(long = "newline-on", parse(try_from_str = parse_newline_on))]
newline_on: Option<char>,

/// Print a newline after n bytes
/// not used with output_mode ascii
/// defaults to 16 for hex and dec and 8 for bin
/// if dividable by 4, bytes will be grouped to 4
#[structopt(long = "newline-after")]
newline_after: Option<u8>,

/// Which board to interact with.
///
/// Must be one of the known board identifiers:
Expand Down Expand Up @@ -101,12 +165,6 @@ struct Args {
/// If no binary is given, flashing will be skipped.
#[structopt(name = "BINARY", parse(from_os_str))]
bin: Option<std::path::PathBuf>,

/// Output mode.
///
/// Can be ascii, hex, dec or bin
#[structopt(short = "o")]
output_mode: Option<OutputMode>,
}

fn main() {
Expand All @@ -125,6 +183,10 @@ fn ravedude() -> anyhow::Result<()> {

let board = board::get_board(&args.board).expect("board not found");

if args.newline_on.is_some() && args.newline_after.is_some() {
bail!("newline_on and newline_after cannot be used at the same time");
}

task_message!("Board", "{}", board.display_name());

if let Some(wait_time) = args.reset_delay {
Expand Down

0 comments on commit 464f52f

Please sign in to comment.