use anyhow::{self, Context};
use chrono::DateTime;
use clap::{self, Parser};
use quick_flash::credentials::get_credentials_from_command_line;
use quick_flash::credentials_manager::CredentialsManager;
use quick_flash::storage::Storage;
use quick_flash::{flash_firmware, get_probes, BaseDirs};
use std::process::exit;
#[derive(clap::Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
firmware_name: Option<String>,
firmware_version: Option<String>,
#[arg(long, short)]
list: bool,
#[arg(long)]
probe: Option<String>,
#[arg(long)]
list_probes: bool,
#[arg(long)]
clear_cache: bool,
#[arg(long, short('r'))]
connect_under_reset: bool,
#[arg(long)]
dates: bool,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
if args.list_probes {
let probes = get_probes()?;
println!(
"VID:PID:Serial (name) listing of {} available debug probe{}:",
probes.len(),
if probes.len().eq(&1) { "" } else { "s" }
);
for probe in probes {
println!(
" - {:04X}:{:04X}:{} ({})",
probe.vendor_id,
probe.product_id,
probe.serial_number.unwrap_or_default(),
probe.identifier
);
}
exit(0);
}
let base_dirs = BaseDirs::new()?;
if args.clear_cache {
eprintln!("Clearing cache directory...");
base_dirs
.clear_firmware_cache()
.context("Failed to clear firmware cache directory")?;
}
let creds_manager = CredentialsManager::new(base_dirs.creds_dir);
let mut all_creds = creds_manager
.get_all()
.context("Failed to load saved credentials")?;
if all_creds.is_empty() {
let creds = get_credentials_from_command_line()
.context("Failed to read credentials from the command line")?;
creds_manager
.add(creds)
.context("Failed to save new credentials")?;
eprintln!("Credentials saved successfully");
all_creds = creds_manager.get_all()?;
}
if all_creds.len() > 1 {
anyhow::bail!("Multiple credentials management is not supported in this version");
}
let creds = all_creds.first().unwrap();
eprintln!("Connecting to \"{}\" storage...", creds.user_storage_name);
let storage = Storage::new(creds).context("Failed to init storage client")?;
let (firmwares, firmwares_modified) = storage
.list_firmwares()
.context("Failed to fetch firmware names from the Bucket")?
.iter()
.map(|f| (f.name.clone(), f.last_modified))
.collect::<(Vec<String>, Vec<i64>)>();
if firmwares.is_empty() {
anyhow::bail!("No firmware found in the Bucket");
}
if args.list && args.firmware_name.is_none() {
println!(
"Listing {} available firmware name{}:",
firmwares.len(),
if firmwares.len().eq(&1) { "" } else { "s" }
);
for (name, timestamp) in firmwares.iter().zip(firmwares_modified.iter()) {
if args.dates {
println!(
" - {} ({})",
name,
DateTime::from_timestamp(*timestamp, 0)
.ok_or(anyhow::anyhow!("not a timestamp"))?
);
} else {
println!(" - {}", name);
}
}
exit(0);
}
let firmware_name = match args.firmware_name {
Some(n) => {
if !firmwares.contains(&n) {
anyhow::bail!("Firmware name \"{}\" not found in the Bucket", n)
}
n
}
None => {
anyhow::bail!(
"Please specify firmware name to continue (you can use the --list option to list all names)"
)
}
};
let (versions, versions_modified) = storage
.list_firmware_versions(&firmware_name)
.context("Failed to fetch firmware versions from the Bucket")?
.iter()
.map(|f| (f.version.clone(), f.last_modified))
.collect::<(Vec<String>, Vec<i64>)>();
if args.list && args.firmware_version.is_none() {
println!(
"Listing {} version{} of firmware \"{}\"",
versions.len(),
if versions.len().eq(&1) { "" } else { "s" },
firmware_name
);
for (version, timestamp) in versions.iter().zip(versions_modified.iter()) {
if args.dates {
println!(
" - {} ({})",
version,
DateTime::from_timestamp(*timestamp, 0)
.ok_or(anyhow::anyhow!("not a timestamp"))?
);
} else {
println!(" - {}", version);
}
}
exit(0);
} else if args.list && args.firmware_version.is_some() {
anyhow::bail!("Invalid use of the --list command")
}
let firmware_version = match args.firmware_version {
Some(v) => {
if !versions.contains(&v) {
anyhow::bail!("Firmware version \"{}\" not found in the Bucket", v)
}
v
}
None => {
anyhow::bail!(
"Please specify firmware version to continue (you can use the --list option to list all versions)"
)
}
};
let probes = get_probes()?;
let probe = match args.probe {
Some(ref p) => probes
.iter()
.find(|probe| probe.serial_number.as_ref().expect("Probe without serial") == p)
.context("Probe not found")?,
None => &probes[0],
};
let probe = probe.open().context("Failed to open probe")?;
let firmware = storage
.download_firmware(
&firmware_name,
&firmware_version,
&base_dirs.firmware_cache_dir,
)
.context("Failed to download firmware")?;
flash_firmware(probe, firmware, args.connect_under_reset, &|s| {
eprintln!("{}", s);
})?;
Ok(())
}