From 93d84254f724010d161faf5f48b41a4b391fc6d1 Mon Sep 17 00:00:00 2001 From: Miraculous Owonubi Date: Wed, 27 Nov 2024 23:25:45 +0100 Subject: [PATCH] feat: iterate on transport generalization for recursive declaration (#991) --- crates/context/config/src/client.rs | 44 ++++- crates/context/config/src/client/env.rs | 9 +- .../config/src/client/env/config/mutate.rs | 2 +- .../config/src/client/env/config/query.rs | 14 +- .../config/src/client/env/proxy/mutate.rs | 2 +- .../config/src/client/env/proxy/query.rs | 10 +- .../config/src/client/protocol/near.rs | 28 +-- .../config/src/client/protocol/starknet.rs | 28 +-- crates/context/config/src/client/relayer.rs | 97 +++++++--- crates/context/config/src/client/transport.rs | 182 +++++++++--------- crates/context/config/src/client/utils.rs | 39 ++++ crates/merod/src/cli/relay.rs | 52 +++-- 12 files changed, 299 insertions(+), 208 deletions(-) create mode 100644 crates/context/config/src/client/utils.rs diff --git a/crates/context/config/src/client.rs b/crates/context/config/src/client.rs index 7b5d80753..859276e6f 100644 --- a/crates/context/config/src/client.rs +++ b/crates/context/config/src/client.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::fmt::Debug; +use std::ops::Deref; use either::Either; use env::Method; @@ -10,10 +11,11 @@ pub mod env; pub mod protocol; pub mod relayer; pub mod transport; +pub mod utils; use config::{ClientConfig, ClientSelectedSigner, Credentials}; use protocol::{near, starknet, Protocol}; -use transport::{Both, Transport, TransportRequest}; +use transport::{Both, Transport, TransportArguments, TransportRequest, UnsupportedProtocol}; pub type AnyTransport = Either< relayer::RelayerTransport, @@ -110,17 +112,43 @@ pub enum ClientError { Transport(T::Error), #[error("codec error: {0}")] Codec(#[from] eyre::Report), - #[error("unsupported protocol: {0}")] - UnsupportedProtocol(String), + #[error( + "unsupported protocol: `{found}`, expected {}", + utils::humanize_iter(expected.deref()) + )] + UnsupportedProtocol { + found: String, + expected: Cow<'static, [Cow<'static, str>]>, + }, +} + +impl<'a, T: Transport> From> for ClientError { + fn from(err: UnsupportedProtocol<'a>) -> Self { + Self::UnsupportedProtocol { + found: err.args.protocol.into_owned(), + expected: err.expected, + } + } } impl Client { async fn send( &self, + protocol: Cow<'_, str>, request: TransportRequest<'_>, payload: Vec, - ) -> Result, T::Error> { - self.transport.send(request, payload).await + ) -> Result, ClientError> { + let res: Result<_, _> = self + .transport + .try_send(TransportArguments { + protocol, + request, + payload, + }) + .await + .into(); + + res?.map_err(ClientError::Transport) } pub fn query<'a, E: Environment<'a, T>>( @@ -182,7 +210,6 @@ impl<'a, T: Transport> CallClient<'a, T> { }; let request = TransportRequest { - protocol: Cow::Borrowed(&self.protocol), network_id: Cow::Borrowed(&self.network_id), contract_id: Cow::Borrowed(&self.contract_id), operation, @@ -190,9 +217,8 @@ impl<'a, T: Transport> CallClient<'a, T> { let response = self .client - .send(request, payload) - .await - .map_err(ClientError::Transport)?; + .send(self.protocol.as_ref().into(), request, payload) + .await?; M::decode(response).map_err(ClientError::Codec) } diff --git a/crates/context/config/src/client/env.rs b/crates/context/config/src/client/env.rs index 2b1734197..f3657ea15 100644 --- a/crates/context/config/src/client/env.rs +++ b/crates/context/config/src/client/env.rs @@ -23,7 +23,7 @@ mod utils { use crate::client::{CallClient, ClientError, Operation}; // todo! when crates are broken up, appropriately locate this - pub(super) async fn send_near_or_starknet( + pub(super) async fn send( client: &CallClient<'_, T>, params: Operation, ) -> Result> @@ -34,9 +34,10 @@ mod utils { match &*client.protocol { Near::PROTOCOL => client.send::(params).await, Starknet::PROTOCOL => client.send::(params).await, - unsupported_protocol => Err(ClientError::UnsupportedProtocol( - unsupported_protocol.to_owned(), - )), + unsupported_protocol => Err(ClientError::UnsupportedProtocol { + found: unsupported_protocol.to_owned(), + expected: vec![Near::PROTOCOL.into(), Starknet::PROTOCOL.into()].into(), + }), } } } diff --git a/crates/context/config/src/client/env/config/mutate.rs b/crates/context/config/src/client/env/config/mutate.rs index d5c851bf3..1c9ad5880 100644 --- a/crates/context/config/src/client/env/config/mutate.rs +++ b/crates/context/config/src/client/env/config/mutate.rs @@ -136,6 +136,6 @@ impl<'a, T: Transport + Debug> ContextConfigMutateRequest<'a, T> { kind: self.kind, }; - utils::send_near_or_starknet(&self.client, Operation::Write(request)).await + utils::send(&self.client, Operation::Write(request)).await } } diff --git a/crates/context/config/src/client/env/config/query.rs b/crates/context/config/src/client/env/config/query.rs index 990109b94..dc9d39f28 100644 --- a/crates/context/config/src/client/env/config/query.rs +++ b/crates/context/config/src/client/env/config/query.rs @@ -28,7 +28,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { context_id: Repr::new(context_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn application_revision( @@ -39,7 +39,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { context_id: Repr::new(context_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn members( @@ -54,7 +54,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { length, }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn has_member( @@ -67,7 +67,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { identity: Repr::new(identity), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn members_revision( @@ -78,7 +78,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { context_id: Repr::new(context_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn privileges( @@ -88,7 +88,7 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { ) -> Result>, ClientError> { let params = privileges::PrivilegesRequest::new(context_id, identities); - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn get_proxy_contract( @@ -99,6 +99,6 @@ impl<'a, T: Transport> ContextConfigQuery<'a, T> { context_id: Repr::new(context_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } } diff --git a/crates/context/config/src/client/env/proxy/mutate.rs b/crates/context/config/src/client/env/proxy/mutate.rs index 14afae533..70620d404 100644 --- a/crates/context/config/src/client/env/proxy/mutate.rs +++ b/crates/context/config/src/client/env/proxy/mutate.rs @@ -128,6 +128,6 @@ impl<'a, T: Transport> ContextProxyMutateRequest<'a, T> { raw_request: self.raw_request, }; - utils::send_near_or_starknet(&self.client, Operation::Write(request)).await + utils::send(&self.client, Operation::Write(request)).await } } diff --git a/crates/context/config/src/client/env/proxy/query.rs b/crates/context/config/src/client/env/proxy/query.rs index da84c2cf9..0a48d43e9 100644 --- a/crates/context/config/src/client/env/proxy/query.rs +++ b/crates/context/config/src/client/env/proxy/query.rs @@ -32,7 +32,7 @@ impl<'a, T: Transport> ContextProxyQuery<'a, T> { offset, length: limit, }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn proposal( @@ -43,13 +43,13 @@ impl<'a, T: Transport> ContextProxyQuery<'a, T> { proposal_id: Repr::new(proposal_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn get_number_of_active_proposals(&self) -> Result> { let params = ActiveProposalRequest; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn get_number_of_proposal_approvals( @@ -60,7 +60,7 @@ impl<'a, T: Transport> ContextProxyQuery<'a, T> { proposal_id: Repr::new(proposal_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } pub async fn get_proposal_approvers( @@ -71,6 +71,6 @@ impl<'a, T: Transport> ContextProxyQuery<'a, T> { proposal_id: Repr::new(proposal_id), }; - utils::send_near_or_starknet(&self.client, Operation::Read(params)).await + utils::send(&self.client, Operation::Read(params)).await } } diff --git a/crates/context/config/src/client/protocol/near.rs b/crates/context/config/src/client/protocol/near.rs index f90211c01..56822c875 100644 --- a/crates/context/config/src/client/protocol/near.rs +++ b/crates/context/config/src/client/protocol/near.rs @@ -29,7 +29,7 @@ use url::Url; use super::Protocol; use crate::client::transport::{ - AssociatedTransport, Operation, Transport, TransportLike, TransportRequest, + AssociatedTransport, Operation, ProtocolTransport, TransportRequest, }; #[derive(Copy, Clone, Debug)] @@ -43,22 +43,6 @@ impl AssociatedTransport for NearTransport<'_> { type Protocol = Near; } -impl TransportLike for NearTransport<'_> { - type Error = NearError; - - async fn try_send( - &self, - request: TransportRequest<'_>, - payload: &Vec, - ) -> Option, Self::Error>> { - if request.protocol == "near" { - Some(self.send(request, payload.to_vec()).await) - } else { - None - } - } -} - #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(try_from = "serde_creds::Credentials")] pub struct Credentials { @@ -172,8 +156,6 @@ impl<'a> NearTransport<'a> { #[derive(Debug, Error)] #[non_exhaustive] pub enum NearError { - #[error("unsupported protocol `{0}`")] - UnsupportedProtocol(String), #[error("unknown network `{0}`")] UnknownNetwork(String), #[error("invalid response from RPC while {operation}")] @@ -206,7 +188,7 @@ pub enum ErrorOperation { FetchAccount, } -impl Transport for NearTransport<'_> { +impl ProtocolTransport for NearTransport<'_> { type Error = NearError; async fn send( @@ -214,12 +196,6 @@ impl Transport for NearTransport<'_> { request: TransportRequest<'_>, payload: Vec, ) -> Result, Self::Error> { - if request.protocol != Near::PROTOCOL { - return Err(NearError::UnsupportedProtocol( - request.protocol.into_owned(), - )); - } - let Some(network) = self.networks.get(&request.network_id) else { return Err(NearError::UnknownNetwork(request.network_id.into_owned())); }; diff --git a/crates/context/config/src/client/protocol/starknet.rs b/crates/context/config/src/client/protocol/starknet.rs index 5dacde3c9..8dafeaf2b 100644 --- a/crates/context/config/src/client/protocol/starknet.rs +++ b/crates/context/config/src/client/protocol/starknet.rs @@ -19,7 +19,7 @@ use thiserror::Error; use super::Protocol; use crate::client::env::proxy::starknet::StarknetProposalWithApprovals; use crate::client::transport::{ - AssociatedTransport, Operation, Transport, TransportLike, TransportRequest, + AssociatedTransport, Operation, ProtocolTransport, TransportRequest, }; #[derive(Copy, Clone, Debug)] @@ -33,22 +33,6 @@ impl AssociatedTransport for StarknetTransport<'_> { type Protocol = Starknet; } -impl TransportLike for StarknetTransport<'_> { - type Error = StarknetError; - - async fn try_send( - &self, - request: TransportRequest<'_>, - payload: &Vec, - ) -> Option, Self::Error>> { - if request.protocol == "near" { - Some(self.send(request, payload.to_vec()).await) - } else { - None - } - } -} - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[serde(try_from = "serde_creds::Credentials")] pub struct Credentials { @@ -153,8 +137,6 @@ impl<'a> StarknetTransport<'a> { #[derive(Debug, Error)] #[non_exhaustive] pub enum StarknetError { - #[error("unsupported protocol: {0}")] - UnsupportedProtocol(String), #[error("unknown network `{0}`")] UnknownNetwork(String), #[error("invalid response from RPC while {operation}")] @@ -191,7 +173,7 @@ pub enum ErrorOperation { FetchNonce, } -impl Transport for StarknetTransport<'_> { +impl ProtocolTransport for StarknetTransport<'_> { type Error = StarknetError; async fn send( @@ -199,12 +181,6 @@ impl Transport for StarknetTransport<'_> { request: TransportRequest<'_>, payload: Vec, ) -> Result, Self::Error> { - if request.protocol != Starknet::PROTOCOL { - return Err(StarknetError::UnsupportedProtocol( - request.protocol.into_owned(), - )); - } - let Some(network) = self.networks.get(&request.network_id) else { return Err(StarknetError::UnknownNetwork( request.network_id.into_owned(), diff --git a/crates/context/config/src/client/relayer.rs b/crates/context/config/src/client/relayer.rs index 605c05a37..7e01411ab 100644 --- a/crates/context/config/src/client/relayer.rs +++ b/crates/context/config/src/client/relayer.rs @@ -1,10 +1,15 @@ +#![allow(clippy::multiple_inherent_impl, reason = "it's fine")] + use std::borrow::Cow; +use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use thiserror::Error; use url::Url; -use super::transport::{Operation, Transport, TransportRequest}; +use super::transport::{ + Operation, Transport, TransportArguments, TransportRequest, UnsupportedProtocol, +}; #[derive(Debug)] #[non_exhaustive] @@ -48,40 +53,84 @@ pub enum RelayerError { "relayer response ({status}): {}", body.is_empty().then_some("").unwrap_or(body) )] - Response { - status: reqwest::StatusCode, - body: String, - }, + Response { status: StatusCode, body: String }, } -impl Transport for RelayerTransport { - type Error = RelayerError; +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "kind", content = "data")] +pub enum ServerError { + UnsupportedProtocol { + found: Cow<'static, str>, + expected: Cow<'static, [Cow<'static, str>]>, + }, +} - async fn send( +impl RelayerTransport { + async fn send<'a>( &self, - request: TransportRequest<'_>, - payload: Vec, - ) -> Result, Self::Error> { + args: TransportArguments<'a>, + ) -> Result, UnsupportedProtocol<'a>>, RelayerError> { + let request = RelayRequest { + protocol: args.protocol, + network_id: args.request.network_id, + contract_id: args.request.contract_id, + operation: args.request.operation, + payload: args.payload, + }; + let response = self .client .post(self.url.clone()) - .json(&RelayRequest { - protocol: request.protocol, - network_id: request.network_id, - contract_id: request.contract_id, - operation: request.operation, - payload, - }) + .json(&request) .send() .await?; - if !response.status().is_success() { - return Err(RelayerError::Response { - status: response.status(), - body: response.text().await?, - }); + match response.status() { + status if status.is_success() => { + return response + .bytes() + .await + .map(|v| Ok(v.into())) + .map_err(|e| e.into()); + } + status if status == StatusCode::BAD_REQUEST => {} + status => { + return Err(RelayerError::Response { + status, + body: response.text().await?, + }) + } } - response.bytes().await.map(Into::into).map_err(Into::into) + let error = response.json::().await?; + + match error { + ServerError::UnsupportedProtocol { found: _, expected } => { + let args = TransportArguments { + protocol: request.protocol, + request: TransportRequest { + network_id: request.network_id, + contract_id: request.contract_id, + operation: request.operation, + }, + payload: request.payload, + }; + + Ok(Err(UnsupportedProtocol { args, expected })) + } + } + } +} + +impl Transport for RelayerTransport { + type Error = RelayerError; + + async fn try_send<'a>( + &self, + args: TransportArguments<'a>, + ) -> Result, Self::Error>, UnsupportedProtocol<'a>> { + self.send(args) + .await + .map_or_else(|e| Ok(Err(e)), |v| v.map(Ok)) } } diff --git a/crates/context/config/src/client/transport.rs b/crates/context/config/src/client/transport.rs index 8b90ca3bf..df9e12ac9 100644 --- a/crates/context/config/src/client/transport.rs +++ b/crates/context/config/src/client/transport.rs @@ -2,16 +2,15 @@ use core::error::Error; use std::borrow::Cow; use either::Either; -use serde::ser::StdError; use serde::{Deserialize, Serialize}; use thiserror::Error; use super::protocol::Protocol; -pub trait Transport { +pub trait ProtocolTransport { type Error: Error; - #[expect(async_fn_in_trait, reason = "Should be fine")] + #[expect(async_fn_in_trait, reason = "constraints are upheld for now")] async fn send( &self, request: TransportRequest<'_>, @@ -19,30 +18,41 @@ pub trait Transport { ) -> Result, Self::Error>; } -#[derive(Debug, Clone)] -#[non_exhaustive] +#[derive(Debug)] pub struct TransportRequest<'a> { - pub protocol: Cow<'a, str>, pub network_id: Cow<'a, str>, pub contract_id: Cow<'a, str>, pub operation: Operation<'a>, } -impl<'a> TransportRequest<'a> { - #[must_use] - pub const fn new( - protocol: Cow<'a, str>, - network_id: Cow<'a, str>, - contract_id: Cow<'a, str>, - operation: Operation<'a>, - ) -> Self { - Self { - protocol, - network_id, - contract_id, - operation, - } - } +#[derive(Debug, Serialize, Deserialize)] +#[expect(clippy::exhaustive_enums, reason = "Considered to be exhaustive")] +pub enum Operation<'a> { + Read { method: Cow<'a, str> }, + Write { method: Cow<'a, str> }, +} + +pub trait Transport { + type Error: Error; + + #[expect(async_fn_in_trait, reason = "constraints are upheld for now")] + async fn try_send<'a>( + &self, + args: TransportArguments<'a>, + ) -> Result, Self::Error>, UnsupportedProtocol<'a>>; +} + +#[derive(Debug)] +pub struct TransportArguments<'a> { + pub protocol: Cow<'a, str>, + pub request: TransportRequest<'a>, + pub payload: Vec, +} + +#[derive(Debug)] +pub struct UnsupportedProtocol<'a> { + pub args: TransportArguments<'a>, + pub expected: Cow<'static, [Cow<'static, str>]>, } #[derive(Debug, Error)] @@ -51,68 +61,76 @@ pub enum EitherError { Left(L), #[error(transparent)] Right(R), - #[error("unsupported protocol: {0}")] - UnsupportedProtocol(String), } -impl Transport for Either { +impl Transport for Either +where + L: Transport, + R: Transport, +{ type Error = EitherError; - async fn send( + async fn try_send<'a>( &self, - request: TransportRequest<'_>, - payload: Vec, - ) -> Result, Self::Error> { + args: TransportArguments<'a>, + ) -> Result, Self::Error>, UnsupportedProtocol<'a>> { match self { - Self::Left(left) => left.send(request, payload).await.map_err(EitherError::Left), - Self::Right(right) => right - .send(request, payload) - .await - .map_err(EitherError::Right), + Self::Left(left) => Ok(left.try_send(args).await?.map_err(EitherError::Left)), + Self::Right(right) => Ok(right.try_send(args).await?.map_err(EitherError::Right)), } } } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[expect(clippy::exhaustive_enums, reason = "Considered to be exhaustive")] -pub enum Operation<'a> { - Read { method: Cow<'a, str> }, - Write { method: Cow<'a, str> }, -} - -pub trait TransportLike { - type Error; - - async fn try_send( - &self, - request: TransportRequest<'_>, - payload: &Vec, - ) -> Option, Self::Error>>; +#[expect(clippy::exhaustive_structs, reason = "this is exhaustive")] +#[derive(Debug, Clone)] +pub struct Both { + pub left: L, + pub right: R, } -impl TransportLike for Both +impl Transport for Both where - L: TransportLike, - R: TransportLike, + L: Transport, + R: Transport, { type Error = EitherError; - async fn try_send( + async fn try_send<'a>( &self, - request: TransportRequest<'_>, - payload: &Vec, - ) -> Option, Self::Error>> { - if let Some(result) = self.left.try_send(request.clone(), payload).await { - return Some(result.map_err(EitherError::Left)); - } - if let Some(result) = self.right.try_send(request, payload).await { - return Some(result.map_err(EitherError::Right)); - } - None + args: TransportArguments<'a>, + ) -> Result, Self::Error>, UnsupportedProtocol<'a>> { + let left = self.left.try_send(args).await; + + let UnsupportedProtocol { + args, + expected: left_expected, + } = match left { + Ok(res) => return Ok(res.map_err(EitherError::Left)), + Err(err) => err, + }; + + let right = self.right.try_send(args).await; + + let UnsupportedProtocol { + args, + expected: right_expected, + } = match right { + Ok(res) => return Ok(res.map_err(EitherError::Right)), + Err(err) => err, + }; + + let mut expected = left_expected.into_owned(); + + expected.extend(right_expected.into_owned()); + + Err(UnsupportedProtocol { + args, + expected: expected.into(), + }) } } -pub trait AssociatedTransport: Transport { +pub trait AssociatedTransport: ProtocolTransport { type Protocol: Protocol; #[inline] @@ -122,32 +140,22 @@ pub trait AssociatedTransport: Transport { } } -#[expect(clippy::exhaustive_structs, reason = "this is exhaustive")] -#[derive(Debug, Clone)] -pub struct Both { - pub left: L, - pub right: R, -} - -impl Transport for Both -where - L: TransportLike, - ::Error: StdError, - R: TransportLike, - ::Error: StdError, //no idea why -{ - type Error = EitherError; +impl Transport for T { + type Error = T::Error; - async fn send( + async fn try_send<'a>( &self, - request: TransportRequest<'_>, - payload: Vec, - ) -> Result, Self::Error> { - match self.try_send(request.clone(), &payload).await { - Some(result) => result, - None => Err(EitherError::UnsupportedProtocol( - request.protocol.into_owned(), - )), + args: TransportArguments<'a>, + ) -> Result, Self::Error>, UnsupportedProtocol<'a>> { + let protocol = Self::protocol(); + + if args.protocol != protocol { + return Err(UnsupportedProtocol { + args, + expected: vec![protocol.into()].into(), + }); } + + Ok(self.send(args.request, args.payload).await) } } diff --git a/crates/context/config/src/client/utils.rs b/crates/context/config/src/client/utils.rs new file mode 100644 index 000000000..86608fbf4 --- /dev/null +++ b/crates/context/config/src/client/utils.rs @@ -0,0 +1,39 @@ +use core::fmt::{self, Write}; + +pub fn humanize_iter(iter: I) -> String +where + I: IntoIterator, +{ + let mut res = String::new(); + + let mut iter = iter.into_iter().peekable(); + + while let Some(item) = iter.next() { + if !res.is_empty() { + if iter.peek().is_some() { + res.push_str(", "); + } else { + res.push_str(" and "); + } + } + + write!(res, "`{}`", item).expect("infallible"); + } + + res +} + +#[test] +fn test_humanize_iter() { + assert_eq!( + humanize_iter(&["near", "starknet", "ethereum", "solana"]), + "`near`, `starknet`, `ethereum` and `solana`" + ); + assert_eq!( + humanize_iter(&["polkadot", "near", "icp"]), + "`polkadot`, `near` and `icp`" + ); + assert_eq!(humanize_iter(&["this", "that"]), "`this` and `that`"); + assert_eq!(humanize_iter(&["me"]), "`me`"); + assert_eq!(humanize_iter::<[u8; 0]>([]), ""); +} diff --git a/crates/merod/src/cli/relay.rs b/crates/merod/src/cli/relay.rs index f52fa8805..764417871 100644 --- a/crates/merod/src/cli/relay.rs +++ b/crates/merod/src/cli/relay.rs @@ -10,8 +10,10 @@ use axum::{Json, Router}; use calimero_config::ConfigFile; use calimero_context_config::client::config::Credentials; use calimero_context_config::client::protocol::{near, starknet}; -use calimero_context_config::client::relayer::RelayRequest; -use calimero_context_config::client::transport::{Both, Transport, TransportRequest}; +use calimero_context_config::client::relayer::{RelayRequest, ServerError}; +use calimero_context_config::client::transport::{ + Both, Transport, TransportArguments, TransportRequest, +}; use clap::{Parser, ValueEnum}; use eyre::{bail, Result as EyreResult}; use futures_util::FutureExt; @@ -119,21 +121,26 @@ impl RelayCommand { let handle = async move { while let Some((request, res_tx)) = rx.recv().await { - let payload = request.payload; - - let request = TransportRequest::new( - request.protocol, - request.network_id, - request.contract_id, - request.operation, - ); - - let _ignored = res_tx.send( - both_transport - .send(request, payload) - .await - .map_err(Into::into), - ); + let args = TransportArguments { + protocol: request.protocol, + request: TransportRequest { + network_id: request.network_id, + contract_id: request.contract_id, + operation: request.operation, + }, + payload: request.payload, + }; + + let res = both_transport + .try_send(args) + .await + .map(|res| res.map_err(Into::into)) + .map_err(|err| ServerError::UnsupportedProtocol { + found: err.args.protocol, + expected: err.expected, + }); + + let _ignored = res_tx.send(res); } }; @@ -153,7 +160,7 @@ impl RelayCommand { type AppState = mpsc::Sender; type RequestPayload = (RelayRequest<'static>, HandlerSender); -type HandlerSender = oneshot::Sender>>; +type HandlerSender = oneshot::Sender>, ServerError>>; async fn handler( State(req_tx): State, @@ -170,6 +177,15 @@ async fn handler( .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let res = match res { + Ok(res) => res, + Err(err) => { + debug!("failed to send request to handler: {:?}", err); + + return Ok((StatusCode::BAD_REQUEST, Json(err)).into_response()); + } + }; + match res { Ok(res) => Ok(res.into_response()), Err(err) => {