//! The native `console` actor: your terminal. //! //! - `@ ` sends a JSON body. `@ ` sends //! `{"type": "text", "text": ...}`. //! - A line without `@` goes to `agent` as text. //! - `/help`, `/quit`. EOF on stdin also quits. use std::sync::Arc; use serde_json::{Value, json}; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::sync::mpsc; use crate::broker::{Broker, Envelope}; pub const ID: &str = "console"; const HELP: &str = "\ @ send a JSON body to an actor @ send {\"type\":\"text\",\"text\":...} send text to the agent /quit stop all actors and exit"; /// Print a message: text bodies as text, everything else as JSON. fn show(env: &Envelope) { let reply = if env.reply_to.is_some() { " ↩" } else { "" }; match env.body["text"].as_str() { Some(text) if env.body["type"] == "text" => println!("[{}{reply}] {text}", env.from), _ => println!("[{}{reply}] {}", env.from, env.body), } } /// Print every message that arrives for the console. pub async fn run_mailbox(mut mailbox: mpsc::Receiver) { while let Some(env) = mailbox.recv().await { show(&env); } } /// Read lines until EOF or `/quit`, and send them as messages. pub async fn run_input(broker: Arc) { println!("Sausage console. Type /help for commands."); let mut lines = BufReader::new(tokio::io::stdin()).lines(); while let Ok(Some(line)) = lines.next_line().await { let line = line.trim(); let (to, body) = match line { "" => continue, "/quit" => break, "/help" => { println!("{HELP}"); continue; } _ => match line.strip_prefix('@') { Some(rest) => { let (to, text) = rest.split_once(char::is_whitespace).unwrap_or((rest, "")); let body = match serde_json::from_str::(text.trim()) { Ok(v @ Value::Object(_)) => v, _ => json!({"type": "text", "text": text.trim()}), }; (to, body) } None => ("agent", json!({"type": "text", "text": line})), }, }; broker.send(ID, Some(to), None, None, body); } }