- gateway/mod.rs: move send_json before test module (items_after_test_module) - memory/vector.rs: fix float_cmp, cast_precision_loss, approx_constant - memory/chunker.rs: fix format_collect, format_push_string, write_with_newline - memory/sqlite.rs: fix useless_vec - heartbeat/engine.rs: fix format_collect, write_with_newline - config/schema.rs: fix needless_raw_string_hashes - tools/composio.rs: fix needless_raw_string_hashes - integrations/registry.rs: fix uninlined_format_args, unused import - tunnel/mod.rs: fix doc_markdown - skills/mod.rs: allow similar_names in test module - channels/cli.rs: fix unreadable_literal - observability/mod.rs: fix manual_string_new - runtime/mod.rs: fix manual_string_new - examples/custom_memory.rs: add Default impl (new_without_default) - examples/custom_channel.rs: fix needless_borrows_for_generic_args
117 lines
3 KiB
Rust
117 lines
3 KiB
Rust
use super::traits::{Channel, ChannelMessage};
|
|
use async_trait::async_trait;
|
|
use tokio::io::{self, AsyncBufReadExt, BufReader};
|
|
use uuid::Uuid;
|
|
|
|
/// CLI channel — stdin/stdout, always available, zero deps
|
|
pub struct CliChannel;
|
|
|
|
impl CliChannel {
|
|
pub fn new() -> Self {
|
|
Self
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Channel for CliChannel {
|
|
fn name(&self) -> &str {
|
|
"cli"
|
|
}
|
|
|
|
async fn send(&self, message: &str, _recipient: &str) -> anyhow::Result<()> {
|
|
println!("{message}");
|
|
Ok(())
|
|
}
|
|
|
|
async fn listen(&self, tx: tokio::sync::mpsc::Sender<ChannelMessage>) -> anyhow::Result<()> {
|
|
let stdin = io::stdin();
|
|
let reader = BufReader::new(stdin);
|
|
let mut lines = reader.lines();
|
|
|
|
while let Ok(Some(line)) = lines.next_line().await {
|
|
let line = line.trim().to_string();
|
|
if line.is_empty() {
|
|
continue;
|
|
}
|
|
if line == "/quit" || line == "/exit" {
|
|
break;
|
|
}
|
|
|
|
let msg = ChannelMessage {
|
|
id: Uuid::new_v4().to_string(),
|
|
sender: "user".to_string(),
|
|
content: line,
|
|
channel: "cli".to_string(),
|
|
timestamp: std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs(),
|
|
};
|
|
|
|
if tx.send(msg).await.is_err() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn cli_channel_name() {
|
|
assert_eq!(CliChannel::new().name(), "cli");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn cli_channel_send_does_not_panic() {
|
|
let ch = CliChannel::new();
|
|
let result = ch.send("hello", "user").await;
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn cli_channel_send_empty_message() {
|
|
let ch = CliChannel::new();
|
|
let result = ch.send("", "").await;
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn cli_channel_health_check() {
|
|
let ch = CliChannel::new();
|
|
assert!(ch.health_check().await);
|
|
}
|
|
|
|
#[test]
|
|
fn channel_message_struct() {
|
|
let msg = ChannelMessage {
|
|
id: "test-id".into(),
|
|
sender: "user".into(),
|
|
content: "hello".into(),
|
|
channel: "cli".into(),
|
|
timestamp: 1_234_567_890,
|
|
};
|
|
assert_eq!(msg.id, "test-id");
|
|
assert_eq!(msg.sender, "user");
|
|
assert_eq!(msg.content, "hello");
|
|
assert_eq!(msg.channel, "cli");
|
|
assert_eq!(msg.timestamp, 1_234_567_890);
|
|
}
|
|
|
|
#[test]
|
|
fn channel_message_clone() {
|
|
let msg = ChannelMessage {
|
|
id: "id".into(),
|
|
sender: "s".into(),
|
|
content: "c".into(),
|
|
channel: "ch".into(),
|
|
timestamp: 0,
|
|
};
|
|
let cloned = msg.clone();
|
|
assert_eq!(cloned.id, msg.id);
|
|
assert_eq!(cloned.content, msg.content);
|
|
}
|
|
}
|