use std::{fmt::Debug, hash::Hash};
#[cfg(feature = "unstable_protocol_v2")]
use futures::{StreamExt as _, future};
#[cfg(feature = "unstable_protocol_v2")]
use serde::{Serialize, de::DeserializeOwned};
#[cfg(feature = "unstable_protocol_v2")]
use crate::DynConnectTo;
use crate::jsonrpc::{Builder, handlers::NullHandler, run::NullRun};
use crate::role::{HasPeer, RemoteStyle};
use crate::schema::v1::{InitializeRequest, NewSessionRequest, NewSessionResponse, SessionId};
#[cfg(feature = "unstable_protocol_v2")]
use crate::schema::v1::{RequestId, Response as RpcResponse};
use crate::schema::{InitializeProxyRequest, METHOD_INITIALIZE_PROXY};
#[cfg(feature = "unstable_protocol_v2")]
use crate::schema::{ProtocolVersion, v2};
use crate::util::MatchDispatchFrom;
#[cfg(feature = "unstable_protocol_v2")]
use crate::{Channel, RawJsonRpcMessage, RawJsonRpcParams};
use crate::{ConnectTo, ConnectionTo, Dispatch, HandleDispatchFrom, Handled, Role, RoleId};
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Client;
impl Role for Client {
type Counterpart = Agent;
fn builder(self) -> Builder<Self> {
Builder::new(self).v1_client()
}
async fn default_handle_dispatch_from(
&self,
message: Dispatch,
_connection: ConnectionTo<Client>,
) -> Result<Handled<Dispatch>, crate::Error> {
Ok(Handled::No {
message,
retry: false,
})
}
fn role_id(&self) -> RoleId {
RoleId::from_singleton(self)
}
fn counterpart(&self) -> Self::Counterpart {
Agent
}
}
impl Client {
pub fn builder(self) -> Builder<Client, NullHandler, NullRun> {
<Self as Role>::builder(self)
}
#[cfg(feature = "unstable_protocol_v2")]
pub fn v2(self) -> Builder<Client, NullHandler, NullRun> {
self.builder().v2_client()
}
#[cfg(feature = "unstable_protocol_v2")]
#[must_use]
pub fn protocol_connector(self) -> ClientProtocolConnector {
ClientProtocolConnector::new()
}
pub async fn connect_with<R>(
self,
agent: impl ConnectTo<Client>,
main_fn: impl AsyncFnOnce(ConnectionTo<Agent>) -> Result<R, crate::Error>,
) -> Result<R, crate::Error> {
self.builder().connect_with(agent, main_fn).await
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Default)]
pub struct ClientProtocolConnector {
v1: Option<DynConnectToFactory<Agent>>,
v2: Option<DynConnectToFactory<Agent>>,
}
#[cfg(feature = "unstable_protocol_v2")]
impl ClientProtocolConnector {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_v1<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
where
C: ConnectTo<Agent>,
{
self.v1 = Some(DynConnectToFactory::new(client));
self
}
#[must_use]
pub fn with_v2<C>(mut self, client: impl FnMut() -> C + Send + 'static) -> Self
where
C: ConnectTo<Agent>,
{
self.v2 = Some(DynConnectToFactory::new(client));
self
}
pub async fn connect_to<C>(
mut self,
mut agent: impl FnMut() -> C + Send + 'static,
) -> Result<(), crate::Error>
where
C: ConnectTo<Client>,
{
let supported = SupportedClientProtocols {
v1: self.v1.is_some(),
v2: self.v2.is_some(),
};
let Some(selected) = supported.highest_configured() else {
return Err(crate::Error::invalid_request()
.data("client protocol connector has no configured ACP protocol implementations"));
};
match selected {
ClientProtocol::V1 => {
let client = self
.v1
.as_mut()
.expect("selected protocol is configured")
.create();
connect_client_protocol(ClientProtocol::V1, client, agent()).await
}
ClientProtocol::V2 => {
let client = self
.v2
.as_mut()
.expect("selected protocol is configured")
.create();
let agent_connection = RunningProtocolPeer::new(agent());
let (client, initialize) =
start_client_protocol(ClientProtocol::V2, client).await?;
let v2_initialize_as_v1 =
normalize_initialize_params_for_agent_protocol(&initialize, AgentProtocol::V1)?;
let (client, agent_connection, initialize_response) =
send_initialize_and_receive(client, agent_connection, initialize).await?;
if initialize_response_negotiated_v1(&initialize_response)
&& let Some(v1) = self.v1.as_mut()
{
let fallback_client = v1.create();
let (fallback_client, fallback_initialize) =
start_client_protocol(ClientProtocol::V1, fallback_client).await?;
let v1_initialize = normalize_initialize_params_for_agent_protocol(
&fallback_initialize,
AgentProtocol::V1,
)?;
if v1_initialize == v2_initialize_as_v1 {
let fallback_response = initialize_response.with_id(
initialize_request_id(&fallback_initialize)
.expect("validated initialize request has an id"),
);
fallback_client.send(fallback_response)?;
return pipe_protocol_peers_until_done(fallback_client, agent_connection)
.await;
}
drop((client, agent_connection, initialize_response));
return connect_client_protocol(ClientProtocol::V1, v1.create(), agent()).await;
}
client.send(initialize_response.into_message())?;
pipe_protocol_peers_until_done(client, agent_connection).await
}
}
}
}
#[cfg(feature = "unstable_protocol_v2")]
struct DynConnectToFactory<R: Role> {
inner: Box<dyn FnMut() -> DynConnectTo<R> + Send>,
}
#[cfg(feature = "unstable_protocol_v2")]
impl<R: Role> DynConnectToFactory<R> {
fn new<C>(mut factory: impl FnMut() -> C + Send + 'static) -> Self
where
C: ConnectTo<R>,
{
Self {
inner: Box::new(move || DynConnectTo::new(factory())),
}
}
fn create(&mut self) -> DynConnectTo<R> {
(self.inner)()
}
}
#[cfg(feature = "unstable_protocol_v2")]
impl<R: Role> Debug for DynConnectToFactory<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynConnectToFactory")
.finish_non_exhaustive()
}
}
impl HasPeer<Client> for Client {
fn remote_style(&self, _peer: Client) -> RemoteStyle {
RemoteStyle::Counterpart
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Agent;
impl Role for Agent {
type Counterpart = Client;
fn builder(self) -> Builder<Self> {
Builder::new(self).v1_agent()
}
fn role_id(&self) -> RoleId {
RoleId::from_singleton(self)
}
fn counterpart(&self) -> Self::Counterpart {
Client
}
async fn default_handle_dispatch_from(
&self,
message: Dispatch,
connection: ConnectionTo<Agent>,
) -> Result<Handled<Dispatch>, crate::Error> {
MatchDispatchFrom::new(message, &connection)
.if_message_from(Agent, async |message: Dispatch| {
let retry = message.has_session_id();
Ok(Handled::No { message, retry })
})
.await
.done()
}
}
impl Agent {
pub fn builder(self) -> Builder<Agent, NullHandler, NullRun> {
<Self as Role>::builder(self)
}
#[cfg(feature = "unstable_protocol_v2")]
pub fn v2(self) -> Builder<Agent, NullHandler, NullRun> {
self.builder().v2_agent()
}
#[cfg(feature = "unstable_protocol_v2")]
#[must_use]
pub fn protocol_router(self) -> AgentProtocolRouter {
AgentProtocolRouter::new()
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Default)]
pub struct AgentProtocolRouter {
v1: Option<DynConnectTo<Client>>,
v2: Option<DynConnectTo<Client>>,
}
#[cfg(feature = "unstable_protocol_v2")]
impl AgentProtocolRouter {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_v1(mut self, agent: impl ConnectTo<Client>) -> Self {
self.v1 = Some(DynConnectTo::new(agent));
self
}
#[must_use]
pub fn with_v2(mut self, agent: impl ConnectTo<Client>) -> Self {
self.v2 = Some(DynConnectTo::new(agent));
self
}
}
#[cfg(feature = "unstable_protocol_v2")]
impl ConnectTo<Client> for AgentProtocolRouter {
async fn connect_to(self, client: impl ConnectTo<Agent>) -> Result<(), crate::Error> {
let client = RunningProtocolPeer::new(client);
let Some((mut first_message, client)) = client.next_message().await? else {
return Ok(());
};
let supported = SupportedAgentProtocols {
v1: self.v1.is_some(),
v2: self.v2.is_some(),
};
let selected = match select_agent_protocol(&mut first_message, supported) {
Ok(selected) => selected,
Err(error) => {
return reject_initialize(client, &first_message, error).await;
}
};
let Some(agent) = selected.take_agent(self) else {
let error = selected.unsupported_error(supported);
return reject_initialize(client, &first_message, error).await;
};
let agent = RunningProtocolPeer::new(agent);
agent.send(first_message)?;
pipe_protocol_peers_until_closed(client, agent).await
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AgentProtocol {
V1,
V2,
}
#[cfg(feature = "unstable_protocol_v2")]
impl AgentProtocol {
fn take_agent(self, agent: AgentProtocolRouter) -> Option<DynConnectTo<Client>> {
match self {
Self::V1 => agent.v1,
Self::V2 => agent.v2,
}
}
fn name(self) -> &'static str {
match self {
Self::V1 => "1",
Self::V2 => "2",
}
}
fn unsupported_error(self, supported: SupportedAgentProtocols) -> crate::Error {
crate::Error::invalid_request().data(format!(
"ACP protocol version {} is not configured; this endpoint supports {}",
self.name(),
supported.description()
))
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SupportedAgentProtocols {
v1: bool,
v2: bool,
}
#[cfg(feature = "unstable_protocol_v2")]
impl SupportedAgentProtocols {
fn highest_compatible(self, requested: ProtocolVersion) -> Option<AgentProtocol> {
if self.v2 && requested >= ProtocolVersion::V2 {
return Some(AgentProtocol::V2);
}
if self.v1 && requested >= ProtocolVersion::V1 {
return Some(AgentProtocol::V1);
}
None
}
fn description(self) -> String {
match (self.v1, self.v2) {
(true, true) => "ACP protocol versions 1 and 2".into(),
(true, false) => "ACP protocol version 1".into(),
(false, true) => "ACP protocol version 2".into(),
(false, false) => "no ACP protocol versions".into(),
}
}
}
#[cfg(feature = "unstable_protocol_v2")]
fn select_agent_protocol(
message: &mut RawJsonRpcMessage,
supported: SupportedAgentProtocols,
) -> Result<AgentProtocol, crate::Error> {
let RawJsonRpcMessage::Request(request) = message else {
return Err(
crate::Error::invalid_request().data("first ACP message must be an initialize request")
);
};
if request.method.as_ref() != "initialize" {
return Err(crate::Error::invalid_request().data("first ACP request must be initialize"));
}
let Some(RawJsonRpcParams::Object(params)) = &mut request.params else {
return Err(invalid_initialize_protocol_version());
};
let Some(protocol_version) = params.get("protocolVersion") else {
return Err(invalid_initialize_protocol_version());
};
let requested = serde_json::from_value::<ProtocolVersion>(protocol_version.clone())
.map_err(|_| invalid_initialize_protocol_version())?;
let selected = highest_compatible_agent_protocol(requested, supported)?;
rewrite_initialize_params(params, requested, selected)?;
Ok(selected)
}
#[cfg(feature = "unstable_protocol_v2")]
fn normalize_initialize_params_for_agent_protocol(
message: &RawJsonRpcMessage,
selected: AgentProtocol,
) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
let RawJsonRpcMessage::Request(request) = message else {
return Err(
crate::Error::invalid_request().data("first ACP message must be an initialize request")
);
};
if request.method.as_ref() != "initialize" {
return Err(crate::Error::invalid_request().data("first ACP request must be initialize"));
}
let Some(RawJsonRpcParams::Object(params)) = &request.params else {
return Err(invalid_initialize_protocol_version());
};
let Some(protocol_version) = params.get("protocolVersion") else {
return Err(invalid_initialize_protocol_version());
};
let requested = serde_json::from_value::<ProtocolVersion>(protocol_version.clone())
.map_err(|_| invalid_initialize_protocol_version())?;
let mut params = params.clone();
rewrite_initialize_params(&mut params, requested, selected)?;
Ok(params)
}
#[cfg(feature = "unstable_protocol_v2")]
fn rewrite_initialize_params(
params: &mut serde_json::Map<String, serde_json::Value>,
requested: ProtocolVersion,
selected: AgentProtocol,
) -> Result<(), crate::Error> {
match selected {
AgentProtocol::V1 => {
let mut initialize = if requested >= ProtocolVersion::V2 {
v2::conversion::v2_to_v1(parse_initialize_params::<v2::InitializeRequest>(params)?)
.map_err(invalid_initialize_params)?
} else {
parse_initialize_params::<InitializeRequest>(params)?
};
initialize.protocol_version = ProtocolVersion::V1;
replace_initialize_params(params, initialize)
}
AgentProtocol::V2 => {
let mut initialize = if requested >= ProtocolVersion::V2 {
parse_initialize_params::<v2::InitializeRequest>(params)?
} else {
v2::conversion::v1_to_v2(parse_initialize_params::<InitializeRequest>(params)?)
.map_err(invalid_initialize_params)?
};
initialize.protocol_version = ProtocolVersion::V2;
replace_initialize_params(params, initialize)
}
}
}
#[cfg(feature = "unstable_protocol_v2")]
fn parse_initialize_params<T: DeserializeOwned>(
params: &serde_json::Map<String, serde_json::Value>,
) -> Result<T, crate::Error> {
serde_json::from_value(serde_json::Value::Object(params.clone()))
.map_err(invalid_initialize_params)
}
#[cfg(feature = "unstable_protocol_v2")]
fn replace_initialize_params(
params: &mut serde_json::Map<String, serde_json::Value>,
initialize: impl Serialize,
) -> Result<(), crate::Error> {
let value = serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?;
let serde_json::Value::Object(object) = value else {
return Err(crate::util::internal_error(
"initialize params did not serialize to an object",
));
};
*params = object;
Ok(())
}
#[cfg(feature = "unstable_protocol_v2")]
fn highest_compatible_agent_protocol(
requested: ProtocolVersion,
supported: SupportedAgentProtocols,
) -> Result<AgentProtocol, crate::Error> {
supported.highest_compatible(requested).ok_or_else(|| {
crate::Error::invalid_request().data(format!(
"unsupported ACP protocol version {requested}; this endpoint supports {}",
supported.description()
))
})
}
#[cfg(feature = "unstable_protocol_v2")]
fn invalid_initialize_protocol_version() -> crate::Error {
crate::Error::invalid_params()
.data("initialize.protocolVersion must be a valid ACP protocol version")
}
#[cfg(feature = "unstable_protocol_v2")]
fn invalid_initialize_params(error: impl ToString) -> crate::Error {
crate::Error::invalid_params().data(format!("invalid initialize params: {}", error.to_string()))
}
#[cfg(feature = "unstable_protocol_v2")]
fn send_initialize_error(
tx: &futures::channel::mpsc::UnboundedSender<Result<RawJsonRpcMessage, crate::Error>>,
message: &RawJsonRpcMessage,
error: crate::Error,
) -> Result<(), crate::Error> {
let id = match message {
RawJsonRpcMessage::Request(request) => request.id.clone(),
RawJsonRpcMessage::Notification(_) | RawJsonRpcMessage::Response(_) => RequestId::Null,
};
tx.unbounded_send(Ok(RawJsonRpcMessage::response(id, Err(error))))
.map_err(crate::util::internal_error)
}
#[cfg(feature = "unstable_protocol_v2")]
async fn reject_initialize(
client: RunningProtocolPeer,
message: &RawJsonRpcMessage,
error: crate::Error,
) -> Result<(), crate::Error> {
let RunningProtocolPeer { mut rx, tx, future } = client;
send_initialize_error(&tx, message, error)?;
drop(tx);
let drain_incoming = async move {
while let Some(message) = rx.next().await {
message?;
}
Ok(())
};
let ((), ()) = futures::try_join!(future, drain_incoming)?;
Ok(())
}
#[cfg(feature = "unstable_protocol_v2")]
struct RunningProtocolPeer {
rx: futures::channel::mpsc::UnboundedReceiver<Result<RawJsonRpcMessage, crate::Error>>,
tx: futures::channel::mpsc::UnboundedSender<Result<RawJsonRpcMessage, crate::Error>>,
future: crate::BoxFuture<'static, Result<(), crate::Error>>,
}
#[cfg(feature = "unstable_protocol_v2")]
impl RunningProtocolPeer {
fn new<R: Role>(component: impl ConnectTo<R>) -> Self {
let (Channel { rx, tx }, future) = component.into_channel_and_future();
Self { rx, tx, future }
}
async fn next_message(self) -> Result<Option<(RawJsonRpcMessage, Self)>, crate::Error> {
let Self { mut rx, tx, future } = self;
match future::select(Box::pin(rx.next()), future).await {
future::Either::Left((Some(message), future)) => {
Ok(Some((message?, Self { rx, tx, future })))
}
future::Either::Left((None, future)) => {
future.await?;
Ok(None)
}
future::Either::Right((result, next_message)) => {
result?;
drop(next_message);
let Some(message) = rx.next().await else {
return Ok(None);
};
Ok(Some((
message?,
Self {
rx,
tx,
future: Box::pin(future::ready(Ok(()))),
},
)))
}
}
}
fn send(&self, message: RawJsonRpcMessage) -> Result<(), crate::Error> {
self.tx
.unbounded_send(Ok(message))
.map_err(crate::util::internal_error)
}
}
#[cfg(feature = "unstable_protocol_v2")]
async fn pipe_protocol_peers_until_closed(
left: RunningProtocolPeer,
right: RunningProtocolPeer,
) -> Result<(), crate::Error> {
let ((), (), (), ()) = futures::try_join!(
left.future,
right.future,
Channel {
rx: left.rx,
tx: right.tx,
}
.copy(),
Channel {
rx: right.rx,
tx: left.tx,
}
.copy(),
)?;
Ok(())
}
#[cfg(feature = "unstable_protocol_v2")]
async fn pipe_protocol_peers_until_done(
left: RunningProtocolPeer,
right: RunningProtocolPeer,
) -> Result<(), crate::Error> {
let bridge = Box::pin(async move {
let ((), ()) = futures::try_join!(
Channel {
rx: left.rx,
tx: right.tx,
}
.copy(),
Channel {
rx: right.rx,
tx: left.tx,
}
.copy(),
)?;
Ok(())
});
match future::select(left.future, future::select(right.future, bridge)).await {
future::Either::Left((result, _))
| future::Either::Right((
future::Either::Left((result, _)) | future::Either::Right((result, _)),
_,
)) => result,
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug)]
struct InitializeResponse {
id: RequestId,
result: Result<serde_json::Value, crate::Error>,
}
#[cfg(feature = "unstable_protocol_v2")]
impl InitializeResponse {
fn from_message(message: RawJsonRpcMessage) -> Result<Self, crate::Error> {
match message {
RawJsonRpcMessage::Response(RpcResponse::Result { id, result }) => Ok(Self {
id,
result: Ok(result),
}),
RawJsonRpcMessage::Response(RpcResponse::Error { id, error }) => Ok(Self {
id,
result: Err(error),
}),
message => Err(crate::Error::invalid_request().data(format!(
"first ACP response must be an initialize response, got {message:?}",
))),
}
}
fn into_message(self) -> RawJsonRpcMessage {
RawJsonRpcMessage::response(self.id, self.result)
}
fn with_id(self, id: RequestId) -> RawJsonRpcMessage {
RawJsonRpcMessage::response(id, self.result)
}
fn protocol_version(&self) -> Option<ProtocolVersion> {
serde_json::from_value(self.result.as_ref().ok()?.get("protocolVersion")?.clone()).ok()
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ClientProtocol {
V1,
V2,
}
#[cfg(feature = "unstable_protocol_v2")]
impl ClientProtocol {
fn name(self) -> &'static str {
match self {
Self::V1 => "1",
Self::V2 => "2",
}
}
}
#[cfg(feature = "unstable_protocol_v2")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SupportedClientProtocols {
v1: bool,
v2: bool,
}
#[cfg(feature = "unstable_protocol_v2")]
impl SupportedClientProtocols {
fn highest_configured(self) -> Option<ClientProtocol> {
if self.v2 {
return Some(ClientProtocol::V2);
}
if self.v1 {
return Some(ClientProtocol::V1);
}
None
}
}
#[cfg(feature = "unstable_protocol_v2")]
async fn start_client_protocol(
protocol: ClientProtocol,
client: DynConnectTo<Agent>,
) -> Result<(RunningProtocolPeer, RawJsonRpcMessage), crate::Error> {
let client = RunningProtocolPeer::new(client);
let Some((initialize, client)) = client.next_message().await? else {
return Err(crate::Error::invalid_request().data(format!(
"ACP protocol version {} client implementation ended before initialize",
protocol.name()
)));
};
ensure_client_initialize_request(protocol, &initialize)?;
Ok((client, initialize))
}
#[cfg(feature = "unstable_protocol_v2")]
async fn send_initialize_and_receive(
client: RunningProtocolPeer,
agent: RunningProtocolPeer,
initialize: RawJsonRpcMessage,
) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
agent.send(initialize)?;
let Some((response, agent)) = agent.next_message().await? else {
return Err(crate::Error::internal_error().data("agent closed before initialize response"));
};
let response = InitializeResponse::from_message(response)?;
Ok((client, agent, response))
}
#[cfg(feature = "unstable_protocol_v2")]
async fn initialize_client_protocol(
protocol: ClientProtocol,
client: DynConnectTo<Agent>,
agent: impl ConnectTo<Client>,
) -> Result<(RunningProtocolPeer, RunningProtocolPeer, InitializeResponse), crate::Error> {
let agent = RunningProtocolPeer::new(agent);
let (client, initialize) = start_client_protocol(protocol, client).await?;
send_initialize_and_receive(client, agent, initialize).await
}
#[cfg(feature = "unstable_protocol_v2")]
async fn connect_client_protocol(
protocol: ClientProtocol,
client: DynConnectTo<Agent>,
agent: impl ConnectTo<Client>,
) -> Result<(), crate::Error> {
let (client, agent, initialize_response) =
initialize_client_protocol(protocol, client, agent).await?;
client.send(initialize_response.into_message())?;
pipe_protocol_peers_until_done(client, agent).await
}
#[cfg(feature = "unstable_protocol_v2")]
fn ensure_client_initialize_request(
protocol: ClientProtocol,
message: &RawJsonRpcMessage,
) -> Result<(), crate::Error> {
let RawJsonRpcMessage::Request(request) = message else {
return Err(crate::Error::invalid_request().data(format!(
"ACP protocol version {} client implementation must send initialize first",
protocol.name()
)));
};
if request.method.as_ref() != "initialize" {
return Err(crate::Error::invalid_request().data(format!(
"ACP protocol version {} client implementation must send initialize first",
protocol.name()
)));
}
Ok(())
}
#[cfg(feature = "unstable_protocol_v2")]
fn initialize_request_id(message: &RawJsonRpcMessage) -> Option<RequestId> {
let RawJsonRpcMessage::Request(request) = message else {
return None;
};
Some(request.id.clone())
}
#[cfg(feature = "unstable_protocol_v2")]
fn initialize_response_negotiated_v1(response: &InitializeResponse) -> bool {
response.protocol_version() == Some(ProtocolVersion::V1)
}
impl HasPeer<Agent> for Agent {
fn remote_style(&self, _peer: Agent) -> RemoteStyle {
RemoteStyle::Counterpart
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Proxy;
impl Role for Proxy {
type Counterpart = Conductor;
async fn default_handle_dispatch_from(
&self,
message: crate::Dispatch,
_connection: crate::ConnectionTo<Self>,
) -> Result<crate::Handled<crate::Dispatch>, crate::Error> {
Ok(Handled::No {
message,
retry: false,
})
}
fn role_id(&self) -> RoleId {
RoleId::from_singleton(self)
}
fn counterpart(&self) -> Self::Counterpart {
Conductor
}
}
impl Proxy {
pub fn builder(self) -> Builder<Proxy, NullHandler, NullRun> {
Builder::new(self)
}
}
impl HasPeer<Proxy> for Proxy {
fn remote_style(&self, _peer: Proxy) -> RemoteStyle {
RemoteStyle::Counterpart
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Conductor;
impl Role for Conductor {
type Counterpart = Proxy;
fn role_id(&self) -> RoleId {
RoleId::from_singleton(self)
}
fn counterpart(&self) -> Self::Counterpart {
Proxy
}
async fn default_handle_dispatch_from(
&self,
message: Dispatch,
cx: ConnectionTo<Conductor>,
) -> Result<Handled<Dispatch>, crate::Error> {
MatchDispatchFrom::new(message, &cx)
.if_request_from(Client, async |_req: InitializeRequest, responder| {
responder.respond_with_error(crate::Error::invalid_request().data(format!(
"proxies must be initialized with `{METHOD_INITIALIZE_PROXY}`"
)))
})
.await
.if_request_from(
Client,
async |request: InitializeProxyRequest, responder| {
let InitializeProxyRequest { initialize } = request;
cx.send_request_to(Agent, initialize)
.forward_response_to(responder)
},
)
.await
.if_request_from(Client, async |request: NewSessionRequest, responder| {
let sent = cx.send_request_to(Agent, request);
let sent = sent.forward_cancellation_from(responder.cancellation());
sent.on_receiving_result({
let cx = cx.clone();
async move |result| {
if let Ok(NewSessionResponse { session_id, .. }) = &result {
cx.add_dynamic_handler(ProxySessionMessages::new(session_id.clone()))?
.run_indefinitely();
}
responder.respond_with_result(result)
}
})
})
.await
.if_message_from(Client, async |message: Dispatch| {
cx.send_proxied_message_to(Agent, message)
})
.await
.if_message_from(Agent, async |message: Dispatch| {
cx.send_proxied_message_to(Client, message)
})
.await
.done()
}
}
impl Conductor {
pub fn builder(self) -> Builder<Conductor, NullHandler, NullRun> {
Builder::new(self)
}
}
impl HasPeer<Client> for Conductor {
fn remote_style(&self, _peer: Client) -> RemoteStyle {
RemoteStyle::Predecessor
}
}
impl HasPeer<Agent> for Conductor {
fn remote_style(&self, _peer: Agent) -> RemoteStyle {
RemoteStyle::Successor
}
}
pub(crate) struct ProxySessionMessages {
session_id: SessionId,
}
impl ProxySessionMessages {
pub fn new(session_id: SessionId) -> Self {
Self { session_id }
}
}
impl<Counterpart: Role> HandleDispatchFrom<Counterpart> for ProxySessionMessages
where
Counterpart: HasPeer<Agent> + HasPeer<Client>,
{
async fn handle_dispatch_from(
&mut self,
message: Dispatch,
connection: ConnectionTo<Counterpart>,
) -> Result<Handled<Dispatch>, crate::Error> {
MatchDispatchFrom::new(message, &connection)
.if_message_from(Agent, async |message| {
if let Some(session_id) = message.get_session_id()?
&& session_id == self.session_id
{
connection.send_proxied_message_to(Client, message)?;
return Ok(Handled::Yes);
}
Ok(Handled::No {
message,
retry: false,
})
})
.await
.done()
}
fn describe_chain(&self) -> impl std::fmt::Debug {
format!("ProxySessionMessages({})", self.session_id)
}
}