#![deny(missing_docs)]
mod commands;
mod error;
mod output;
use clap::{Parser, Subcommand};
use output::OutputFormatter;
#[derive(Parser)]
#[command(name = "canlink")]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(short, long, global = true)]
json: bool,
}
#[derive(Subcommand)]
enum Commands {
List,
Info {
backend: String,
},
Send {
backend: String,
channel: u32,
#[arg(value_parser = parse_can_id)]
id: u32,
data: Vec<String>,
#[arg(short, long, value_name = "MS")]
periodic: Option<u64>,
#[arg(short = 'n', long, default_value = "0")]
count: u32,
},
Receive {
backend: String,
channel: u32,
#[arg(short, long, default_value = "1")]
count: usize,
},
Validate {
config: String,
},
}
fn parse_can_id(s: &str) -> Result<u32, String> {
let s = s.trim_start_matches("0x").trim_start_matches("0X");
u32::from_str_radix(s, 16).map_err(|e| format!("Invalid CAN ID: {}", e))
}
fn main() {
use canlink_hal::BackendRegistry;
use canlink_tscan::TSCanBackendFactory;
use std::sync::Arc;
let registry = BackendRegistry::global();
let tscan_factory = Arc::new(TSCanBackendFactory::new());
if let Err(e) = registry.register(tscan_factory) {
eprintln!("Warning: Failed to register tscan backend: {}", e);
}
let cli = Cli::parse();
let formatter = OutputFormatter::new(cli.json);
let result = match cli.command {
Commands::List => commands::list::execute(&formatter),
Commands::Info { backend } => commands::info::execute(&backend, &formatter),
Commands::Send {
backend,
channel,
id,
data,
periodic,
count,
} => commands::send::execute(
&backend,
channel,
id,
&data,
periodic,
if periodic.is_some() {
Some(count)
} else {
None
},
&formatter,
),
Commands::Receive {
backend,
channel,
count,
} => commands::receive::execute(&backend, channel, count, &formatter),
Commands::Validate { config } => commands::validate::execute(&config, &formatter),
};
match result {
Ok(_) => std::process::exit(0),
Err(e) => {
let _ = formatter.print_error(&e.to_string());
std::process::exit(e.exit_code());
}
}
}