heddle_thread_api/
transport.rs1use std::future::Future;
4
5use api::{
6 framing,
7 heddle::api::common::{CallContext, CallFailure},
8 v2::MethodDescriptor,
9};
10use prost::Message;
11
12#[cfg(feature = "iroh")]
13mod iroh;
14#[cfg(feature = "iroh")]
15pub use iroh::{IrohTransport, Reader, Writer, accepted_stream};
16
17#[derive(Debug, thiserror::Error)]
18pub enum Error {
19 #[error("transport I/O: {0}")]
20 Io(String),
21 #[error("RPC made no progress before its timeout")]
22 Timeout,
23 #[error("invalid v2 transport: {0}")]
24 Protocol(&'static str),
25 #[error("RPC failed: {0:?}")]
26 Remote(RemoteFailure),
27 #[error(transparent)]
28 Framing(#[from] framing::FrameError),
29 #[error(transparent)]
30 Metadata(#[from] api::RequestMetadataError),
31 #[error(transparent)]
32 Decode(#[from] prost::DecodeError),
33}
34
35#[derive(Debug)]
39pub struct RemoteFailure {
40 pub code: i32,
41 pub message: String,
42 detail: Option<Vec<u8>>,
43}
44impl RemoteFailure {
45 pub fn detail(
46 &self,
47 ) -> Result<Option<api::heddle::api::common::ErrorDetail>, prost::DecodeError> {
48 self.detail
49 .as_deref()
50 .map(prost::Message::decode)
51 .transpose()
52 }
53}
54impl From<CallFailure> for RemoteFailure {
55 fn from(failure: CallFailure) -> Self {
56 Self {
57 code: failure.code,
58 message: failure.message,
59 detail: failure.error.map(|e| e.encode_to_vec()),
60 }
61 }
62}
63
64pub trait Authorize: Send + Sync {
68 fn context(
69 &self,
70 method: &'static MethodDescriptor,
71 body: &[u8],
72 ) -> impl Future<Output = Result<CallContext, Error>> + Send;
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn remote_failure_retains_typed_details_without_boxing_the_error_path() {
81 use api::heddle::api::common::{ErrorDetail, ErrorReason};
82 let detail = ErrorDetail {
83 reason: ErrorReason::PolicyDenied as i32,
84 resource: "thread".into(),
85 ..Default::default()
86 };
87 let failure = RemoteFailure::from(CallFailure {
88 code: 7,
89 message: "review required".into(),
90 error: Some(detail.clone()),
91 });
92 assert_eq!(failure.detail().expect("typed detail"), Some(detail));
93 assert_eq!(failure.message, "review required");
94 }
95}