1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//! CLI entry point for pocket thermal label printers over BLE or USB serial.
//!
//! Everything beyond argument parsing and process exit lives in [`cli`].
mod cli;
use clap::Parser;
use cli::args::Cli;
use cli::tips::emit_error_tips;
use tracing_subscriber::EnvFilter;
fn init_tracing(verbose: bool) {
let default = if verbose { "debug" } else { "info" };
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default));
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_target(true)
.with_writer(std::io::stderr)
.init();
}
/// Exit code for "terminated by SIGINT", by shell convention (128 + 2).
const EXIT_INTERRUPTED: i32 = 130;
fn result_code(result: anyhow::Result<i32>) -> i32 {
match result {
Ok(code) => code,
Err(error) => {
emit_error_tips(&error);
eprintln!("error: {error:#}");
1
}
}
}
fn main() {
let cli = Cli::parse();
if let Some(result) = cli::run_sync(&cli) {
let code = result_code(result);
if code != 0 {
std::process::exit(code);
}
return;
}
init_tracing(cli.verbose);
let runtime = match tokio::runtime::Builder::new_multi_thread()
// CLI jobs need one foreground worker plus the BLE notification pump,
// not one worker per logical CPU. Bounding the pool avoids spawning a
// dozen threads on larger hosts while preserving the multi-threaded
// runtime required by BleTransport's blocking disconnect backstop.
.worker_threads(2)
.enable_all()
.build()
{
Ok(runtime) => runtime,
Err(error) => {
eprintln!("error: start async runtime: {error}");
std::process::exit(1);
}
};
// On Ctrl-C, drop the in-flight command rather than exiting from under it.
// Dropping it drops the open session, and `BleTransport`'s `Drop` blocks
// until the printer is disconnected — otherwise an interrupted print
// leaves the single-client BLE link held until the printer times out.
let code = runtime.block_on(async {
tokio::select! {
result = cli::run(cli) => result_code(result),
_ = tokio::signal::ctrl_c() => {
eprintln!("\ninterrupted — releasing the printer link");
EXIT_INTERRUPTED
}
}
});
if code != 0 {
std::process::exit(code);
}
}