Files
codeg/src-tauri/vendor/sacp-tokio/src/lib.rs
xintaofei ac0d849a7b chore: upgrade sacp to 11.0.0 stable and agent-client-protocol-schema to 0.11
- Upgrade sacp/sacp-tokio from 11.0.0-alpha.1 to 11.0.0 stable
- Upgrade agent-client-protocol-schema from 0.10 to 0.11
- Rebase vendor/sacp-tokio patches (kill_tree, CREATE_NO_WINDOW, stderr
  truncation) onto official 11.0.0 baseline
- Adapt renamed types: FileSystemCapability → FileSystemCapabilities,
  KillTerminalCommandRequest/Response → KillTerminalRequest/Response

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:10:15 +08:00

97 lines
3.1 KiB
Rust

//! Tokio-based utilities for SACP
//!
//! This crate provides higher-level functionality for working with SACP
//! that requires the Tokio async runtime, such as spawning agent processes
//! and creating connections.
mod acp_agent;
pub use acp_agent::{AcpAgent, LineDirection};
use sacp::{ByteStreams, ConnectTo, Role};
use std::sync::Arc;
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
pub struct Stdio {
debug_callback: Option<Arc<dyn Fn(&str, LineDirection) + Send + Sync + 'static>>,
}
impl Stdio {
pub fn new() -> Self {
Self {
debug_callback: None,
}
}
pub fn with_debug<F>(mut self, callback: F) -> Self
where
F: Fn(&str, LineDirection) + Send + Sync + 'static,
{
self.debug_callback = Some(Arc::new(callback));
self
}
}
impl Default for Stdio {
fn default() -> Self {
Self::new()
}
}
impl<Counterpart: Role> ConnectTo<Counterpart> for Stdio {
async fn connect_to(
self,
client: impl ConnectTo<Counterpart::Counterpart>,
) -> Result<(), sacp::Error> {
if let Some(callback) = self.debug_callback {
use futures::AsyncBufReadExt;
use futures::AsyncWriteExt;
use futures::StreamExt;
use futures::io::BufReader;
// With debug: use Lines with interception
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
// Convert stdio to line streams with debug inspection
let incoming_callback = callback.clone();
let incoming_lines = Box::pin(BufReader::new(stdin.compat()).lines().inspect(
move |result| {
if let Ok(line) = result {
incoming_callback(line, LineDirection::Stdin);
}
},
))
as std::pin::Pin<Box<dyn futures::Stream<Item = std::io::Result<String>> + Send>>;
// Create a sink that writes lines with debug logging
let outgoing_sink = Box::pin(futures::sink::unfold(
(stdout.compat_write(), callback),
async move |(mut writer, callback), line: String| {
callback(&line, LineDirection::Stdout);
let mut bytes = line.into_bytes();
bytes.push(b'\n');
writer.write_all(&bytes).await?;
Ok::<_, std::io::Error>((writer, callback))
},
))
as std::pin::Pin<Box<dyn futures::Sink<String, Error = std::io::Error> + Send>>;
ConnectTo::<Counterpart>::connect_to(
sacp::Lines::new(outgoing_sink, incoming_lines),
client,
)
.await
} else {
// Without debug: use simple ByteStreams
ConnectTo::<Counterpart>::connect_to(
ByteStreams::new(
tokio::io::stdout().compat_write(),
tokio::io::stdin().compat(),
),
client,
)
.await
}
}
}