walletsuite_tx_compiler/error.rs
1//! Error codes and the [`TxCompilerError`] type.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Discriminator for the class of error raised by the compiler.
7///
8/// The variants use stable string discriminators so downstream consumers
9/// can share audit receipts and error vocabulary across versions.
10#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
12#[non_exhaustive]
13pub enum TxCompilerErrorCode {
14 /// The caller passed an input that failed top-level shape checks.
15 InvalidPayload,
16 /// The chain identifier is not one this crate compiles.
17 UnsupportedChain,
18 /// The transaction type is not recognised.
19 UnsupportedTxType,
20 /// The fee mode is not valid for the target chain.
21 UnsupportedFeeMode,
22 /// An address failed chain-specific validation.
23 InvalidAddress,
24 /// A numeric amount (wei, SUN, gas unit) was malformed.
25 InvalidAmount,
26 /// A mode-required fee parameter was absent.
27 MissingFeeParams,
28 /// The Tron block-header reference is malformed or incomplete.
29 InvalidBlockHeader,
30 /// An EVM ERC-20 calldata payload was malformed.
31 InvalidCalldata,
32 /// A downstream compilation step failed unexpectedly.
33 CompilationFailed,
34}
35
36impl TxCompilerErrorCode {
37 /// Stable string representation used in audit records and error JSON.
38 #[must_use]
39 pub const fn as_str(self) -> &'static str {
40 match self {
41 Self::InvalidPayload => "INVALID_PAYLOAD",
42 Self::UnsupportedChain => "UNSUPPORTED_CHAIN",
43 Self::UnsupportedTxType => "UNSUPPORTED_TX_TYPE",
44 Self::UnsupportedFeeMode => "UNSUPPORTED_FEE_MODE",
45 Self::InvalidAddress => "INVALID_ADDRESS",
46 Self::InvalidAmount => "INVALID_AMOUNT",
47 Self::MissingFeeParams => "MISSING_FEE_PARAMS",
48 Self::InvalidBlockHeader => "INVALID_BLOCK_HEADER",
49 Self::InvalidCalldata => "INVALID_CALLDATA",
50 Self::CompilationFailed => "COMPILATION_FAILED",
51 }
52 }
53}
54
55impl fmt::Display for TxCompilerErrorCode {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(self.as_str())
58 }
59}
60
61/// Structured error returned by every public operation.
62///
63/// The error carries a machine-readable [`TxCompilerErrorCode`], a
64/// human-readable message, and an optional JSON object with debug context.
65///
66/// Only [`TxCompilerError::code`] is part of the stable API: downstream
67/// code should match on `code` for branching logic. The `message` text
68/// is human-readable and MAY change between versions for clarity.
69/// `details` is an unstable debug payload — do not pattern-match on
70/// its keys.
71#[derive(Debug, Clone, Serialize, Deserialize)]
72#[non_exhaustive]
73pub struct TxCompilerError {
74 /// Machine-readable error discriminator.
75 pub code: TxCompilerErrorCode,
76 /// Human-readable description of the failure. Not part of the
77 /// stable API — do not pattern-match on contents.
78 pub message: String,
79 /// Optional structured context useful for debugging. Unstable;
80 /// do not pattern-match on keys.
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub details: Option<serde_json::Value>,
83}
84
85impl TxCompilerError {
86 /// Build an error without a `details` payload.
87 #[must_use]
88 pub fn new(code: TxCompilerErrorCode, message: impl Into<String>) -> Self {
89 Self {
90 code,
91 message: message.into(),
92 details: None,
93 }
94 }
95
96 /// Build an error with a structured `details` payload.
97 #[must_use]
98 pub fn with_details(
99 code: TxCompilerErrorCode,
100 message: impl Into<String>,
101 details: serde_json::Value,
102 ) -> Self {
103 Self {
104 code,
105 message: message.into(),
106 details: Some(details),
107 }
108 }
109}
110
111impl fmt::Display for TxCompilerError {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "{}: {}", self.code, self.message)
114 }
115}
116
117impl std::error::Error for TxCompilerError {}