use bpaf::*;
use stm32cubeprogrammer::utility::HexAddress;
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options, version)]
pub struct Options {
#[bpaf(short('v'), long("verbose"), req_flag(()), count, map(|l| {
match l {
0 => log::LevelFilter::Info,
1 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
}
}))]
pub verbose: log::LevelFilter,
#[bpaf(long, fallback(false))]
pub quiet: bool,
#[bpaf(long, env("STM32_CUBE_PROGRAMMER_DIR"))]
pub stm32_cube_programmer_dir: std::path::PathBuf,
#[bpaf(long("serial"))]
pub probe_serial: Option<stm32cubeprogrammer::probe::Serial>,
#[bpaf(long, fallback(Protocol::Swd))]
pub protocol: Protocol,
#[bpaf(long("list"))]
pub list_probes: bool,
#[bpaf(external(target_command), many)]
pub target_commands: Vec<TargetCommand>,
}
#[derive(Debug, Clone, Bpaf, PartialEq)]
pub enum TargetCommand {
#[bpaf(command, adjacent)]
FlashBin(#[bpaf(external(bin_file_info))] BinFileInfo),
#[bpaf(command, adjacent)]
FlashHex {
#[bpaf(long)]
file: std::path::PathBuf,
},
#[bpaf(command, adjacent)]
BleUpdate(#[bpaf(external(ble_stack_info))] BleStackInfo),
#[bpaf(command, adjacent)]
BleInfo {
#[bpaf(long, fallback(None))]
compare: Option<stm32cubeprogrammer::fus::Version>,
},
#[bpaf(command, adjacent)]
Reset(#[bpaf(fallback(ResetMode::Hardware), external(reset_mode))] ResetMode),
#[bpaf(command, adjacent)]
MassErase,
#[bpaf(command, adjacent)]
Protect,
#[bpaf(command, adjacent)]
Unprotect,
}
#[derive(Debug, Clone, Bpaf, PartialEq)]
pub struct BinFileInfo {
#[bpaf(long)]
pub file: std::path::PathBuf,
#[bpaf(long)]
pub address: HexAddress,
}
impl std::fmt::Display for BinFileInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Binary file: `{}` ; Address: 0x{:x}",
self.file.display(),
self.address.0
)
}
}
#[derive(Debug, Clone, Bpaf, PartialEq)]
pub struct BleStackInfo {
#[bpaf(long)]
pub file: std::path::PathBuf,
#[bpaf(long)]
pub address: HexAddress,
#[bpaf(long)]
pub version: Option<stm32cubeprogrammer::fus::Version>,
#[bpaf(long)]
pub force: bool,
}
impl std::fmt::Display for BleStackInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"BLE stack file: `{}` ; Address: 0x{:x} ; Stack version: {}, Force update: {}",
self.file.display(),
self.address.0,
self.version
.as_ref()
.map(|v| v.to_string())
.unwrap_or_else(|| "Not given".to_string()),
self.force
)
}
}
#[derive(Debug, Clone, Bpaf, PartialEq)]
pub enum ResetMode {
Hardware,
Software,
Core,
}
impl From<ResetMode> for stm32cubeprogrammer::probe::ResetMode {
fn from(value: ResetMode) -> Self {
match value {
ResetMode::Hardware => stm32cubeprogrammer::probe::ResetMode::Hardware,
ResetMode::Software => stm32cubeprogrammer::probe::ResetMode::Software,
ResetMode::Core => stm32cubeprogrammer::probe::ResetMode::Core,
}
}
}
#[derive(Debug, Clone, Bpaf, PartialEq)]
pub enum Protocol {
Swd,
Jtag,
}
impl std::str::FromStr for Protocol {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"swd" => Ok(Protocol::Swd),
"jtag" => Ok(Protocol::Jtag),
_ => Err("Invalid protocol".to_string()),
}
}
}
impl From<Protocol> for stm32cubeprogrammer::probe::Protocol {
fn from(value: Protocol) -> Self {
match value {
Protocol::Swd => stm32cubeprogrammer::probe::Protocol::Swd,
Protocol::Jtag => stm32cubeprogrammer::probe::Protocol::Jtag,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_reset() {
std::env::set_var("STM32_CUBE_PROGRAMMER_DIR", "some/dir");
let value = options().run_inner(&["reset"]).unwrap();
println!("{:?}", value);
assert_eq!(
value.target_commands,
vec![TargetCommand::Reset(ResetMode::Hardware)]
);
let value = options().run_inner(&["reset", "--hardware"]).unwrap();
println!("{:?}", value);
assert_eq!(
value.target_commands,
vec![TargetCommand::Reset(ResetMode::Hardware)]
);
let value = options().run_inner(&["reset", "--software"]).unwrap();
println!("{:?}", value);
assert_eq!(
value.target_commands,
vec![TargetCommand::Reset(ResetMode::Software)]
);
let value = options().run_inner(&["reset", "--core"]).unwrap();
println!("{:?}", value);
assert_eq!(
value.target_commands,
vec![TargetCommand::Reset(ResetMode::Core)]
);
}
#[test]
fn parse_multi() {
std::env::set_var("STM32_CUBE_PROGRAMMER_DIR", "some/dir");
let command =
"unprotect ble-update --file stack.bin --address 0x123 flash-bin --file app.bin --address 0x456 protect reset";
let value = options()
.run_inner(command.split(' ').collect::<Vec<_>>().as_slice())
.unwrap();
println!("{:?}", value);
assert_eq!(
value.target_commands,
vec![
TargetCommand::Unprotect,
TargetCommand::BleUpdate(BleStackInfo {
file: std::path::PathBuf::from("stack.bin"),
address: HexAddress(0x123),
version: None,
force: false,
}),
TargetCommand::FlashBin(BinFileInfo {
file: std::path::PathBuf::from("app.bin"),
address: HexAddress(0x456),
}),
TargetCommand::Protect,
TargetCommand::Reset(ResetMode::Hardware),
]
)
}
}