Skip to content

Commit 1b9d6ca

Browse files
committed
Merge branch 'feat/json-output'
2 parents 8c7460d + 5c81f9a commit 1b9d6ca

File tree

11 files changed

+462
-127
lines changed

11 files changed

+462
-127
lines changed

Cargo.lock

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stm32cubeprogrammer-cli/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ bpaf = { version = "0.9", features = ["derive"] }
1515
env_logger = "0.11.5"
1616
indicatif = "0.17.9"
1717
indicatif-log-bridge = "0.2.3"
18-
stm32cubeprogrammer = { path = "../stm32cubeprogrammer" }
18+
stm32cubeprogrammer = { path = "../stm32cubeprogrammer", features = [
19+
"serde",
20+
"validations",
21+
] }
1922
dotenvy.workspace = true
2023
log.workspace = true
2124
anyhow = "1.0.94"
25+
serde_json = "1"
26+
serde = { version = "1", features = ["derive"] }
2227

2328
[dev-dependencies]
2429
test-log.workspace = true

stm32cubeprogrammer-cli/src/display_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl DisplayHandler {
3939
}
4040

4141
pub fn set_finish(&self) {
42-
self.progress_bar.finish();
42+
self.progress_bar.finish_and_clear();
4343
}
4444
}
4545

stm32cubeprogrammer-cli/src/main.rs

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
//! This crate is supplied as is without any warranty. Use at your own risk.
4949
5050
mod display_handler;
51+
mod output;
5152
mod parse;
5253

5354
use anyhow::Context;
@@ -193,13 +194,21 @@ fn init_display_handler(verbosity: log::LevelFilter) -> std::sync::Arc<Mutex<Dis
193194
std::sync::Arc::new(Mutex::new(DisplayHandler::new(logger)))
194195
}
195196

196-
/// Main entry point of the CLI
197-
fn main() -> Result<(), anyhow::Error> {
197+
fn main_inner() -> Result<crate::output::Output, anyhow::Error> {
198198
// Parse command line arguments
199199
let options = parse::options().run();
200200

201+
let mut cli_output =
202+
output::Output::new(std::env::args_os(), &options.stm32_cube_programmer_dir);
203+
204+
let verbosity = if options.quiet {
205+
log::LevelFilter::Error
206+
} else {
207+
options.verbose
208+
};
209+
201210
// Init api
202-
let display_handler = init_display_handler(options.verbosity);
211+
let display_handler = init_display_handler(verbosity);
203212
let api = stm32cubeprogrammer::CubeProgrammer::builder()
204213
.cube_programmer_dir(&options.stm32_cube_programmer_dir)
205214
.display_callback(display_handler.clone())
@@ -211,6 +220,8 @@ fn main() -> Result<(), anyhow::Error> {
211220
.list_available_probes()
212221
.with_context(|| "Failed to list available probes")?;
213222

223+
cli_output.add_probe_list(&probes);
224+
214225
// Early return if the list_probes flag is set
215226
if options.list_probes {
216227
if probes.is_empty() {
@@ -221,7 +232,7 @@ fn main() -> Result<(), anyhow::Error> {
221232
}
222233
}
223234

224-
return Ok(());
235+
return Ok(cli_output);
225236
}
226237

227238
// Select a probe
@@ -231,54 +242,77 @@ fn main() -> Result<(), anyhow::Error> {
231242
}
232243

233244
let selected_probe = select_probe(&probes, &options.probe_serial)?;
245+
cli_output.add_selected_probe(selected_probe);
234246

235247
// Create a managed connection
236248
let mut programmer_connection =
237249
ProgrammerConnection::new(&api, selected_probe, options.protocol.into());
238250

239-
programmer_connection.connection().map_err(|x| {
240-
error!("Failed to connect to target: {:?}", x);
241-
x
242-
})?;
251+
// Connect to the target and add target information to the output
252+
cli_output.add_target_information(
253+
programmer_connection
254+
.connection()
255+
.map_err(|x| {
256+
error!("Failed to connect to target: {:?}", x);
257+
x
258+
})?
259+
.general_information(),
260+
);
243261

244262
// Check if the command list includes a fus command and if so, check if the current target even supports FUS
245263
// Early return if the target does not support FUS
246264
if options
247265
.target_commands
248266
.iter()
249267
.any(|command| matches!(command, parse::TargetCommand::UpdateBleStack(_)))
250-
&& !programmer_connection.connection()?.fus_support()
268+
&& !programmer_connection
269+
.connection()?
270+
.general_information()
271+
.fus_support
251272
{
252273
error!("The target does not support FUS commands");
253274
return Err(anyhow::anyhow!("The target does not support FUS commands"));
254275
}
255276

256277
// Handle commands
257278
for command in options.target_commands {
258-
match command {
279+
let command_output = match command {
259280
parse::TargetCommand::FlashBin(bin_file_info) => {
281+
log::info!("Flash binary file: {}", bin_file_info);
282+
260283
display_handler
261284
.lock()
262285
.unwrap()
263286
.set_message("Flashing binary file");
264287

265288
programmer_connection
266289
.connection()?
267-
.download_bin_file(bin_file_info.file, bin_file_info.address.0, false, true)
290+
.download_bin_file(&bin_file_info.file, bin_file_info.address.0, false, true)
268291
.with_context(|| "Failed to flash binary file")?;
292+
293+
output::CommandOutput::FlashBin {
294+
file: bin_file_info.file,
295+
address: bin_file_info.address.0,
296+
}
269297
}
270298
parse::TargetCommand::FlashHex { file } => {
299+
log::info!("Flash hex file: `{:?}`", file);
300+
271301
display_handler
272302
.lock()
273303
.unwrap()
274304
.set_message("Flashing hex file");
275305

276306
programmer_connection
277307
.connection()?
278-
.download_hex_file(file, false, true)
308+
.download_hex_file(&file, false, true)
279309
.with_context(|| "Failed to flash hex file")?;
310+
311+
output::CommandOutput::FlashHex { file }
280312
}
281313
parse::TargetCommand::UpdateBleStack(ble_stack_info) => {
314+
log::info!("Update BLE stack: {}", ble_stack_info);
315+
282316
display_handler
283317
.lock()
284318
.unwrap()
@@ -305,7 +339,7 @@ fn main() -> Result<(), anyhow::Error> {
305339
if flash {
306340
fus_programmer
307341
.upgrade_wireless_stack(
308-
ble_stack_info.file,
342+
&ble_stack_info.file,
309343
ble_stack_info.address.0,
310344
false,
311345
true,
@@ -317,19 +351,53 @@ fn main() -> Result<(), anyhow::Error> {
317351
.start_wireless_stack()
318352
.with_context(|| "Failed to start BLE stack")?;
319353
}
354+
355+
output::CommandOutput::UpdateBleStack {
356+
file: ble_stack_info.file,
357+
address: ble_stack_info.address.0,
358+
}
359+
}
360+
parse::TargetCommand::BleStackInfo { compare } => {
361+
display_handler
362+
.lock()
363+
.unwrap()
364+
.set_message("Get BLE stack information");
365+
366+
let fus_programmer = programmer_connection.fus_connection()?;
367+
let target_version = fus_programmer.fus_info().wireless_stack_version;
368+
369+
log::info!("FUS info: {}", fus_programmer.fus_info());
370+
371+
if let Some(compare) = compare {
372+
log::info!("Comparing BLE stack versions. Given version: {} ; Version on target: {} ; Stack up to date: {}", compare, target_version, compare == target_version);
373+
}
374+
375+
fus_programmer
376+
.start_wireless_stack()
377+
.with_context(|| "Failed to start BLE stack")?;
378+
379+
output::CommandOutput::BleStackInfo(fus_programmer.fus_info().clone())
320380
}
321381
parse::TargetCommand::Reset(reset_mode) => {
382+
log::info!("Resetting target: {:?}", reset_mode);
383+
322384
display_handler
323385
.lock()
324386
.unwrap()
325387
.set_message("Resetting target");
326388

327389
programmer_connection
328390
.connection()?
329-
.reset_target(reset_mode.into())
391+
.reset_target(reset_mode.clone().into())
330392
.with_context(|| "Failed to reset target")?;
393+
394+
output::CommandOutput::Reset {
395+
reset_mode: reset_mode.into(),
396+
}
331397
}
332398
parse::TargetCommand::MassErase => {
399+
log::info!("Mass erase");
400+
333401
display_handler
334402
.lock()
335403
.unwrap()
@@ -339,8 +407,12 @@ fn main() -> Result<(), anyhow::Error> {
339407
.connection()?
340408
.mass_erase()
341409
.with_context(|| "Failed to mass erase target")?;
410+
411+
output::CommandOutput::MassErase
342412
}
343413
parse::TargetCommand::Protect => {
414+
log::info!("Enable read protection");
415+
344416
display_handler
345417
.lock()
346418
.unwrap()
@@ -350,8 +422,12 @@ fn main() -> Result<(), anyhow::Error> {
350422
.connection()?
351423
.enable_read_out_protection()
352424
.with_context(|| "Failed to enable read protection")?;
425+
426+
output::CommandOutput::Protect
353427
}
354428
parse::TargetCommand::Unprotect => {
429+
log::info!("Disable read protection");
430+
355431
display_handler
356432
.lock()
357433
.unwrap()
@@ -361,11 +437,22 @@ fn main() -> Result<(), anyhow::Error> {
361437
.connection()?
362438
.disable_read_out_protection()
363439
.with_context(|| "Failed to disable read protection")?;
440+
441+
output::CommandOutput::Unprotect
364442
}
365-
}
443+
};
366444

445+
cli_output.add_command_output(command_output);
367446
display_handler.lock().unwrap().set_finish();
368447
}
369448

449+
Ok(cli_output)
450+
}
451+
452+
/// Main entry point of the CLI
453+
fn main() -> Result<(), anyhow::Error> {
454+
let output = main_inner()?;
455+
println!("{}", serde_json::to_string_pretty(&output)?);
456+
370457
Ok(())
371458
}

0 commit comments

Comments
 (0)