Skip to main content

twinleaf_tools/
lib.rs

1use clap::Parser;
2use indicatif::MultiProgress;
3use std::path::PathBuf;
4use std::sync::OnceLock;
5use twinleaf::device::DeviceRoute;
6use twinleaf::tio::util;
7pub mod cli;
8pub mod tools;
9pub mod tui;
10
11pub use cli::*;
12
13pub fn install_error_handler() -> eyre::Result<()> {
14    color_eyre::config::HookBuilder::default()
15        .display_env_section(false)
16        .display_location_section(false)
17        .install()
18}
19
20static MULTI_PROGRESS: OnceLock<MultiProgress> = OnceLock::new();
21
22pub fn init_logging() {
23    let logger =
24        env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
25            .format_target(false)
26            .build();
27    let max_level = logger.filter();
28    let mp = MultiProgress::new();
29    indicatif_log_bridge::LogWrapper::new(mp.clone(), logger)
30        .try_init()
31        .expect("logger already initialized");
32    log::set_max_level(max_level);
33    let _ = MULTI_PROGRESS.set(mp);
34}
35
36pub fn multi_progress() -> &'static MultiProgress {
37    MULTI_PROGRESS
38        .get()
39        .expect("init_logging() must be called before multi_progress()")
40}
41
42pub trait ProxyHelp<T> {
43    fn with_proxy_help(self) -> eyre::Result<T>;
44}
45
46impl<T> ProxyHelp<T> for eyre::Result<T> {
47    fn with_proxy_help(self) -> eyre::Result<T> {
48        use color_eyre::Help;
49        self.suggestion("start `tio proxy` first if using with multiple applications")
50            .suggestion("or specify a source with -r <url>")
51    }
52}
53
54fn parse_device_route(s: &str) -> Result<DeviceRoute, String> {
55    DeviceRoute::from_str(s).map_err(|_| format!("invalid sensor route: {s:?}"))
56}
57
58fn parse_existing_file(s: &str) -> Result<PathBuf, String> {
59    let p = PathBuf::from(s);
60    match std::fs::metadata(&p) {
61        Ok(m) if m.is_file() => Ok(p),
62        Ok(_) => Err(format!("not a regular file: {s:?}")),
63        Err(e) => Err(format!("cannot read {s:?}: {e}")),
64    }
65}
66
67#[derive(Parser, Debug, Clone)]
68pub struct TioOpts {
69    /// Sensor root address (e.g., tcp://localhost, serial:///dev/ttyUSB0)
70    #[arg(
71        short = 'r',
72        long = "root",
73        default_value_t = util::default_proxy_url().to_string(),
74        value_hint = clap::ValueHint::Url,
75        help = "Sensor root address"
76    )]
77    pub root: String,
78
79    /// Sensor path in the sensor tree (e.g., /, /0, /0/1)
80    #[arg(
81        short = 's',
82        long = "sensor",
83        default_value = "/",
84        value_parser = parse_device_route,
85        help = "Sensor path in the sensor tree"
86    )]
87    pub route: DeviceRoute,
88}