use std::env;
use std::ffi::OsString;
pub mod app;
pub mod protocol;
pub mod quirk;
pub mod shape;
mod convert;
pub mod device;
mod format_message;
pub mod help;
mod interface;
mod legacy;
mod replay;
mod stop_signal;
mod tui;
pub use quirk::QuirkArgs;
pub use shape::ShapeArgs;
pub use crate::engine::{CANBOAT_JSON_VERSION, COPYRIGHT_ID};
pub fn help_footer() -> String {
format!("canboat.json v{CANBOAT_JSON_VERSION}\n{COPYRIGHT_ID}")
}
pub fn log_startup(name: &str, version: &str) {
log::info!("{name} v{version} (canboat.json v{CANBOAT_JSON_VERSION}) {COPYRIGHT_ID}");
}
pub(crate) fn stdout(live: bool) -> Box<dyn std::io::Write> {
let out = std::io::stdout().lock();
if live {
Box::new(out)
} else {
Box::new(std::io::BufWriter::new(out))
}
}
pub(crate) fn stdin_is_live() -> bool {
#[cfg(unix)]
{
use std::os::fd::AsFd;
if let Ok(fd) = std::io::stdin().as_fd().try_clone_to_owned() {
return !std::fs::File::from(fd)
.metadata()
.is_ok_and(|m| m.is_file());
}
}
true
}
pub fn canboat_argv() -> Vec<OsString> {
normalize(env::args_os())
}
pub fn normalize<I: IntoIterator<Item = OsString>>(args: I) -> Vec<OsString> {
args.into_iter().map(rewrite_one).collect()
}
fn rewrite_one(arg: OsString) -> OsString {
let Some(s) = arg.to_str() else {
return arg;
};
if !s.starts_with('-') || s.starts_with("--") {
return arg;
}
if s.len() <= 2 {
return arg;
}
let mut chars = s[1..].chars();
let Some(c) = chars.next() else {
return arg;
};
if !c.is_ascii_alphabetic() {
return arg;
}
OsString::from(format!("-{s}"))
}
#[cfg(test)]
mod tests {
use super::*;
fn norm(input: &[&str]) -> Vec<String> {
normalize(input.iter().map(|s| OsString::from(*s)))
.into_iter()
.map(|s| s.to_string_lossy().into_owned())
.collect()
}
#[test]
fn translates_canboat_long_options() {
assert_eq!(
norm(&["analyzer", "-json", "-nv", "-fixtime", "pgn-test"]),
vec!["analyzer", "--json", "--nv", "--fixtime", "pgn-test"]
);
}
#[test]
fn leaves_short_options_alone() {
assert_eq!(
norm(&["bin", "-r", "-w", "-d"]),
vec!["bin", "-r", "-w", "-d"]
);
}
#[test]
fn leaves_double_dash_alone() {
assert_eq!(
norm(&["bin", "--rx", "12,34", "--"]),
vec!["bin", "--rx", "12,34", "--"]
);
}
#[test]
fn leaves_single_dash_alone() {
assert_eq!(norm(&["bin", "-"]), vec!["bin", "-"]);
}
#[test]
fn leaves_negative_numbers_alone() {
assert_eq!(norm(&["bin", "-42", "-0.5"]), vec!["bin", "-42", "-0.5"]);
}
#[test]
fn translates_with_equals() {
assert_eq!(norm(&["bin", "-fixtime=foo"]), vec!["bin", "--fixtime=foo"]);
}
}