use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use anyhow::Result;
use clap::Parser;
use rax25::sync::Client;
use rax25::{Addr, Kiss};
#[derive(Parser, Debug)]
struct Opt {
#[clap(short = 'p', default_value = "/dev/null")]
port: String,
#[clap(short = 's')]
src: String,
#[clap(short = 'r')]
cr: bool,
#[clap(short = 'e')]
ext: bool,
#[clap(short = 'v', default_value = "0")]
v: usize,
#[clap()]
dst: String,
}
fn main() -> Result<()> {
let opt = Opt::parse();
let done = Arc::new(AtomicBool::new(false));
stderrlog::new()
.module("rax25")
.verbosity(opt.v)
.init()
.unwrap();
let k = Kiss::new(&opt.port)?;
let mut c = Client::new(Addr::new(&opt.src)?, Box::new(k));
let d = done.clone();
ctrlc::set_handler(move || {
println!("Received SIGINT signal, shutting down...");
d.store(true, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
let (tx, rx) = std::sync::mpsc::channel();
let d = done.clone();
let cr = opt.cr;
std::thread::spawn(move || {
use std::io::BufRead;
while !d.load(Ordering::SeqCst) {
let stdin = std::io::stdin();
let mut iterator = stdin.lock().lines();
if let Some(line) = iterator.next() {
if cr {
tx.send(line.and_then(|s| Ok(s.trim_end().to_owned() + "\r")))
.unwrap();
} else {
tx.send(line).unwrap();
}
}
}
});
eprintln!("==== CONNECTING");
c.connect(&Addr::new(&opt.dst)?, opt.ext)?;
eprintln!("==== Connected to {}", opt.dst);
while !done.load(Ordering::SeqCst) && !c.eof() {
if let Ok(Some(data)) = c.read_until(done.clone()) {
let s = match String::from_utf8(data.clone()) {
Ok(s) => s,
Err(_) => String::from_utf8(data.iter().map(|&b| b & 0x7F).collect())?,
};
let s = if opt.cr { s.replace("\r", "\n") } else { s };
print!("{s}");
std::io::stdout().flush()?;
}
match rx.recv_timeout(std::time::Duration::from_secs(0)) {
Ok(Ok(line)) => {
if line == "exit" {
done.store(true, Ordering::SeqCst);
break;
}
c.write(&line.as_bytes())?;
}
Ok(Err(e)) => eprintln!("Error reading line: {}", e),
Err(_) => {}
};
}
done.store(true, Ordering::SeqCst);
Ok(())
}