use std::{
io::{self, stdin, stdout, Read, Write},
path::PathBuf,
sync::Arc,
};
use anyhow::Context;
use bytes::Bytes;
use clap::Parser;
use geph5_client::{Client, Config};
use nanorpc::{JrpcRequest, RpcTransport};
#[derive(Parser)]
struct CliArgs {
#[arg(short, long)]
config: PathBuf,
#[arg(short, long)]
stdio_rpc: bool,
#[arg(long)]
stdio_vpn: bool,
}
fn main() -> anyhow::Result<()> {
smolscale::permanently_single_threaded();
let args = CliArgs::parse();
let config: serde_json::Value = serde_yaml::from_slice(&std::fs::read(args.config)?)?;
let config: Config = serde_json::from_value(config)?;
let client = Client::start(config);
if args.stdio_rpc {
let rpc = Arc::new(client.control_client().0);
std::thread::spawn(move || {
let stdin = stdin();
for line in stdin.lines() {
let line = line.unwrap();
let line: JrpcRequest = serde_json::from_str(&line).unwrap();
let rpc = rpc.clone();
let resp = smolscale::block_on(async move { rpc.call_raw(line).await }).unwrap();
println!("{}", serde_json::to_string(&resp).unwrap());
}
});
} else if args.stdio_vpn {
run_stdio_vpn(client.clone())?;
}
smolscale::block_on(client.wait_until_dead())?;
Ok(())
}
fn run_stdio_vpn(client: Client) -> anyhow::Result<()> {
let client_clone = client.clone();
std::thread::spawn(move || -> anyhow::Result<()> {
let mut stdin = stdin().lock();
let mut length_buf = [0u8; 2];
loop {
match stdin.read_exact(&mut length_buf) {
Ok(_) => {
let length = u16::from_be_bytes(length_buf) as usize;
let mut packet_buf = vec![0u8; length];
stdin.read_exact(&mut packet_buf)?;
smol::future::block_on(client_clone.send_vpn_packet(Bytes::from(packet_buf)))?;
}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
break;
}
Err(e) => {
return Err(e).context("Error reading from stdin");
}
}
}
Ok(())
});
std::thread::spawn(move || -> anyhow::Result<()> {
let mut stdout = stdout().lock();
loop {
let packet = smol::future::block_on(client.recv_vpn_packet())?;
let length = std::cmp::min(packet.len(), u16::MAX as usize) as u16;
stdout.write_all(&length.to_be_bytes())?;
stdout.write_all(&packet)?;
stdout.flush()?;
}
});
Ok(())
}