use std::sync::Arc;
use tokio::sync::mpsc::Sender;
use super::encoder::SseEncoder;
use super::protocol::SsePayload;
use super::send_string_logged;
pub type SseControlMirror = Arc<dyn Fn(SsePayload) + Send + Sync>;
#[inline]
pub fn mirror_sse_control_optional(mirror: Option<&SseControlMirror>, payload: &SsePayload) {
if let Some(m) = mirror {
m(payload.clone());
}
}
pub async fn send_sse_control_payload_optional(
out: Option<&Sender<String>>,
mirror: Option<&SseControlMirror>,
payload: SsePayload,
context: &'static str,
encoder: &dyn SseEncoder,
) -> bool {
mirror_sse_control_optional(mirror, &payload);
let Some(tx) = out else {
return true;
};
send_string_logged(tx, encoder.encode(&payload), context).await
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use super::super::protocol::SsePayload;
use super::*;
#[tokio::test]
async fn mirror_optional_invokes_callback_when_some() {
let seen = Arc::new(Mutex::new(Vec::<SsePayload>::new()));
let seen_cb = Arc::clone(&seen);
let mirror: SseControlMirror = Arc::new(move |p| {
seen_cb.lock().expect("lock").push(p);
});
let p = SsePayload::WorkspaceChanged {
workspace_changed: true,
};
mirror_sse_control_optional(Some(&mirror), &p);
let v = seen.lock().expect("lock");
assert_eq!(v.len(), 1);
}
#[tokio::test]
async fn send_sse_control_payload_optional_without_tx_still_ok() {
let encoder = crate::cm_sse_protocol::sse::V2Encoder;
let p = SsePayload::ToolRunning {
tool_running: false,
};
assert!(send_sse_control_payload_optional(None, None, p, "test_ctx", &encoder).await);
}
#[tokio::test]
async fn send_sse_control_payload_optional_with_tx_delivers() {
let encoder = crate::cm_sse_protocol::sse::V2Encoder;
let (tx, mut rx) = mpsc::channel::<String>(4);
let p = SsePayload::ParsingToolCalls {
parsing_tool_calls: true,
};
assert!(send_sse_control_payload_optional(Some(&tx), None, p, "test_ctx", &encoder).await);
drop(tx);
let line = rx.recv().await.expect("line");
assert!(line.contains("parsing_tool_calls"));
}
}