//! # terminal-pet //! //! A pet whose mood follows the shell and the machine. Like every renderer it //! is thin: connect to the socket, read frames, redraw. It never touches a //! sensor and never reads the shell. //! //! Usage: //! ```text //! terminal-pet [--socket ] //! terminal-pet --help | --version //! ``` //! Socket defaults to `$XDG_RUNTIME_DIR/signald.sock`, falling back to //! `~/.local/state/signald/sock`. use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use signal_schema::Signal; use terminal_pet::{pet_from_signals, render}; const USAGE: &str = "\ Usage: terminal-pet [--socket ] --socket signald's socket (default $XDG_RUNTIME_DIR/signald.sock, else ~/.local/state/signald/sock) -h, --help print this and exit -V, --version print the version and exit "; fn main() { let socket = parse_socket(); if let Err(e) = run(&socket) { eprintln!("terminal-pet: could not read signald at {}: {e}", socket.display()); eprintln!("terminal-pet: is signald running? (signald --socket {})", socket.display()); std::process::exit(1); } } /// Connect and render live. Frames this build cannot decode are skipped by the /// iterator, so a metric appended after this renderer was built does not end /// the stream. fn run(socket: &Path) -> std::io::Result<()> { let frames = signal_client::Frames::connect(socket)?; // Keyed by (name, source) so a metric and the health of the collector that // produced it stay distinct, matching the daemon's own cache. let mut latest: BTreeMap<(u8, u8), Signal> = BTreeMap::new(); for sig in frames { let sig = sig?; latest.insert((sig.name.to_u8(), sig.source.to_u8()), sig); let snapshot: Vec = latest.values().cloned().collect(); let pet = pet_from_signals(&snapshot, now_millis()); print!("\x1b[2J\x1b[H{}", render(&pet)); } Ok(()) } fn now_millis() -> u64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|d| d.as_millis() as u64) .unwrap_or(0) } fn parse_socket() -> PathBuf { let mut args = std::env::args().skip(1); while let Some(arg) = args.next() { match arg.as_str() { "--help" | "-h" => { print!("{USAGE}"); std::process::exit(0); } "--version" | "-V" => { println!("terminal-pet {}", env!("CARGO_PKG_VERSION")); std::process::exit(0); } "--socket" => { if let Some(path) = args.next() { return PathBuf::from(path); } } _ => {} } } signal_client::default_socket_path() }