use clap::Parser;
use futures::{SinkExt, StreamExt};
use serde_json::json;
use std::io::{self, Write};
use tokio_tungstenite::{connect_async, tungstenite::Message};
#[derive(Parser, Debug)]
#[command(name = "rwi_cli")]
#[command(about = "Interactive RWI WebSocket client", long_about = None)]
struct Args {
#[arg(long, default_value = "ws://127.0.0.1:8088/rwi/v1")]
url: String,
#[arg(long, default_value = "test-token-123")]
token: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let args = Args::parse();
let url = if args.url.contains('?') {
format!("{}&token={}", args.url, args.token)
} else {
format!("{}?token={}", args.url, args.token)
};
println!("Connecting to {}...", url);
let (ws, _) = connect_async(&url).await?;
println!("Connected! Type JSON commands (or 'quit' to exit).");
println!();
let (mut write, mut read) = ws.split();
let read_task = tokio::spawn(async move {
while let Some(msg) = read.next().await {
match msg {
Ok(Message::Text(text)) => {
let text_str = text.to_string();
let v: serde_json::Value = serde_json::from_str(&text_str)
.unwrap_or_else(|_| json!({"raw": text_str}));
println!("< {}", serde_json::to_string_pretty(&v).unwrap_or(text_str));
}
Ok(Message::Close(_)) => {
println!("Connection closed by server");
break;
}
Ok(Message::Ping(_)) => {
println!("Received ping");
}
Ok(Message::Pong(_)) => {}
Err(e) => {
eprintln!("Error: {}", e);
break;
}
_ => {}
}
}
});
loop {
print!("> ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
if input == "quit" || input == "exit" {
break;
}
let cmd: serde_json::Value = match serde_json::from_str(input) {
Ok(v) => v,
Err(e) => {
eprintln!("Invalid JSON: {}", e);
continue;
}
};
let cmd = if cmd.get("rwi").is_none() || cmd.get("action_id").is_none() {
let mut cmd = cmd;
if cmd.get("action").is_some() {
cmd["rwi"] = json!("1.0");
if cmd.get("action_id").is_none() {
cmd["action_id"] = json!(uuid::Uuid::new_v4().to_string());
}
}
cmd
} else {
cmd
};
let json_str = serde_json::to_string(&cmd)?;
println!("> {}", json_str);
if let Err(e) = write.send(Message::Text(json_str.into())).await {
eprintln!("Send error: {}", e);
break;
}
}
write.close().await.ok();
read_task.abort();
println!("Goodbye!");
Ok(())
}