mod diagnostics;
use anyhow::Context;
use colored::Colorize;
use diagnostics::render_diagnostics;
use std::ffi::OsString;
use std::{path::PathBuf, process};
use crate::rpc::client::RpcClient;
use crate::util::cargo::build_artifact;
use crate::util::cargo::cargo_target;
use crate::util::common_options::{
BinaryDownloadOptions, CargoOptions, OperationError, ProbeOptions,
};
use crate::util::logging::{LevelFilter, setup_logging};
use crate::util::{cli, logging};
use crate::{Config, parse_and_resolve_cli_args, run_app};
#[derive(Debug, clap::Parser)]
#[clap(
name = "cargo flash",
bin_name = "cargo flash",
version = env!("PROBE_RS_VERSION"),
long_version = env!("PROBE_RS_LONG_VERSION"),
after_long_help = CargoOptions::help_message("cargo flash")
)]
struct CliOptions {
#[arg(long)]
pub reset_halt: bool,
#[arg(value_name = "level", long)]
pub log: Option<LevelFilter>,
#[arg(value_name = "path", long)]
pub path: Option<PathBuf>,
#[arg(value_name = "directory", long)]
pub work_dir: Option<PathBuf>,
#[command(flatten)]
pub cargo_options: CargoOptions,
#[command(flatten)]
pub probe_options: ProbeOptions,
#[command(flatten)]
pub download_options: BinaryDownloadOptions,
#[command(flatten)]
pub format_options: crate::FormatOptions,
#[cfg(feature = "remote")]
#[arg(
long,
global = true,
env = "PROBE_RS_REMOTE_HOST",
help_heading = "REMOTE CONFIGURATION"
)]
host: Option<String>,
#[cfg(feature = "remote")]
#[arg(
long,
global = true,
env = "PROBE_RS_REMOTE_TOKEN",
help_heading = "REMOTE CONFIGURATION"
)]
token: Option<String>,
#[arg(long, global = true, env = "PROBE_RS_CONFIG_PRESET")]
preset: Option<String>,
}
pub async fn main(args: Vec<OsString>, config: Config) -> anyhow::Result<()> {
let opt = parse_and_resolve_cli_args::<CliOptions>(args, &config)?;
let _log_guard = setup_logging(None, opt.log);
#[cfg(feature = "remote")]
let connection_params = opt
.host
.as_ref()
.map(|host| (host.clone(), opt.token.clone()));
#[cfg(not(feature = "remote"))]
let connection_params = None;
let terminate = run_app(connection_params, async |mut client| {
let main_result = main_try(&mut client, opt).await;
let r = client.registry().await;
match main_result {
Ok(()) => Ok(false),
Err(e) => {
render_diagnostics(&r, e);
Ok(true)
}
}
})
.await?;
if terminate {
process::exit(1);
}
Ok(())
}
async fn main_try(client: &mut RpcClient, opt: CliOptions) -> Result<(), OperationError> {
if let Some(ref work_dir) = opt.work_dir {
std::env::set_current_dir(work_dir).map_err(|error| {
OperationError::FailedToChangeWorkingDirectory {
source: error,
path: work_dir.clone(),
}
})?;
}
let work_dir = std::env::current_dir()?;
let image_instr_set;
let path = if let Some(path_buf) = &opt.path {
image_instr_set = None;
path_buf.clone()
} else {
let cargo_options = opt.cargo_options.to_cargo_options();
image_instr_set = cargo_target(opt.cargo_options.target.as_deref());
build_artifact(&work_dir, &cargo_options)
.map_err(|error| {
if let Some(ref work_dir) = opt.work_dir {
OperationError::FailedToBuildExternalCargoProject {
source: error,
path: work_dir.canonicalize().unwrap(),
}
} else {
OperationError::FailedToBuildCargoProject(error)
}
})?
.path()
.into()
};
logging::eprintln(format!(
" {} {}",
"Flashing".green().bold(),
path.display()
));
let session = cli::attach_probe(client, opt.probe_options, false).await?;
cli::flash(
&session,
&path,
opt.download_options.chip_erase,
opt.format_options,
opt.download_options,
None,
image_instr_set,
)
.await?;
let core = session.core(0);
if opt.reset_halt {
core.reset_and_halt(std::time::Duration::from_millis(500))
.await
.context("Failed to reset and halt target")?;
} else {
core.reset().await.context("Failed to reset target")?;
}
Ok(())
}