diff --git a/Cargo.toml b/Cargo.toml index 3fc3705..b80448c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = ["crates/aerro", "crates/aerro-macros"] # (Reserved for workspace-wide clippy overrides; currently none.) [workspace.package] -version = "0.6.2" +version = "0.6.3" edition = "2024" rust-version = "1.85" license = "MIT OR Apache-2.0" diff --git a/crates/aerro-macros/src/codegen/aerro_impl.rs b/crates/aerro-macros/src/codegen/aerro_impl.rs index 4927559..c9bed9f 100644 --- a/crates/aerro-macros/src/codegen/aerro_impl.rs +++ b/crates/aerro-macros/src/codegen/aerro_impl.rs @@ -88,7 +88,7 @@ pub fn emit_aerro_impl(cfg: &EnumCfg) -> TokenStream { } } - fn encode_payload(&self, __route: ::aerro::Exposure, __buf: &mut ::std::vec::Vec) { + fn encode_payload(&self, __route: ::aerro::Exposure, __buf: &mut ::std::vec::Vec) -> ::core::result::Result<(), ::aerro::EncodeError> { let _ = __route; match self { #(#encode_arms)* @@ -132,7 +132,7 @@ fn encode_payload_arm(v: &VariantCfg) -> TokenStream { if v.fields.is_empty() { return quote! { Self::#variant => { - // No payload to encode. + ::core::result::Result::Ok(()) } }; } @@ -156,11 +156,10 @@ fn encode_payload_arm(v: &VariantCfg) -> TokenStream { quote! { Self::#variant #pat => { let __tup = ( #(#payload_exprs ,)* ); - if let ::core::result::Result::Ok(__bytes) = - ::bincode::encode_to_vec(&__tup, ::bincode::config::standard()) - { - __buf.extend_from_slice(&__bytes); - } + let __bytes = ::bincode::encode_to_vec(&__tup, ::bincode::config::standard()) + .map_err(|e| ::aerro::EncodeError(e.to_string()))?; + __buf.extend_from_slice(&__bytes); + ::core::result::Result::Ok(()) } } } else { @@ -177,11 +176,10 @@ fn encode_payload_arm(v: &VariantCfg) -> TokenStream { quote! { Self::#variant #pat => { let __tup = ( #(#payload_exprs ,)* ); - if let ::core::result::Result::Ok(__bytes) = - ::bincode::encode_to_vec(&__tup, ::bincode::config::standard()) - { - __buf.extend_from_slice(&__bytes); - } + let __bytes = ::bincode::encode_to_vec(&__tup, ::bincode::config::standard()) + .map_err(|e| ::aerro::EncodeError(e.to_string()))?; + __buf.extend_from_slice(&__bytes); + ::core::result::Result::Ok(()) } } } diff --git a/crates/aerro/src/remote.rs b/crates/aerro/src/remote.rs index 09e6ac7..1d59e6b 100644 --- a/crates/aerro/src/remote.rs +++ b/crates/aerro/src/remote.rs @@ -106,7 +106,9 @@ mod tests { fn downcast_recovers_known_type() { use crate::Exposure; let mut buf = Vec::new(); - Boom { x: 7 }.encode_payload(Exposure::Internal, &mut buf); + Boom { x: 7 } + .encode_payload(Exposure::Internal, &mut buf) + .unwrap(); let r = make(RemoteErrorParts { category: Category::System, type_id: "toy.boom".into(), diff --git a/crates/aerro/src/test_support.rs b/crates/aerro/src/test_support.rs index 5a8a77a..edf59bd 100644 --- a/crates/aerro/src/test_support.rs +++ b/crates/aerro/src/test_support.rs @@ -26,9 +26,15 @@ impl Aerro for Boom { Code::Internal } - fn encode_payload(&self, _route: Exposure, buf: &mut Vec) { - let bytes = bincode::encode_to_vec(self, bincode::config::standard()).unwrap(); + fn encode_payload( + &self, + _route: Exposure, + buf: &mut Vec, + ) -> Result<(), crate::EncodeError> { + let bytes = bincode::encode_to_vec(self, bincode::config::standard()) + .map_err(|e| crate::EncodeError(e.to_string()))?; buf.extend_from_slice(&bytes); + Ok(()) } fn decode_payload(type_id: &str, bytes: &[u8]) -> Result { diff --git a/crates/aerro/src/traits/aerro.rs b/crates/aerro/src/traits/aerro.rs index 2b7632a..181ee85 100644 --- a/crates/aerro/src/traits/aerro.rs +++ b/crates/aerro/src/traits/aerro.rs @@ -2,7 +2,10 @@ use tonic::Code; -use crate::{Category, Exposure, error::DecodeError}; +use crate::{ + Category, Exposure, + error::{DecodeError, EncodeError}, +}; /// Universal trait implemented by every typed error. /// @@ -31,7 +34,7 @@ pub trait Aerro: std::error::Error + Send + Sync + 'static { /// Encode the variant's payload into bincode bytes. `route` is the /// exposure level of the destination; fields marked `#[aerro(redact)]` /// are replaced with `Default::default()` whenever `route != Internal`. - fn encode_payload(&self, route: Exposure, buf: &mut Vec); + fn encode_payload(&self, route: Exposure, buf: &mut Vec) -> Result<(), EncodeError>; /// Decode a typed variant from a `type_id` + bincode bytes. fn decode_payload(type_id: &str, bytes: &[u8]) -> Result diff --git a/crates/aerro/src/wire/decode.rs b/crates/aerro/src/wire/decode.rs index 231382b..be53d5d 100644 --- a/crates/aerro/src/wire/decode.rs +++ b/crates/aerro/src/wire/decode.rs @@ -138,7 +138,13 @@ mod tests { fn code(&self) -> Code { Code::NotFound } - fn encode_payload(&self, _: crate::Exposure, _: &mut Vec) {} + fn encode_payload( + &self, + _: crate::Exposure, + _: &mut Vec, + ) -> Result<(), crate::EncodeError> { + Ok(()) + } fn decode_payload(_: &str, _: &[u8]) -> Result { Err(crate::DecodeError::Missing) } diff --git a/crates/aerro/src/wire/encode.rs b/crates/aerro/src/wire/encode.rs index a322870..76d84e7 100644 --- a/crates/aerro/src/wire/encode.rs +++ b/crates/aerro/src/wire/encode.rs @@ -1,7 +1,7 @@ //! `ServiceFailure` → `tonic::Status` encoding — see spec §6, §9. use prost::Message; -use tonic::Status; +use tonic::{Code, Status}; use crate::{Aerro, Category, Exposure, Frame, ServiceFailure}; @@ -40,7 +40,9 @@ pub fn encode(sf: &ServiceFailure, opts: &EncodeOptions) -> Status let outer_msg = redact_message(&sf.inner, route); let mut payload = Vec::new(); - sf.inner.encode_payload(route, &mut payload); + if let Err(e) = sf.inner.encode_payload(route, &mut payload) { + return Status::new(Code::Internal, format!("aerro: encode failed: {e}")); + } let wire_frames = if route == Exposure::Public { Vec::new()