diff --git a/js/packages/truapi/src/client.test.ts b/js/packages/truapi/src/client.test.ts index 5d971886..60a17d43 100644 --- a/js/packages/truapi/src/client.test.ts +++ b/js/packages/truapi/src/client.test.ts @@ -2,7 +2,7 @@ import type { Result } from "neverthrow"; import { describe, expect, it } from "bun:test"; import { createTransport } from "./client.js"; -import { CallError, indexedTaggedUnion, Result as ScaleResult, str, _void } from "./scale.js"; +import { indexedTaggedUnion, Result as ScaleResult, str, _void } from "./scale.js"; import type { Codec } from "./scale.js"; import { createClient, SubscriptionError } from "./generated/client.js"; import * as T from "./generated/types.js"; @@ -60,7 +60,7 @@ function providerFixture() { /** Encode a V1 host-handshake response result payload. */ function handshakeResponsePayload(value: { success: true; value: undefined }): Uint8Array { - return versionedV1(ScaleResult(_void, CallError(T.VersionedHostHandshakeError))).enc({ + return versionedV1(ScaleResult(_void, T.HostHandshakeError)).enc({ tag: "V1", value, }); @@ -74,11 +74,11 @@ function accountGetResponsePayload( } | { success: false; - value: { tag: "Domain"; value: T.VersionedHostAccountGetError }; + value: T.HostAccountGetError; }, ): Uint8Array { return versionedV1( - ScaleResult(T.HostAccountGetResponse, CallError(T.VersionedHostAccountGetError)), + ScaleResult(T.HostAccountGetResponse, T.HostAccountGetError), ).enc({ tag: "V1", value }); } @@ -158,7 +158,7 @@ describe("generated client transport", () => { const response = client.account.getAccount({ productAccountId: { dotNsIdentifier: "foo", derivationIndex: 0 }, }); - const reason = { tag: "V1", value: { tag: "NotConnected", value: undefined } } as const; + const reason = { tag: "NotConnected", value: undefined } as const; const frame = unwrap( encodeWireMessage({ requestId: "p:1", @@ -166,7 +166,7 @@ describe("generated client transport", () => { id: W.ACCOUNT_GET_ACCOUNT.response, value: accountGetResponsePayload({ success: false, - value: { tag: "Domain", value: reason }, + value: reason, }), }, }), @@ -176,7 +176,7 @@ describe("generated client transport", () => { const result = await response; expect(result.isErr()).toBe(true); - expect(result._unsafeUnwrapErr()).toEqual({ tag: "Domain", value: reason }); + expect(result._unsafeUnwrapErr()).toEqual(reason); }); it("auto-responds to an inbound handshake with the versioned-result shape", () => { @@ -275,18 +275,14 @@ describe("generated client transport", () => { }); const reason = { tag: "PermissionDenied", value: undefined } as const; - const callError = { - tag: "Domain", - value: { tag: "V1", value: reason }, - } as const; const frame = unwrap( encodeWireMessage({ requestId: sub.subscriptionId, payload: { id: W.PAYMENT_BALANCE_SUBSCRIBE.interrupt, - value: versionedV1(CallError(T.VersionedHostPaymentBalanceSubscribeError)).enc({ + value: T.VersionedHostPaymentBalanceSubscribeError.enc({ tag: "V1", - value: callError, + value: reason, }), }, }), @@ -297,7 +293,7 @@ describe("generated client transport", () => { expect(completions).toEqual([]); expect(errors).toHaveLength(1); expect(errors[0]).toBeInstanceOf(SubscriptionError); - expect((errors[0] as SubscriptionError).reason).toEqual(callError); + expect((errors[0] as SubscriptionError).reason).toEqual(reason); expect(fixture.sent).toHaveLength(1); }); @@ -312,18 +308,15 @@ describe("generated client transport", () => { .subscribe({ error: (error) => errors.push(error) }); const reason = "Denied"; - const callError = { - tag: "Domain", - value: { tag: "V1", value: reason }, - } as const; const frame = unwrap( encodeWireMessage({ requestId: sub.subscriptionId, payload: { id: W.COIN_PAYMENT_REBALANCE_PURSE.interrupt, - value: versionedV1( - CallError(T.VersionedHostCoinPaymentRebalancePurseError), - ).enc({ tag: "V1", value: callError }), + value: T.VersionedHostCoinPaymentRebalancePurseError.enc({ + tag: "V1", + value: reason, + }), }, }), "encode typed coin payment interrupt", @@ -332,7 +325,7 @@ describe("generated client transport", () => { expect(errors).toHaveLength(1); expect(errors[0]).toBeInstanceOf(SubscriptionError); - expect((errors[0] as SubscriptionError).reason).toEqual(callError); + expect((errors[0] as SubscriptionError).reason).toEqual(reason); }); it("treats a malformed receive payload as terminal and sends _stop", () => { diff --git a/js/packages/truapi/src/client.ts b/js/packages/truapi/src/client.ts index f45481f4..fb16af14 100644 --- a/js/packages/truapi/src/client.ts +++ b/js/packages/truapi/src/client.ts @@ -13,11 +13,9 @@ import { type WireProvider, } from "./transport.js"; import { - CallError, indexedTaggedUnion, Result, _void, - type CallErrorValue, type Codec, type ResultPayload, } from "./scale.js"; @@ -52,10 +50,7 @@ function protocolVersionTag(version: number): `V${number}` { return `V${version}` as `V${number}`; } -type HandshakeResponse = ResultPayload< - undefined, - CallErrorValue ->; +type HandshakeResponse = ResultPayload; const HANDSHAKE_WIRE_VERSION = 1; /** @@ -67,7 +62,7 @@ function handshakeResponseCodec( return indexedTaggedUnion({ [protocolVersionTag(version)]: [ version - 1, - Result(_void, CallError(T.VersionedHostHandshakeError)), + Result(_void, T.HostHandshakeError), ] as const, }) as Codec<{ tag: `V${number}`; value: HandshakeResponse }>; } @@ -94,14 +89,8 @@ function encodeUnsupportedHandshakeResponse(version: number): Uint8Array { value: { success: false, value: { - tag: "Domain", - value: { - tag: "V1", - value: { - tag: "UnsupportedProtocolVersion", - value: undefined, - }, - }, + tag: "UnsupportedProtocolVersion", + value: undefined, }, }, }); diff --git a/js/packages/truapi/src/scale.ts b/js/packages/truapi/src/scale.ts index c9a09a1c..7a5a11bb 100644 --- a/js/packages/truapi/src/scale.ts +++ b/js/packages/truapi/src/scale.ts @@ -8,11 +8,9 @@ import { Bytes, Enum, - Struct, createCodec, createDecoder, enhanceCodec, - str, u8, _void, type Codec, @@ -123,25 +121,6 @@ export function TaggedUnion( return Enum(inner) as unknown as Codec>; } -/** Public TS value for Rust's derived `CallError` enum. */ -export type CallErrorValue = - | { tag: "Domain"; value: D } - | { tag: "Denied"; value?: undefined } - | { tag: "Unsupported"; value?: undefined } - | { tag: "MalformedFrame"; value: { reason: string } } - | { tag: "HostFailure"; value: { reason: string } }; - -/** SCALE codec for Rust's derived `CallError` enum. */ -export function CallError(domain: Codec): Codec> { - return TaggedUnion({ - Domain: domain, - Denied: _void, - Unsupported: _void, - MalformedFrame: Struct({ reason: str }), - HostFailure: Struct({ reason: str }), - }) as Codec>; -} - type TaggedUnionCodecs = { [Sym: symbol]: never; [Num: number]: never; diff --git a/rust/crates/truapi-codegen/src/rust.rs b/rust/crates/truapi-codegen/src/rust.rs index c7878829..3afbef01 100644 --- a/rust/crates/truapi-codegen/src/rust.rs +++ b/rust/crates/truapi-codegen/src/rust.rs @@ -200,10 +200,30 @@ mod tests { } } + /// Domain payload enum backing a versioned test wrapper, carrying the + /// `Unknown { reason }` catch-all the error emission folds into. + fn domain_test_type(name: &str) -> TypeDef { + TypeDef { + name: format!("V01{name}"), + module_path: Vec::new(), + generic_params: Vec::new(), + kind: TypeDefKind::Enum(vec![VariantDef { + name: "Unknown".to_string(), + fields: VariantFields::Named(vec![FieldDef { + name: "reason".to_string(), + type_ref: TypeRef::Primitive("str".to_string()), + docs: None, + }]), + docs: None, + }]), + docs: None, + } + } + fn versioned_request_test_types() -> Vec { ["ReqWrapper", "RespWrapper", "ErrWrapper"] .into_iter() - .map(versioned_test_type) + .flat_map(|name| [versioned_test_type(name), domain_test_type(name)]) .collect() } @@ -672,10 +692,7 @@ mod tests { docs: None, }], public_trait_order: vec!["Permissions".to_string()], - types: vec![ - versioned_test_type("RespWrapper"), - versioned_test_type("ErrWrapper"), - ], + types: versioned_request_test_types(), }; let err = generate_dispatcher(&api).expect_err("missing target version must error"); diff --git a/rust/crates/truapi-codegen/src/rust/dispatcher.rs b/rust/crates/truapi-codegen/src/rust/dispatcher.rs index 183327ed..29f25a87 100644 --- a/rust/crates/truapi-codegen/src/rust/dispatcher.rs +++ b/rust/crates/truapi-codegen/src/rust/dispatcher.rs @@ -150,6 +150,10 @@ struct MethodEmission { request_payload: Option, response_wrapper: Option, error_payload: WirePayload, + /// `|reason| ` closure literal folding framework + /// `CallError` variants onto the flat wire error encoding. Present iff + /// `error_payload` is versioned. + error_fallback: Option, item_wrapper: Option, } @@ -231,6 +235,14 @@ impl MethodEmission { ), }; + let error_fallback = match &error_payload { + WirePayload::Versioned(wrapper) => Some( + error_fallback_expr(api, wrapper) + .with_context(|| format!("Method `{}`", method.name))?, + ), + WirePayload::Raw(_) => None, + }; + Ok(MethodEmission { name: method.name.clone(), wire_name: wire_method.to_string(), @@ -238,6 +250,7 @@ impl MethodEmission { kind: method.kind, request_payload, response_wrapper, + error_fallback, error_payload, item_wrapper, }) @@ -256,6 +269,13 @@ impl MethodEmission { matches!(self.request_payload, Some(WirePayload::Raw(_))) || self.uses_raw_unit_ok_payload() } + /// The catch-all closure literal for this method's versioned error type. + fn fallback_expr(&self, method: &str) -> Result<&str> { + self.error_fallback.as_deref().with_context(|| { + format!("Method `{method}`: versioned error emission requires a domain catch-all") + }) + } + fn uses_raw_unit_ok_payload(&self) -> bool { matches!(self.kind, MethodKind::Request) && self.response_wrapper.is_none() @@ -285,6 +305,7 @@ impl MethodEmission { let Some(error) = self.error_payload.versioned_name() else { bail!("Method `{method}`: versioned request methods must use versioned errors"); }; + let fallback = self.fallback_expr(method)?; write_indented( out, 16, @@ -298,6 +319,7 @@ impl MethodEmission { return Ok(encode_versioned_err_payload( error, ::LATEST, + {fallback}, )); }} }}; @@ -357,6 +379,7 @@ impl MethodEmission { let Some(target_version_expr) = target_version_expr.as_deref() else { bail!("Method `{method}`: versioned responses require a target version"); }; + let fallback = self.fallback_expr(method)?; write_indented( out, 16, @@ -365,7 +388,7 @@ impl MethodEmission { let response: versioned::{module}::{response} = match host.{method}({call_args}).await {{ Ok(value) => value, Err(err) => {{ - return Ok(encode_versioned_err_payload(err, {target_version_expr})); + return Ok(encode_versioned_err_payload(err, {target_version_expr}, {fallback})); }} }}; Ok(encode_versioned_ok_payload(response)) @@ -375,6 +398,7 @@ impl MethodEmission { } None => match (&self.error_payload, target_version_expr.as_deref()) { (WirePayload::Versioned(_), Some(target_version_expr)) => { + let fallback = self.fallback_expr(method)?; write_indented( out, 16, @@ -383,7 +407,7 @@ impl MethodEmission { match host.{method}({call_args}).await {{ Ok(()) => Ok(encode_versioned_unit_ok_payload({target_version_expr})), Err(err) => {{ - Ok(encode_versioned_err_payload(err, {target_version_expr})) + Ok(encode_versioned_err_payload(err, {target_version_expr}, {fallback})) }} }} "# @@ -452,6 +476,7 @@ impl MethodEmission { { let decode_error = match error { Some(error) => { + let fallback = self.fallback_expr(method)?; let block = formatdoc! { r#" Err(err) => {{ @@ -462,6 +487,7 @@ impl MethodEmission { return Err(encode_versioned_interrupt_payload( error, ::LATEST, + {fallback}, )); }} "# @@ -510,6 +536,7 @@ impl MethodEmission { if error.is_none() { bail!("Method `{method}`: result subscription methods must have an error wrapper"); } + let fallback = self.fallback_expr(method)?; write_indented( out, 16, @@ -518,7 +545,7 @@ impl MethodEmission { let stream = match host.{method}({call_args}).await {{ Ok(sub) => sub, Err(err) => {{ - return Err(encode_versioned_interrupt_payload(err, {target_version_expr})); + return Err(encode_versioned_interrupt_payload(err, {target_version_expr}, {fallback})); }} }}; "# @@ -601,6 +628,110 @@ fn versioned_wrapper_root<'a>( Ok(name) } +/// Build the `|reason| ` closure literal for a versioned error +/// wrapper. The closure constructs the latest domain enum's catch-all variant +/// (`Unknown`, or `Internal` where no `Unknown` exists) so framework +/// `CallError` variants can fold onto the flat wire error encoding. +/// +/// A reason-carrying catch-all (`Unknown { reason }` / `Unknown(GenericError)`) +/// preserves the framework reason; a unit catch-all (a bare `Internal`) has +/// nowhere to carry it, so the reason is dropped and the folded framework +/// variants collapse onto one discriminant. +fn error_fallback_expr(api: &ApiDefinition, wrapper_name: &str) -> Result { + let is_version_variant = |v: &VariantDef| { + v.name + .strip_prefix('V') + .is_some_and(|n| n.parse::().is_ok()) + }; + let wrapper_variants = api + .types + .iter() + .find_map(|ty| match &ty.kind { + TypeDefKind::Enum(variants) + if ty.name == wrapper_name && variants.iter().all(is_version_variant) => + { + Some(variants) + } + _ => None, + }) + .with_context(|| format!("versioned error wrapper `{wrapper_name}` not extracted"))?; + let latest = wrapper_variants + .iter() + .max_by_key(|v| v.name[1..].parse::().unwrap_or(0)) + .with_context(|| format!("versioned error wrapper `{wrapper_name}` has no variants"))?; + let VariantFields::Unnamed(inner) = &latest.fields else { + bail!( + "versioned error wrapper `{wrapper_name}`: latest variant `{}` must carry \ + exactly one domain payload", + latest.name + ); + }; + let [inner_ty] = inner.as_slice() else { + bail!( + "versioned error wrapper `{wrapper_name}`: latest variant `{}` must carry \ + exactly one domain payload", + latest.name + ); + }; + let inner_path = rust_type_ref(inner_ty)?; + let TypeRef::Named { name, .. } = inner_ty else { + bail!("versioned error wrapper `{wrapper_name}`: domain payload must be a named enum"); + }; + let bare_name = version_prefixed_type(name).map_or(name.as_str(), |(_, base)| base); + // Wrappers over the `GenericError` struct carry the reason directly. + if bare_name == "GenericError" { + return Ok(format!("|reason| {inner_path} {{ reason }}")); + } + let domain_variants = api + .types + .iter() + .find_map(|ty| match &ty.kind { + TypeDefKind::Enum(variants) + if ty.name == *name && !variants.iter().all(is_version_variant) => + { + Some(variants) + } + _ => None, + }) + .with_context(|| format!("domain error enum `{name}` not extracted"))?; + let catch_all = domain_variants + .iter() + .find(|v| v.name == "Unknown") + .or_else(|| domain_variants.iter().find(|v| v.name == "Internal")) + .with_context(|| { + format!( + "domain error enum `{bare_name}` has no `Unknown`/`Internal` catch-all; \ + the flat wire error encoding requires one" + ) + })?; + let variant = &catch_all.name; + match &catch_all.fields { + // Unit catch-all: no field for the folded reason, so it is dropped. + VariantFields::Unit => Ok(format!("|_reason| {inner_path}::{variant}")), + VariantFields::Named(fields) + if fields.len() == 1 + && fields[0].name == "reason" + && matches!(&fields[0].type_ref, TypeRef::Primitive(p) if p == "str") => + { + Ok(format!("|reason| {inner_path}::{variant} {{ reason }}")) + } + VariantFields::Unnamed(types) + if matches!( + types.as_slice(), + [TypeRef::Named { name, args }] if name == "GenericError" && args.is_empty() + ) => + { + Ok(format!( + "|reason| {inner_path}::{variant}(truapi::v01::GenericError {{ reason }})" + )) + } + _ => bail!( + "domain error enum `{bare_name}`: catch-all variant `{variant}` has an \ + unsupported payload shape for reason folding" + ), + } +} + fn versioned_wrapper_names(api: &ApiDefinition) -> BTreeSet { api.types .iter() diff --git a/rust/crates/truapi-codegen/src/ts.rs b/rust/crates/truapi-codegen/src/ts.rs index 019c33f7..a989951d 100644 --- a/rust/crates/truapi-codegen/src/ts.rs +++ b/rust/crates/truapi-codegen/src/ts.rs @@ -36,7 +36,6 @@ struct CodecContext { enum NameMode<'a> { #[default] Public, - PreserveQualified, Generated { aliases: &'a BTreeMap, }, @@ -45,7 +44,6 @@ enum NameMode<'a> { fn resolve_named(name: &str, mode: NameMode<'_>) -> String { match mode { NameMode::Public => public_versioned_type_name(name), - NameMode::PreserveQualified => name.to_string(), NameMode::Generated { aliases } => aliases .get(name) .cloned() @@ -58,7 +56,6 @@ fn resolve_named(name: &str, mode: NameMode<'_>) -> String { fn qualify_named(resolved: &str, mode: NameMode<'_>) -> String { match mode { NameMode::Public => format!("T.{resolved}"), - NameMode::PreserveQualified => format!("T.{resolved}"), NameMode::Generated { .. } => resolved.to_string(), } } @@ -1326,46 +1323,16 @@ fn emit_response( }) } +/// Error payloads share the response emission: the wire carries the flat +/// domain enum, with any `CallError` layer in the trait signature confined to +/// the Rust host side. fn emit_error_response( ty: &TypeRef, wrappers: &HashMap, ctx: &CodecContext, wire_version: Option, ) -> Result { - let Some(error_wrapper_ty) = call_error_inner(ty) else { - return emit_response(ty, wrappers, ctx, wire_version); - }; - - if let Some((wrapper_name, _wrapper)) = versioned_wrapper_for(error_wrapper_ty, wrappers) { - let version = wire_version.ok_or_else(|| { - anyhow::anyhow!("versioned error wrapper `{wrapper_name}` has no selected wire version") - })?; - let versioned_name = versioned_wrapper_ts_name(wrapper_name); - let inner_type_ts = format!("S.CallErrorValue"); - let inner_codec_expr = format!("S.CallError(T.{versioned_name})"); - let wire_codec_expr = indexed_versioned_codec_expr([(version, inner_codec_expr.clone())])?; - return Ok(ResponseEmission { - inner_type_ts: inner_type_ts.clone(), - wire_type_ts: format!("{{ tag: \"V{version}\"; value: {inner_type_ts} }}"), - wire_codec_expr, - inner_codec_expr, - }); - } - - let inner_type_ts = format!( - "S.CallErrorValue<{}>", - ts_type_qualified_preserve(error_wrapper_ty)? - ); - let inner_codec_expr = format!( - "S.CallError({})", - codec_expr_mode(error_wrapper_ty, true, ctx, NameMode::PreserveQualified)? - ); - Ok(ResponseEmission { - inner_type_ts: inner_type_ts.clone(), - wire_type_ts: inner_type_ts, - wire_codec_expr: inner_codec_expr.clone(), - inner_codec_expr, - }) + emit_response(call_error_inner(ty).unwrap_or(ty), wrappers, ctx, wire_version) } fn versioned_kind_codec_expr_mode( @@ -2256,10 +2223,6 @@ fn ts_type_qualified(ty: &TypeRef) -> Result { ts_type_with_named(ty, true, NameMode::Public) } -fn ts_type_qualified_preserve(ty: &TypeRef) -> Result { - ts_type_with_named(ty, true, NameMode::PreserveQualified) -} - fn ts_field_name(name: &str, ty: &TypeRef) -> (String, bool) { let camel = to_camel_case(name); let optional = matches!(ty, TypeRef::Option(_)); diff --git a/rust/crates/truapi-codegen/tests/golden/dispatcher.rs b/rust/crates/truapi-codegen/tests/golden/dispatcher.rs index 25f2fb13..58b06182 100644 --- a/rust/crates/truapi-codegen/tests/golden/dispatcher.rs +++ b/rust/crates/truapi-codegen/tests/golden/dispatcher.rs @@ -85,6 +85,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -93,7 +94,7 @@ where let response: versioned::account::HostAccountGetResponse = match host.get_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostAccountGetError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -113,6 +114,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -121,7 +123,7 @@ where let response: versioned::account::HostAccountGetAliasResponse = match host.get_account_alias(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostAccountGetError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -141,6 +143,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountCreateProofError::Unknown { reason }, )); } }; @@ -149,7 +152,7 @@ where let response: versioned::account::HostAccountCreateProofResponse = match host.create_account_proof(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostAccountCreateProofError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -169,6 +172,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -177,7 +181,7 @@ where let response: versioned::account::HostGetLegacyAccountsResponse = match host.get_legacy_accounts(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostAccountGetError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -197,6 +201,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostGetUserIdError::Unknown { reason }, )); } }; @@ -205,7 +210,7 @@ where let response: versioned::account::HostGetUserIdResponse = match host.get_user_id(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostGetUserIdError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -225,6 +230,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostRequestLoginError::Unknown { reason }, )); } }; @@ -233,7 +239,7 @@ where let response: versioned::account::HostRequestLoginResponse = match host.request_login(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostRequestLoginError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -274,6 +280,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -282,7 +289,7 @@ where let response: versioned::chain::RemoteChainHeadHeaderResponse = match host.get_head_header(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -302,6 +309,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -310,7 +318,7 @@ where let response: versioned::chain::RemoteChainHeadBodyResponse = match host.get_head_body(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -330,6 +338,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -338,7 +347,7 @@ where let response: versioned::chain::RemoteChainHeadStorageResponse = match host.get_head_storage(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -358,6 +367,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -366,7 +376,7 @@ where let response: versioned::chain::RemoteChainHeadCallResponse = match host.call_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -386,6 +396,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -394,7 +405,7 @@ where let response: versioned::chain::RemoteChainHeadUnpinResponse = match host.unpin_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -414,6 +425,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -422,7 +434,7 @@ where let response: versioned::chain::RemoteChainHeadContinueResponse = match host.continue_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -442,6 +454,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -450,7 +463,7 @@ where let response: versioned::chain::RemoteChainHeadStopOperationResponse = match host.stop_head_operation(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -470,6 +483,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -478,7 +492,7 @@ where let response: versioned::chain::RemoteChainSpecGenesisHashResponse = match host.get_spec_genesis_hash(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -498,6 +512,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -506,7 +521,7 @@ where let response: versioned::chain::RemoteChainSpecChainNameResponse = match host.get_spec_chain_name(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -526,6 +541,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -534,7 +550,7 @@ where let response: versioned::chain::RemoteChainSpecPropertiesResponse = match host.get_spec_properties(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -554,6 +570,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -562,7 +579,7 @@ where let response: versioned::chain::RemoteChainTransactionBroadcastResponse = match host.broadcast_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -582,6 +599,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -590,7 +608,7 @@ where let response: versioned::chain::RemoteChainTransactionStopResponse = match host.stop_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -616,6 +634,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatCreateRoomError::Unknown { reason }, )); } }; @@ -624,7 +643,7 @@ where let response: versioned::chat::HostChatCreateRoomResponse = match host.create_room(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostChatCreateRoomError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -644,6 +663,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatRegisterBotError::Unknown { reason }, )); } }; @@ -652,7 +672,7 @@ where let response: versioned::chat::HostChatRegisterBotResponse = match host.register_bot(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostChatRegisterBotError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -684,6 +704,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatPostMessageError::Unknown { reason }, )); } }; @@ -692,7 +713,7 @@ where let response: versioned::chat::HostChatPostMessageResponse = match host.post_message(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostChatPostMessageError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -745,6 +766,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -753,7 +775,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreatePurseResponse = match host.create_purse(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -773,6 +795,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -781,7 +804,7 @@ where let response: versioned::coin_payment::HostCoinPaymentQueryPurseResponse = match host.query_purse(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -803,6 +826,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -811,7 +835,7 @@ where let stream = match host.rebalance_purse(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -833,6 +857,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -841,7 +866,7 @@ where let stream = match host.delete_purse(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -861,6 +886,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -869,7 +895,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreateReceivableResponse = match host.create_receivable(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -889,6 +915,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -897,7 +924,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreateChequeResponse = match host.create_cheque(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -919,6 +946,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -927,7 +955,7 @@ where let stream = match host.deposit(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -949,6 +977,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -957,7 +986,7 @@ where let stream = match host.refund(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -979,6 +1008,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -987,7 +1017,7 @@ where let stream = match host.listen_for_payment(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -1013,6 +1043,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostDeriveEntropyError::Unknown { reason }, )); } }; @@ -1021,7 +1052,7 @@ where let response: versioned::entropy::HostDeriveEntropyResponse = match host.derive(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostDeriveEntropyError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1047,6 +1078,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1055,7 +1087,7 @@ where let response: versioned::local_storage::HostLocalStorageReadResponse = match host.read(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1075,6 +1107,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1083,7 +1116,7 @@ where let response: versioned::local_storage::HostLocalStorageWriteResponse = match host.write(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1103,6 +1136,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1111,7 +1145,7 @@ where let response: versioned::local_storage::HostLocalStorageClearResponse = match host.clear(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1137,6 +1171,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPushNotificationError::Unknown { reason }, )); } }; @@ -1145,7 +1180,7 @@ where let response: versioned::notifications::HostPushNotificationResponse = match host.send_push_notification(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostPushNotificationError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1165,6 +1200,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1173,7 +1209,7 @@ where let response: versioned::notifications::HostPushNotificationCancelResponse = match host.cancel_push_notification(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1201,6 +1237,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason }, )); } }; @@ -1209,7 +1246,7 @@ where let stream = match host.balance_subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason })); } }; Ok(subscription_stream::(stream)) @@ -1229,6 +1266,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentError::Unknown { reason }, )); } }; @@ -1237,7 +1275,7 @@ where let response: versioned::payment::HostPaymentResponse = match host.request(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostPaymentError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1259,6 +1297,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentStatusSubscribeError::Unknown { reason }, )); } }; @@ -1267,7 +1306,7 @@ where let stream = match host.status_subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::HostPaymentStatusSubscribeError::Unknown { reason })); } }; Ok(subscription_stream::(stream)) @@ -1287,6 +1326,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentTopUpError::Unknown { reason }, )); } }; @@ -1295,7 +1335,7 @@ where let response: versioned::payment::HostPaymentTopUpResponse = match host.top_up(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostPaymentTopUpError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1321,6 +1361,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1329,7 +1370,7 @@ where let response: versioned::permissions::HostDevicePermissionResponse = match host.request_device_permission(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1349,6 +1390,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1357,7 +1399,7 @@ where let response: versioned::permissions::RemotePermissionResponse = match host.request_remote_permission(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1398,6 +1440,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::PreimageSubmitError::Unknown { reason }, )); } }; @@ -1406,7 +1449,7 @@ where let response: versioned::preimage::RemotePreimageSubmitResponse = match host.submit(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::PreimageSubmitError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1432,6 +1475,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::ResourceAllocationError::Unknown { reason }, )); } }; @@ -1440,7 +1484,7 @@ where let response: versioned::resource_allocation::HostRequestResourceAllocationResponse = match host.request(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::ResourceAllocationError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1466,6 +1510,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostCreateTransactionError::Unknown { reason }, )); } }; @@ -1474,7 +1519,7 @@ where let response: versioned::signing::HostCreateTransactionResponse = match host.create_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostCreateTransactionError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1494,6 +1539,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostCreateTransactionError::Unknown { reason }, )); } }; @@ -1502,7 +1548,7 @@ where let response: versioned::signing::HostCreateTransactionWithLegacyAccountResponse = match host.create_transaction_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostCreateTransactionError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1522,6 +1568,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1530,7 +1577,7 @@ where let response: versioned::signing::HostSignRawWithLegacyAccountResponse = match host.sign_raw_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1550,6 +1597,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1558,7 +1606,7 @@ where let response: versioned::signing::HostSignPayloadWithLegacyAccountResponse = match host.sign_payload_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1578,6 +1626,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1586,7 +1635,7 @@ where let response: versioned::signing::HostSignRawResponse = match host.sign_raw(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1606,6 +1655,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1614,7 +1664,7 @@ where let response: versioned::signing::HostSignPayloadResponse = match host.sign_payload(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1642,6 +1692,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1650,7 +1701,7 @@ where let stream = match host.subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(subscription_stream::(stream)) @@ -1670,6 +1721,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason }, )); } }; @@ -1678,7 +1730,7 @@ where let response: versioned::statement_store::RemoteStatementStoreCreateProofResponse = match host.create_proof(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1698,6 +1750,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason }, )); } }; @@ -1706,7 +1759,7 @@ where let response: versioned::statement_store::RemoteStatementStoreCreateProofAuthorizedResponse = match host.create_proof_authorized(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1726,6 +1779,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1734,7 +1788,7 @@ where match host.submit(&cx, request).await { Ok(()) => Ok(encode_versioned_unit_ok_payload(target_version)), Err(err) => { - Ok(encode_versioned_err_payload(err, target_version)) + Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })) } } }) @@ -1759,6 +1813,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostHandshakeError::Unknown(truapi::v01::GenericError { reason }), )); } }; @@ -1767,7 +1822,7 @@ where let response: versioned::system::HostHandshakeResponse = match host.handshake(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostHandshakeError::Unknown(truapi::v01::GenericError { reason }))); } }; Ok(encode_versioned_ok_payload(response)) @@ -1787,6 +1842,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1795,7 +1851,7 @@ where let response: versioned::system::HostFeatureSupportedResponse = match host.feature_supported(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1815,6 +1871,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostNavigateToError::Unknown { reason }, )); } }; @@ -1823,7 +1880,7 @@ where let response: versioned::system::HostNavigateToResponse = match host.navigate_to(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostNavigateToError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) diff --git a/rust/crates/truapi-server/src/frame.rs b/rust/crates/truapi-server/src/frame.rs index c324200a..94a40065 100644 --- a/rust/crates/truapi-server/src/frame.rs +++ b/rust/crates/truapi-server/src/frame.rs @@ -16,6 +16,8 @@ //! reconstruct string action tags on every frame. use parity_scale_codec::{Decode, Encode, Error as CodecError, Input, Output}; +use truapi::CallError; +use truapi::versioned::{FromLatest, Versioned}; use crate::generated::wire_table::{RequestFrameIds, SubscriptionFrameIds, WIRE_TABLE, WireKind}; @@ -42,14 +44,21 @@ pub fn encode_versioned_unit_ok_payload(version: u8) -> Vec { vec![version_index(version), 0] } -/// Encode `Versioned>` from an ordinary error value. -pub fn encode_versioned_err_payload(value: T, version: u8) -> Vec { - let encoded = value.encode(); - let mut out = Vec::with_capacity(encoded.len() + 2); - out.push(version_index(version)); - out.push(1); - out.extend_from_slice(&encoded); - out +/// Encode `Versioned>` from a handler error. +/// +/// The wire carries the flat domain enum: `[version][Err][domain bytes]`. +/// Framework-level [`CallError`] variants have no slot in that shape, so they +/// fold into the method's domain catch-all via `fallback` (reason preserved +/// where the variant carries one). +pub fn encode_versioned_err_payload( + error: CallError, + target_version: u8, + fallback: impl FnOnce(String) -> E::Latest, +) -> Vec +where + E: Versioned + FromLatest + Encode, +{ + encode_versioned_result_payload(flatten_call_error(error, target_version, fallback), 1) } /// Encode `Result<(), _>` for unversioned methods whose success type is unit. @@ -62,13 +71,38 @@ pub fn encode_raw_err_payload(value: T) -> Vec { Err::<(), T>(value).encode() } -/// Encode a versioned subscription interrupt payload from an ordinary error. -pub fn encode_versioned_interrupt_payload(value: T, version: u8) -> Vec { - let encoded = value.encode(); - let mut out = Vec::with_capacity(encoded.len() + 1); - out.push(version_index(version)); - out.extend_from_slice(&encoded); - out +/// Encode a versioned subscription interrupt payload from a handler error. +/// +/// The wire carries the domain envelope directly: `[version][domain bytes]`. +/// Framework-level [`CallError`] variants fold into the method's domain +/// catch-all via `fallback`, as on the request error path. +pub fn encode_versioned_interrupt_payload( + error: CallError, + target_version: u8, + fallback: impl FnOnce(String) -> E::Latest, +) -> Vec +where + E: Versioned + FromLatest + Encode, +{ + flatten_call_error(error, target_version, fallback).encode() +} + +/// Resolve a handler error to the domain envelope that goes on the wire. +fn flatten_call_error( + error: CallError, + target_version: u8, + fallback: impl FnOnce(String) -> E::Latest, +) -> E +where + E: Versioned + FromLatest, +{ + let reason = match error { + CallError::Domain(envelope) => return envelope, + CallError::Denied => "denied".to_string(), + CallError::Unsupported => "unsupported".to_string(), + CallError::MalformedFrame { reason } | CallError::HostFailure { reason } => reason, + }; + E::from_latest(fallback(reason), target_version) } impl Encode for ProtocolMessage { @@ -395,18 +429,82 @@ mod tests { ); } + /// Domain errors encode flat: `[version][Err][domain bytes]`, with no + /// framework tier between the result index and the domain enum. + #[test] + fn encode_versioned_err_payload_encodes_flat_domain_errors() { + let error: truapi::CallError = + truapi::CallError::Domain(truapi::versioned::payment::HostPaymentTopUpError::V1( + truapi::v01::HostPaymentTopUpError::PartialPayment { credited: 5u128 }, + )); + let mut expected = vec![0u8, 1u8]; + truapi::v01::HostPaymentTopUpError::PartialPayment { credited: 5u128 } + .encode_to(&mut expected); + assert_eq!( + encode_versioned_err_payload(error, 1, |reason| { + truapi::v01::HostPaymentTopUpError::Unknown { reason } + }), + expected + ); + } + + /// Framework variants fold into the domain catch-all so the wire never + /// carries a `CallError` discriminant. #[test] - fn encode_versioned_err_payload_wraps_error_values() { + fn encode_versioned_err_payload_folds_framework_errors_into_catch_all() { + let error: truapi::CallError = + truapi::CallError::Denied; let mut expected = vec![0u8, 1u8]; - 9u32.encode_to(&mut expected); - assert_eq!(encode_versioned_err_payload(9u32, 1), expected); + truapi::v01::HostPaymentTopUpError::Unknown { + reason: "denied".into(), + } + .encode_to(&mut expected); + assert_eq!( + encode_versioned_err_payload(error, 1, |reason| { + truapi::v01::HostPaymentTopUpError::Unknown { reason } + }), + expected + ); + } + + /// Interrupt payloads carry the domain envelope directly: + /// `[version][domain bytes]`. + #[test] + fn encode_versioned_interrupt_payload_encodes_flat_domain_errors() { + let error: truapi::CallError< + truapi::versioned::payment::HostPaymentBalanceSubscribeError, + > = truapi::CallError::Domain( + truapi::versioned::payment::HostPaymentBalanceSubscribeError::V1( + truapi::v01::HostPaymentBalanceSubscribeError::PermissionDenied, + ), + ); + assert_eq!( + encode_versioned_interrupt_payload(error, 1, |reason| { + truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason } + }), + vec![0u8, 0u8] + ); } + /// Framework variants fold into the catch-all on the interrupt path too. #[test] - fn encode_versioned_interrupt_payload_wraps_error_values() { - let mut expected = vec![1u8]; - 9u32.encode_to(&mut expected); - assert_eq!(encode_versioned_interrupt_payload(9u32, 2), expected); + fn encode_versioned_interrupt_payload_folds_framework_errors() { + let error: truapi::CallError< + truapi::versioned::payment::HostPaymentBalanceSubscribeError, + > = truapi::CallError::HostFailure { + reason: "unavailable".into(), + }; + let mut expected = vec![0u8]; + truapi::v01::HostPaymentBalanceSubscribeError::Unknown { + reason: "unavailable".into(), + } + .encode_to(&mut expected); + assert_eq!( + encode_versioned_interrupt_payload(error, 1, |reason| { + truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason } + }), + expected + ); } /// IdFactory mints monotonically increasing ids prefixed with the diff --git a/rust/crates/truapi-server/src/generated/dispatcher.rs b/rust/crates/truapi-server/src/generated/dispatcher.rs index 231dd362..26fe2bf1 100644 --- a/rust/crates/truapi-server/src/generated/dispatcher.rs +++ b/rust/crates/truapi-server/src/generated/dispatcher.rs @@ -84,6 +84,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -93,7 +94,11 @@ where match host.get_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -120,6 +125,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -129,7 +135,11 @@ where match host.get_account_alias(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -156,6 +166,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountCreateProofError::Unknown { reason }, )); } }; @@ -165,7 +176,13 @@ where match host.create_account_proof(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostAccountCreateProofError::Unknown { + reason, + }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -192,6 +209,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, )); } }; @@ -201,7 +219,11 @@ where match host.get_legacy_accounts(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostAccountGetError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -228,6 +250,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostGetUserIdError::Unknown { reason }, )); } }; @@ -237,7 +260,11 @@ where match host.get_user_id(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostGetUserIdError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -264,6 +291,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostRequestLoginError::Unknown { reason }, )); } }; @@ -273,7 +301,11 @@ where match host.request_login(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostRequestLoginError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -328,6 +360,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -337,7 +370,11 @@ where match host.get_head_header(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -364,6 +401,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -373,7 +411,11 @@ where match host.get_head_body(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -400,6 +442,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -409,7 +452,11 @@ where match host.get_head_storage(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -436,6 +483,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -445,7 +493,11 @@ where match host.call_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -472,6 +524,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -481,7 +534,11 @@ where match host.unpin_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -508,6 +565,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -517,7 +575,11 @@ where match host.continue_head(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -538,6 +600,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -546,7 +609,7 @@ where let response: versioned::chain::RemoteChainHeadStopOperationResponse = match host.stop_head_operation(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -566,6 +629,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -574,7 +638,7 @@ where let response: versioned::chain::RemoteChainSpecGenesisHashResponse = match host.get_spec_genesis_hash(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -600,6 +664,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -609,7 +674,11 @@ where match host.get_spec_chain_name(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -636,6 +705,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -645,7 +715,11 @@ where match host.get_spec_properties(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -666,6 +740,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -674,7 +749,7 @@ where let response: versioned::chain::RemoteChainTransactionBroadcastResponse = match host.broadcast_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -694,6 +769,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -702,7 +778,7 @@ where let response: versioned::chain::RemoteChainTransactionStopResponse = match host.stop_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -734,18 +810,27 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatCreateRoomError::Unknown { + reason, + }, )); } }; let target_version = request.version(); let cx = CallContext::with_request_id(request_id.clone()); - let response: versioned::chat::HostChatCreateRoomResponse = - match host.create_room(&cx, request).await { - Ok(value) => value, - Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); - } - }; + let response: versioned::chat::HostChatCreateRoomResponse = match host + .create_room(&cx, request) + .await + { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostChatCreateRoomError::Unknown { reason }, + )); + } + }; Ok(encode_versioned_ok_payload(response)) }) }, @@ -770,18 +855,25 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatRegisterBotError::Unknown { reason }, )); } }; let target_version = request.version(); let cx = CallContext::with_request_id(request_id.clone()); - let response: versioned::chat::HostChatRegisterBotResponse = - match host.register_bot(&cx, request).await { - Ok(value) => value, - Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); - } - }; + let response: versioned::chat::HostChatRegisterBotResponse = match host + .register_bot(&cx, request) + .await + { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostChatRegisterBotError::Unknown { reason }, + )); + } + }; Ok(encode_versioned_ok_payload(response)) }) }, @@ -824,18 +916,25 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostChatPostMessageError::Unknown { reason }, )); } }; let target_version = request.version(); let cx = CallContext::with_request_id(request_id.clone()); - let response: versioned::chat::HostChatPostMessageResponse = - match host.post_message(&cx, request).await { - Ok(value) => value, - Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); - } - }; + let response: versioned::chat::HostChatPostMessageResponse = match host + .post_message(&cx, request) + .await + { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostChatPostMessageError::Unknown { reason }, + )); + } + }; Ok(encode_versioned_ok_payload(response)) }) }, @@ -900,6 +999,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -908,7 +1008,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreatePurseResponse = match host.create_purse(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -928,6 +1028,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -936,7 +1037,7 @@ where let response: versioned::coin_payment::HostCoinPaymentQueryPurseResponse = match host.query_purse(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -958,6 +1059,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -966,7 +1068,7 @@ where let stream = match host.rebalance_purse(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -988,6 +1090,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -996,7 +1099,7 @@ where let stream = match host.delete_purse(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -1016,6 +1119,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -1024,7 +1128,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreateReceivableResponse = match host.create_receivable(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -1044,6 +1148,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -1052,7 +1157,7 @@ where let response: versioned::coin_payment::HostCoinPaymentCreateChequeResponse = match host.create_cheque(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(encode_versioned_ok_payload(response)) @@ -1074,6 +1179,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -1082,7 +1188,7 @@ where let stream = match host.deposit(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -1104,6 +1210,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -1112,7 +1219,7 @@ where let stream = match host.refund(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -1134,6 +1241,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |_reason| truapi::v01::CoinPaymentError::Internal, )); } }; @@ -1142,7 +1250,7 @@ where let stream = match host.listen_for_payment(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |_reason| truapi::v01::CoinPaymentError::Internal)); } }; Ok(subscription_stream::(stream)) @@ -1174,18 +1282,25 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostDeriveEntropyError::Unknown { reason }, )); } }; let target_version = request.version(); let cx = CallContext::with_request_id(request_id.clone()); - let response: versioned::entropy::HostDeriveEntropyResponse = - match host.derive(&cx, request).await { - Ok(value) => value, - Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); - } - }; + let response: versioned::entropy::HostDeriveEntropyResponse = match host + .derive(&cx, request) + .await + { + Ok(value) => value, + Err(err) => { + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostDeriveEntropyError::Unknown { reason }, + )); + } + }; Ok(encode_versioned_ok_payload(response)) }) }, @@ -1210,6 +1325,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1218,7 +1334,7 @@ where let response: versioned::local_storage::HostLocalStorageReadResponse = match host.read(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1238,6 +1354,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1246,7 +1363,7 @@ where let response: versioned::local_storage::HostLocalStorageWriteResponse = match host.write(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1266,6 +1383,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason }, )); } }; @@ -1274,7 +1392,7 @@ where let response: versioned::local_storage::HostLocalStorageClearResponse = match host.clear(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostLocalStorageReadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1300,6 +1418,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPushNotificationError::Unknown { reason }, )); } }; @@ -1308,7 +1427,7 @@ where let response: versioned::notifications::HostPushNotificationResponse = match host.send_push_notification(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostPushNotificationError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1328,6 +1447,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1336,7 +1456,7 @@ where let response: versioned::notifications::HostPushNotificationCancelResponse = match host.cancel_push_notification(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1364,6 +1484,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason }, )); } }; @@ -1372,7 +1493,7 @@ where let stream = match host.balance_subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::HostPaymentBalanceSubscribeError::Unknown { reason })); } }; Ok(subscription_stream::(stream)) @@ -1397,6 +1518,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentError::Unknown { reason }, )); } }; @@ -1406,7 +1528,11 @@ where match host.request(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostPaymentError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1429,6 +1555,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentStatusSubscribeError::Unknown { reason }, )); } }; @@ -1437,7 +1564,7 @@ where let stream = match host.status_subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::HostPaymentStatusSubscribeError::Unknown { reason })); } }; Ok(subscription_stream::(stream)) @@ -1463,6 +1590,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostPaymentTopUpError::Unknown { reason }, )); } }; @@ -1472,7 +1600,11 @@ where match host.top_up(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostPaymentTopUpError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1499,6 +1631,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1507,7 +1640,7 @@ where let response: versioned::permissions::HostDevicePermissionResponse = match host.request_device_permission(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1533,6 +1666,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1542,7 +1676,11 @@ where match host.request_remote_permission(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1597,6 +1735,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::PreimageSubmitError::Unknown { reason }, )); } }; @@ -1606,7 +1745,11 @@ where match host.submit(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::PreimageSubmitError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1633,6 +1776,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::ResourceAllocationError::Unknown { reason }, )); } }; @@ -1641,7 +1785,7 @@ where let response: versioned::resource_allocation::HostRequestResourceAllocationResponse = match host.request(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::ResourceAllocationError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1673,6 +1817,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostCreateTransactionError::Unknown { reason }, )); } }; @@ -1682,7 +1827,13 @@ where match host.create_transaction(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostCreateTransactionError::Unknown { + reason, + }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1703,6 +1854,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostCreateTransactionError::Unknown { reason }, )); } }; @@ -1711,7 +1863,7 @@ where let response: versioned::signing::HostCreateTransactionWithLegacyAccountResponse = match host.create_transaction_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostCreateTransactionError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1731,6 +1883,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1739,7 +1892,7 @@ where let response: versioned::signing::HostSignRawWithLegacyAccountResponse = match host.sign_raw_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1759,6 +1912,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1767,7 +1921,7 @@ where let response: versioned::signing::HostSignPayloadWithLegacyAccountResponse = match host.sign_payload_with_legacy_account(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::HostSignPayloadError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1792,6 +1946,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1801,7 +1956,11 @@ where match host.sign_raw(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1828,6 +1987,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, )); } }; @@ -1837,7 +1997,11 @@ where match host.sign_payload(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostSignPayloadError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -1866,6 +2030,7 @@ where return Err(encode_versioned_interrupt_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1874,7 +2039,7 @@ where let stream = match host.subscribe(&cx, request).await { Ok(sub) => sub, Err(err) => { - return Err(encode_versioned_interrupt_payload(err, target_version)); + return Err(encode_versioned_interrupt_payload(err, target_version, |reason| truapi::v01::GenericError { reason })); } }; Ok(subscription_stream::(stream)) @@ -1894,6 +2059,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason }, )); } }; @@ -1902,7 +2068,7 @@ where let response: versioned::statement_store::RemoteStatementStoreCreateProofResponse = match host.create_proof(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1922,6 +2088,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason }, )); } }; @@ -1930,7 +2097,7 @@ where let response: versioned::statement_store::RemoteStatementStoreCreateProofAuthorizedResponse = match host.create_proof_authorized(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::RemoteStatementStoreCreateProofError::Unknown { reason })); } }; Ok(encode_versioned_ok_payload(response)) @@ -1950,6 +2117,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -1958,7 +2126,7 @@ where match host.submit(&cx, request).await { Ok(()) => Ok(encode_versioned_unit_ok_payload(target_version)), Err(err) => { - Ok(encode_versioned_err_payload(err, target_version)) + Ok(encode_versioned_err_payload(err, target_version, |reason| truapi::v01::GenericError { reason })) } } }) @@ -1989,6 +2157,11 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| { + truapi::v01::HostHandshakeError::Unknown( + truapi::v01::GenericError { reason }, + ) + }, )); } }; @@ -1998,7 +2171,15 @@ where match host.handshake(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| { + truapi::v01::HostHandshakeError::Unknown( + truapi::v01::GenericError { reason }, + ) + }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -2025,6 +2206,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::GenericError { reason }, )); } }; @@ -2034,7 +2216,11 @@ where match host.feature_supported(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::GenericError { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) @@ -2061,6 +2247,7 @@ where return Ok(encode_versioned_err_payload( error, ::LATEST, + |reason| truapi::v01::HostNavigateToError::Unknown { reason }, )); } }; @@ -2070,7 +2257,11 @@ where match host.navigate_to(&cx, request).await { Ok(value) => value, Err(err) => { - return Ok(encode_versioned_err_payload(err, target_version)); + return Ok(encode_versioned_err_payload( + err, + target_version, + |reason| truapi::v01::HostNavigateToError::Unknown { reason }, + )); } }; Ok(encode_versioned_ok_payload(response)) diff --git a/rust/crates/truapi-server/src/subscription.rs b/rust/crates/truapi-server/src/subscription.rs index b6766d16..8c8b25e6 100644 --- a/rust/crates/truapi-server/src/subscription.rs +++ b/rust/crates/truapi-server/src/subscription.rs @@ -156,7 +156,7 @@ impl SubscriptionManager { generation, } = token; let rid = request_id.clone(); - let stream_transport = transport.clone(); + let stream_transport = transport; // Cancellation channel. let (cancel_tx, cancel_rx) = futures::channel::oneshot::channel::<()>(); @@ -191,11 +191,11 @@ impl SubscriptionManager { let active = self.active.clone(); let future: BoxFuture<'static, ()> = Box::pin(async move { - let completed = { + { let mut cancel_rx = cancel_rx; loop { match select(cancel_rx, stream.next()).await { - Either::Left((_cancelled, _next)) => break false, + Either::Left((_cancelled, _next)) => break, Either::Right((item, next_cancel_rx)) => { cancel_rx = next_cancel_rx; match item { @@ -216,37 +216,27 @@ impl SubscriptionManager { value, }, }); - break false; + break; } - None => break true, + // Natural end of stream is not signalled on the + // wire; only an explicit interrupt carries a + // terminal frame. + None => break, } } } } - }; + } // Only remove the slot if it still holds THIS generation; a // superseding reservation owns its own cleanup. - let removed = { - let mut active = active.lock().unwrap(); - let owned = matches!( - active.get(&request_id), - Some(Slot::Live { generation: g, .. }) if *g == generation - ); - if owned { - active.remove(&request_id); - } - owned - }; - - if completed && removed { - transport.send(ProtocolMessage { - request_id, - payload: Payload { - id: interrupt_id, - value: Vec::new(), - }, - }); + let mut active = active.lock().unwrap(); + let owned = matches!( + active.get(&request_id), + Some(Slot::Live { generation: g, .. }) if *g == generation + ); + if owned { + active.remove(&request_id); } }); @@ -408,23 +398,25 @@ mod tests { } /// A stream that yields 2 items then ends naturally must produce 2 - /// `_receive` frames followed by one `_interrupt` frame. + /// `_receive` frames and nothing else: natural end of stream is not + /// signalled on the wire. #[test] - fn register_completion_emits_interrupt() { + fn register_completion_emits_no_terminal_frame() { let transport_typed = Arc::new(RecordingTransport::new()); let transport_dyn: Arc = transport_typed.clone(); let manager = SubscriptionManager::new(thread_per_subscription_spawner()); let items = dummy_stream(vec![vec![0xaa], vec![0xbb]]); manager.register("p:1".to_string(), 99, 98, items, transport_dyn); - let observed = transport_typed.wait_for(3, std::time::Duration::from_secs(2)); - assert_eq!(observed, 3, "expected 2 receive frames + 1 interrupt"); + let observed = transport_typed.wait_for(2, std::time::Duration::from_secs(2)); + assert_eq!(observed, 2, "expected exactly 2 receive frames"); + // Give a stray interrupt a chance to arrive before asserting. + std::thread::sleep(std::time::Duration::from_millis(50)); let frames = transport_typed.sent(); + assert_eq!(frames.len(), 2, "natural completion must not emit a frame"); assert_eq!(frames[0].payload.id, 99); assert_eq!(frames[0].payload.value, vec![0xaa]); assert_eq!(frames[1].payload.id, 99); assert_eq!(frames[1].payload.value, vec![0xbb]); - assert_eq!(frames[2].payload.id, 98); - assert_eq!(frames[2].payload.value, Vec::::new()); } /// Calling `handle_stop` twice on the same request id must be a @@ -513,23 +505,19 @@ mod tests { let items = dummy_stream(vec![vec![0xaa]]); manager.register("p:1".to_string(), 99, 98, items, transport_dyn); - // Exactly the second stream's frames appear: one receive + one - // completion interrupt. The first (pending) stream contributes none. - let observed = transport_typed.wait_for(2, std::time::Duration::from_secs(2)); - assert_eq!( - observed, 2, - "expected the second stream's receive + interrupt only" - ); + // Exactly the second stream's frame appears; the first (pending) + // stream contributes none, and natural completion emits nothing. + let observed = transport_typed.wait_for(1, std::time::Duration::from_secs(2)); + assert_eq!(observed, 1, "expected the second stream's receive only"); let frames = transport_typed.sent(); assert_eq!(frames[0].payload.id, 99); assert_eq!(frames[0].payload.value, vec![0xaa]); - assert_eq!(frames[1].payload.id, 98); manager.handle_stop("p:1"); std::thread::sleep(std::time::Duration::from_millis(50)); assert_eq!( transport_typed.sent().len(), - 2, + 1, "no leaked frames from the superseded stream" ); } diff --git a/rust/crates/truapi-server/tests/wire_result_shape.rs b/rust/crates/truapi-server/tests/wire_result_shape.rs index 16deb1f1..dbd77f7f 100644 --- a/rust/crates/truapi-server/tests/wire_result_shape.rs +++ b/rust/crates/truapi-server/tests/wire_result_shape.rs @@ -11,8 +11,8 @@ //! `HostFeatureSupportedResponse`. //! - A `local_storage_read_request` whose stub returns //! `Err(HostLocalStorageReadError::Full)` produces a response whose -//! payload begins with `0x00` (V1), then `0x01` (Err), followed by the encoded -//! `HostLocalStorageReadError::Full`. +//! payload begins with `0x00` (V1), then `0x01` (Err), followed by the flat +//! encoded `HostLocalStorageReadError::Full` (no framework error tier). //! //! Both halves prove the wire layout stays in lockstep with the TS //! `S.indexedTaggedUnion({ V1: S.Result(ok, err) })` codec. @@ -23,7 +23,7 @@ use parity_scale_codec::{Decode, Encode}; use truapi::versioned::system::HostFeatureSupportedRequest; use truapi::versioned::{Versioned, account, payment, statement_store}; -use truapi::{CallError, v01}; +use truapi::v01; use truapi_server::core::TrUApiCore; use truapi_server::frame::{Payload, ProtocolMessage, request_ids, subscription_ids}; @@ -86,15 +86,9 @@ fn local_storage_read_err_response_uses_err_discriminant() { assert_eq!(response.request_id, "p:2"); assert_eq!(response.payload.id, ids.response_id); - // Wire payload: - // [V1 disc=0x00][Err disc=0x01][CallError::Domain][V1 error][encoded error body]. + // Wire payload: [V1 disc=0x00][Err disc=0x01][encoded flat error body]. let mut expected = vec![0x00u8, 0x01u8]; - CallError::Domain( - truapi::versioned::local_storage::HostLocalStorageReadError::V1( - v01::HostLocalStorageReadError::Full, - ), - ) - .encode_to(&mut expected); + v01::HostLocalStorageReadError::Full.encode_to(&mut expected); assert_eq!(response.payload.value, expected); assert_eq!(response.payload.value.first(), Some(&0x00)); assert_eq!(response.payload.value.get(1), Some(&0x01)); @@ -104,8 +98,11 @@ fn versioned_result_err_payload(error: E) -> Vec where E: Clone + Encode + Versioned, { - let mut expected = vec![version_index(error.version()), 0x01u8]; - CallError::Domain(error).encode_to(&mut expected); + // [version][Err][flat domain bytes]: the envelope's own version tag is + // hoisted in front of the result discriminant. + let encoded = error.encode(); + let mut expected = vec![encoded[0], 0x01u8]; + expected.extend_from_slice(&encoded[1..]); expected } @@ -113,9 +110,8 @@ fn versioned_interrupt_err_payload(error: E) -> Vec where E: Clone + Encode + Versioned, { - let mut expected = vec![version_index(error.version())]; - CallError::Domain(error).encode_to(&mut expected); - expected + // Interrupts carry the versioned domain envelope directly. + error.encode() } fn assert_request_returns_domain_error( @@ -180,7 +176,7 @@ fn version_index(version: u8) -> u8 { } #[test] -fn deferred_account_proof_returns_framework_unsupported() { +fn deferred_account_proof_folds_unsupported_into_domain_catch_all() { let core = make_core(); let request = account::HostAccountCreateProofRequest::V1(v01::HostAccountCreateProofRequest { product_account_id: v01::ProductAccountId { @@ -208,7 +204,15 @@ fn deferred_account_proof_returns_framework_unsupported() { ); assert_eq!(response.request_id, "p:account-proof"); assert_eq!(response.payload.id, ids.response_id); - assert_eq!(response.payload.value, vec![0x00u8, 0x01u8, 0x02u8]); + // `CallError::Unsupported` folds into the domain catch-all on the wire. + assert_eq!( + response.payload.value, + versioned_result_err_payload(account::HostAccountCreateProofError::V1( + v01::HostAccountCreateProofError::Unknown { + reason: "unsupported".to_string(), + }, + )), + ); } #[test] @@ -322,15 +326,16 @@ fn malformed_result_subscription_start_interrupts_with_malformed_frame() { assert_eq!(sent.len(), 1); assert_eq!(sent[0].request_id, "p:malformed-sub"); assert_eq!(sent[0].payload.id, ids.interrupt_id); - assert_eq!(sent[0].payload.value.first(), Some(&0x00)); - let mut payload = &sent[0].payload.value[1..]; - let error = CallError::::decode(&mut payload) + let mut payload = &sent[0].payload.value[..]; + let error = payment::HostPaymentBalanceSubscribeError::decode(&mut payload) .expect("decode malformed interrupt error"); assert!(payload.is_empty()); match error { - CallError::MalformedFrame { reason } => assert!(!reason.is_empty()), - other => panic!("expected MalformedFrame interrupt, got {other:?}"), + payment::HostPaymentBalanceSubscribeError::V1( + v01::HostPaymentBalanceSubscribeError::Unknown { reason }, + ) => assert!(!reason.is_empty()), + other => panic!("expected domain catch-all interrupt, got {other:?}"), } } diff --git a/rust/crates/truapi/src/v01/coin_payment.rs b/rust/crates/truapi/src/v01/coin_payment.rs index 71580a6e..da1cd25b 100644 --- a/rust/crates/truapi/src/v01/coin_payment.rs +++ b/rust/crates/truapi/src/v01/coin_payment.rs @@ -70,7 +70,7 @@ pub enum CoinPaymentError { UnsupportedChannel, /// Required host/user-agent capability is unavailable. UserAgentCapabilityUnavailable, - /// Unexpected runtime failure. + /// Unexpected runtime failure, and the reason-less catch-all. Internal, }