use anyhow::Result;
use crate::lf::commands::chat::CliContext;
use crate::lf::commands::radio::ambient_channel;
use crate::store::BusMessage;
use crate::wave::bus::POLL_CADENCE;
use crate::wave::channel::matches_prefix;
pub fn run(channel: Option<&str>, json: bool) -> Result<()> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async {
tokio::select! {
result = follow(channel, json) => result,
_ = tokio::signal::ctrl_c() => Ok(()),
}
})
}
async fn follow(channel: Option<&str>, json: bool) -> Result<()> {
let context = CliContext::detect().await;
let Some(store) = context.store.clone() else {
eprintln!("no registry store here; nothing to tune in to");
return Ok(());
};
let prefix = match channel {
Some(channel) => channel.to_string(),
None => match ambient_channel(&context, &store).await? {
Some(channel) => channel,
None => {
eprintln!("no wave here; nothing to tune in to");
return Ok(());
}
},
};
let mut cursor = store.bus_head().await?;
loop {
tokio::time::sleep(POLL_CADENCE).await;
for message in store.read_bus_after(cursor).await? {
cursor = message.id;
if matches_prefix(&message.channel, &prefix) {
println!("{}", line_for(&message, json));
}
}
}
}
fn line_for(message: &BusMessage, json: bool) -> String {
if json {
return serde_json::json!({
"id": message.id,
"channel": message.channel,
"byline": message.byline,
"text": message.text,
"at": message.at,
})
.to_string();
}
format!("[{}] {}: {}", message.channel, message.byline, message.text)
}
#[cfg(test)]
mod tests {
use super::*;
fn message(channel: &str, byline: &str, text: &str) -> BusMessage {
BusMessage {
id: 7,
channel: channel.into(),
byline: byline.into(),
text: text.into(),
at: 1_780_000_000,
}
}
#[test]
fn a_subscription_is_a_prefix_over_the_dot_tree() {
assert!(matches_prefix("goals", "goals"));
assert!(matches_prefix("goals.148e", "goals"));
assert!(!matches_prefix("goals", "goals.148e"));
assert!(!matches_prefix("goalsmith", "goals"));
}
#[test]
fn a_heard_frame_shows_byline_beside_channel() {
assert_eq!(
line_for(&message("goals.148e", "ci", "all green"), false),
"[goals.148e] ci: all green"
);
let json: serde_json::Value =
serde_json::from_str(&line_for(&message("goals.148e", "ci", "all green"), true))
.expect("valid NDJSON");
assert_eq!(json["channel"], "goals.148e");
assert_eq!(json["byline"], "ci");
assert_eq!(json["text"], "all green");
}
}