From f76ee1b5cafccc34566dab7e99a65eaf75721ff3 Mon Sep 17 00:00:00 2001 From: Joe Polny Date: Mon, 18 Aug 2025 10:13:05 -0400 Subject: [PATCH 1/6] feat(transact): migrate from thiserror to snafu --- Cargo.lock | 25 +++- crates/algokit_transact/Cargo.toml | 2 +- crates/algokit_transact/src/address.rs | 30 ++--- crates/algokit_transact/src/error.rs | 60 +++++++--- crates/algokit_transact/src/multisig.rs | 42 +++---- crates/algokit_transact/src/traits.rs | 6 +- .../algokit_transact/src/transactions/mod.rs | 18 +-- crates/algokit_transact/src/utils.rs | 6 +- crates/algokit_transact_ffi/Cargo.toml | 4 +- crates/algokit_transact_ffi/src/lib.rs | 111 ++++++++---------- crates/algokit_transact_ffi/src/multisig.rs | 20 ++-- .../src/transactions/application_call.rs | 14 +-- .../src/transactions/asset_config.rs | 14 +-- .../src/transactions/asset_freeze.rs | 4 +- .../src/transactions/keyreg.rs | 14 +-- 15 files changed, 186 insertions(+), 184 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4f4e62d9..6c596e652 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,7 +115,7 @@ dependencies = [ "serde_repr", "serde_with", "sha2", - "thiserror 2.0.12", + "snafu", ] [[package]] @@ -131,7 +131,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "thiserror 2.0.12", + "snafu", "tsify-next", "uniffi", "wasm-bindgen", @@ -3595,6 +3595,27 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "snafu" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.5.10" diff --git a/crates/algokit_transact/Cargo.toml b/crates/algokit_transact/Cargo.toml index f1198d3ff..85dd66f09 100644 --- a/crates/algokit_transact/Cargo.toml +++ b/crates/algokit_transact/Cargo.toml @@ -21,7 +21,7 @@ serde_json = "1.0.133" serde_repr = "0.1.20" serde_with = "3.11.0" sha2 = { workspace = true } -thiserror = { workspace = true } +snafu = "0.8" [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/crates/algokit_transact/src/address.rs b/crates/algokit_transact/src/address.rs index 5c52c1c4d..8cf85d5c4 100644 --- a/crates/algokit_transact/src/address.rs +++ b/crates/algokit_transact/src/address.rs @@ -64,15 +64,13 @@ impl FromStr for Address { /// or if the checksum does not match. fn from_str(s: &str) -> Result { if s.len() != ALGORAND_ADDRESS_LENGTH { - return Err(AlgoKitTransactError::InvalidAddress( - "Algorand address must be exactly 58 characters".into(), - )); + return Err(AlgoKitTransactError::InvalidAddress { + message: "Algorand address must be exactly 58 characters".into(), + }); } let decoded_address = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, s) - .ok_or_else(|| { - AlgoKitTransactError::InvalidAddress( - "Invalid base32 encoding for Algorand address".into(), - ) + .ok_or_else(|| AlgoKitTransactError::InvalidAddress { + message: "Invalid base32 encoding for Algorand address".into(), })?; // Although this is called public key (and it actually is when the account is a `KeyPairAccount`), @@ -81,24 +79,20 @@ impl FromStr for Address { let pub_key: [u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = decoded_address [..ALGORAND_PUBLIC_KEY_BYTE_LENGTH] .try_into() - .map_err(|_| { - AlgoKitTransactError::InvalidAddress( - "Could not decode address into 32-byte public key".to_string(), - ) + .map_err(|_| AlgoKitTransactError::InvalidAddress { + message: "Could not decode address into 32-byte public key".to_string(), })?; let checksum: [u8; ALGORAND_CHECKSUM_BYTE_LENGTH] = decoded_address [ALGORAND_PUBLIC_KEY_BYTE_LENGTH..] .try_into() - .map_err(|_| { - AlgoKitTransactError::InvalidAddress( - "Could not get 4-byte checksum from decoded address".to_string(), - ) + .map_err(|_| AlgoKitTransactError::InvalidAddress { + message: "Could not get 4-byte checksum from decoded address".to_string(), })?; if pub_key_to_checksum(&pub_key) != checksum { - return Err(AlgoKitTransactError::InvalidAddress( - "Checksum is invalid".to_string(), - )); + return Err(AlgoKitTransactError::InvalidAddress { + message: "Checksum is invalid".to_string(), + }); } Ok(Address(pub_key)) } diff --git a/crates/algokit_transact/src/error.rs b/crates/algokit_transact/src/error.rs index 9997f10b9..f69db83b7 100644 --- a/crates/algokit_transact/src/error.rs +++ b/crates/algokit_transact/src/error.rs @@ -4,35 +4,59 @@ //! transaction processing, including encoding/decoding errors, validation errors, //! and other transaction-related failures. -use thiserror::Error; +use snafu::Snafu; /// Represents errors that can occur during Algorand transaction operations. /// /// This enum encompasses various failure scenarios that may arise when creating, /// manipulating, serializing, or deserializing Algorand transactions. -#[derive(Debug, Error)] +#[derive(Debug, Snafu)] pub enum AlgoKitTransactError { - #[error("Error ocurred during encoding: {0}")] - EncodingError(#[from] rmp_serde::encode::Error), + #[snafu(display("Error ocurred during encoding: {source}"))] + EncodingError { source: rmp_serde::encode::Error }, - #[error("Error ocurred during decoding: {0}")] - DecodingError(#[from] rmp_serde::decode::Error), + #[snafu(display("Error ocurred during decoding: {source}"))] + DecodingError { source: rmp_serde::decode::Error }, - #[error("Error ocurred during msgpack encoding: {0}")] - MsgpackEncodingError(#[from] rmpv::encode::Error), + #[snafu(display("Error ocurred during msgpack encoding: {source}"))] + MsgpackEncodingError { source: rmpv::encode::Error }, - #[error("Error ocurred during msgpack decoding: {0}")] - MsgpackDecodingError(#[from] rmpv::decode::Error), + #[snafu(display("Error ocurred during msgpack decoding: {source}"))] + MsgpackDecodingError { source: rmpv::decode::Error }, - #[error("Unknown transaction type: {0}")] - UnknownTransactionType(String), + #[snafu(display("Unknown transaction type: {message}"))] + UnknownTransactionType { message: String }, - #[error("{0}")] - InputError(String), + #[snafu(display("{message}"))] + InputError { message: String }, - #[error("{0}")] - InvalidAddress(String), + #[snafu(display("{message}"))] + InvalidAddress { message: String }, - #[error("Invalid multisig signature: {0}")] - InvalidMultisigSignature(String), + #[snafu(display("Invalid multisig signature: {message}"))] + InvalidMultisigSignature { message: String }, +} + +impl From for AlgoKitTransactError { + fn from(source: rmp_serde::encode::Error) -> Self { + AlgoKitTransactError::EncodingError { source } + } +} + +impl From for AlgoKitTransactError { + fn from(source: rmp_serde::decode::Error) -> Self { + AlgoKitTransactError::DecodingError { source } + } +} + +impl From for AlgoKitTransactError { + fn from(source: rmpv::encode::Error) -> Self { + AlgoKitTransactError::MsgpackEncodingError { source } + } +} + +impl From for AlgoKitTransactError { + fn from(source: rmpv::decode::Error) -> Self { + AlgoKitTransactError::MsgpackDecodingError { source } + } } diff --git a/crates/algokit_transact/src/multisig.rs b/crates/algokit_transact/src/multisig.rs index 3b39c3332..057049811 100644 --- a/crates/algokit_transact/src/multisig.rs +++ b/crates/algokit_transact/src/multisig.rs @@ -77,21 +77,17 @@ impl MultisigSignature { subsignatures: Vec, ) -> Result { if version == 0 { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Version cannot be zero".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Version cannot be zero".to_string(), + }); } if subsignatures.is_empty() { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Subsignatures cannot be empty".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Subsignatures cannot be empty".to_string(), + }); } if threshold == 0 || threshold as usize > subsignatures.len() { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Threshold must be greater than zero and less than or equal \ - to the number of sub-signers" - .to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { message: "Threshold must be greater than zero and less than or equal to the number of sub-signers".to_string() }); } Ok(Self { version, @@ -141,9 +137,9 @@ impl MultisigSignature { subsig.signature = Some(subsignature); } if !found { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Address not found in multisig signature".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Address not found in multisig signature".to_string(), + }); } Ok(Self { @@ -164,19 +160,19 @@ impl MultisigSignature { /// Returns [`AlgoKitTransactError::InvalidMultisigSignature`] if the version, threshold, or participants differ. pub fn merge(&self, other: &Self) -> Result { if self.version != other.version { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Cannot merge multisig signatures with different versions".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Cannot merge multisig signatures with different versions".to_string(), + }); } if self.threshold != other.threshold { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Cannot merge multisig signatures with different thresholds".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Cannot merge multisig signatures with different thresholds".to_string(), + }); } if self.participants() != other.participants() { - return Err(AlgoKitTransactError::InvalidMultisigSignature( - "Cannot merge multisig signatures with different participants".to_string(), - )); + return Err(AlgoKitTransactError::InvalidMultisigSignature { + message: "Cannot merge multisig signatures with different participants".to_string(), + }); } let subsignatures = self diff --git a/crates/algokit_transact/src/traits.rs b/crates/algokit_transact/src/traits.rs index fdd943fbd..4ebcf254f 100644 --- a/crates/algokit_transact/src/traits.rs +++ b/crates/algokit_transact/src/traits.rs @@ -62,9 +62,9 @@ pub trait AlgorandMsgpack: Serialize + for<'de> Deserialize<'de> { /// deserialization fails. fn decode(bytes: &[u8]) -> Result { if bytes.is_empty() { - return Err(AlgoKitTransactError::InputError( - "attempted to decode 0 bytes".to_string(), - )); + return Err(AlgoKitTransactError::InputError { + message: "attempted to decode 0 bytes".to_string(), + }); } // If there is a PREFIX defined, bytes is longer than the prefix, and the bytes start diff --git a/crates/algokit_transact/src/transactions/mod.rs b/crates/algokit_transact/src/transactions/mod.rs index f47c1f07a..293ae31be 100644 --- a/crates/algokit_transact/src/transactions/mod.rs +++ b/crates/algokit_transact/src/transactions/mod.rs @@ -115,10 +115,10 @@ impl Transaction { if let Some(max_fee) = request.max_fee { if calculated_fee > max_fee { - return Err(AlgoKitTransactError::InputError(format!( + return Err(AlgoKitTransactError::InputError { message: format!( "Transaction fee {} µALGO is greater than max fee {} µALGO", calculated_fee, max_fee - ))); + )}); } } @@ -205,10 +205,10 @@ impl AlgorandMsgpack for SignedTransaction { Ok(stxn) } - _ => Err(AlgoKitTransactError::InputError(format!( + _ => Err(AlgoKitTransactError::InputError { message: format!( "expected signed transaction to be a map, but got a: {:#?}", value.type_id() - ))), + )}), } } } @@ -235,16 +235,16 @@ impl Transactions for &[Transaction] { /// A result containing the transactions with group assign or an error if grouping fails. fn assign_group(self) -> Result, AlgoKitTransactError> { if self.len() > MAX_TX_GROUP_SIZE { - return Err(AlgoKitTransactError::InputError(format!( + return Err(AlgoKitTransactError::InputError { message: format!( "Transaction group size exceeds the max limit of {}", MAX_TX_GROUP_SIZE - ))); + )}); } if self.is_empty() { - return Err(AlgoKitTransactError::InputError(String::from( - "Transaction group size cannot be 0", - ))); + return Err(AlgoKitTransactError::InputError { + message: String::from("Transaction group size cannot be 0"), + }); } let group_id = compute_group_id(self)?; diff --git a/crates/algokit_transact/src/utils.rs b/crates/algokit_transact/src/utils.rs index da615ce15..3b14126ff 100644 --- a/crates/algokit_transact/src/utils.rs +++ b/crates/algokit_transact/src/utils.rs @@ -98,9 +98,9 @@ pub fn compute_group_id(txs: &[Transaction]) -> Result for JsValue { impl From for AlgoKitTransactError { fn from(e: algokit_transact::AlgoKitTransactError) -> Self { match e { - algokit_transact::AlgoKitTransactError::DecodingError(_) => { - AlgoKitTransactError::DecodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::DecodingError { .. } => { + AlgoKitTransactError::DecodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::EncodingError(_) => { - AlgoKitTransactError::EncodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::EncodingError { .. } => { + AlgoKitTransactError::EncodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::MsgpackDecodingError(_) => { - AlgoKitTransactError::DecodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::MsgpackDecodingError { .. } => { + AlgoKitTransactError::DecodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::MsgpackEncodingError(_) => { - AlgoKitTransactError::EncodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::MsgpackEncodingError { .. } => { + AlgoKitTransactError::EncodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::UnknownTransactionType(_) => { - AlgoKitTransactError::DecodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::UnknownTransactionType { .. } => { + AlgoKitTransactError::DecodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::InputError(e) => { - AlgoKitTransactError::InputError(e.to_string()) + algokit_transact::AlgoKitTransactError::InputError { message } => { + AlgoKitTransactError::InputError { message } } - algokit_transact::AlgoKitTransactError::InvalidAddress(_) => { - AlgoKitTransactError::DecodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::InvalidAddress { .. } => { + AlgoKitTransactError::DecodingError { message: e.to_string() } } - algokit_transact::AlgoKitTransactError::InvalidMultisigSignature(_) => { - AlgoKitTransactError::DecodingError(e.to_string()) + algokit_transact::AlgoKitTransactError::InvalidMultisigSignature { .. } => { + AlgoKitTransactError::DecodingError { message: e.to_string() } } } } @@ -136,12 +138,10 @@ impl TryFrom for algokit_transact::KeyPairAccount { fn try_from(value: KeyPairAccount) -> Result { let pub_key: [u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = bytebuf_to_bytes(&value.pub_key) - .map_err(|e| { - AlgoKitTransactError::DecodingError(format!( - "Error while decoding a public key: {}", - e - )) - })?; + .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( + "Error while decoding a public key: {}", + e + )})?; Ok(algokit_transact::KeyPairAccount::from_pubkey(&pub_key)) } @@ -254,9 +254,7 @@ impl TryFrom for algokit_transact::Transaction { .count() > 1 { - return Err(Self::Error::DecodingError( - "Multiple transaction type specific fields set".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Multiple transaction type specific fields set".to_string() }); } match tx.transaction_type { @@ -325,9 +323,7 @@ impl TryFrom for algokit_transact::PaymentTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::Payment || tx.payment.is_none() { - return Err(Self::Error::DecodingError( - "Payment data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Payment data missing".to_string() }); } let data = tx.clone().payment.unwrap(); @@ -362,9 +358,7 @@ impl TryFrom for algokit_transact::AssetTransferTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetTransfer || tx.asset_transfer.is_none() { - return Err(Self::Error::DecodingError( - "Asset Transfer data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Asset Transfer data missing".to_string() }); } let data = tx.clone().asset_transfer.unwrap(); @@ -507,12 +501,10 @@ impl TryFrom for algokit_transact::SignedTransaction { .signature .map(|sig| bytebuf_to_bytes(&sig)) .transpose() - .map_err(|e| { - AlgoKitTransactError::DecodingError(format!( - "Error while decoding the signature in a signed transaction: {}", - e - )) - })?, + .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( + "Error while decoding the signature in a signed transaction: {}", + e + )})?, auth_address: signed_tx .auth_address .map(|addr| addr.parse()) @@ -526,12 +518,10 @@ impl TryFrom for algokit_transact::SignedTransaction { } fn bytebuf_to_bytes(buf: &ByteBuf) -> Result<[u8; N], AlgoKitTransactError> { - buf.to_vec().try_into().map_err(|_| { - AlgoKitTransactError::DecodingError(format!( - "Expected {} bytes but got a different length", - N - )) - }) + buf.to_vec().try_into().map_err(|_| AlgoKitTransactError::DecodingError { message: format!( + "Expected {} bytes but got a different length", + N + )}) } fn byte32_to_bytebuf(b32: Byte32) -> ByteBuf { @@ -687,9 +677,7 @@ pub fn decode_transactions( #[ffi_func] pub fn estimate_transaction_size(transaction: Transaction) -> Result { let core_tx: algokit_transact::Transaction = transaction.try_into()?; - core_tx.estimate_size()?.try_into().map_err(|_| { - AlgoKitTransactError::EncodingError("Failed to convert size to u64".to_string()) - }) + core_tx.estimate_size()?.try_into().map_err(|_| AlgoKitTransactError::EncodingError { message: "Failed to convert size to u64".to_string() }) } #[ffi_func] @@ -698,13 +686,10 @@ pub fn keypair_account_from_pub_key( ) -> Result { Ok( algokit_transact::KeyPairAccount::from_pubkey(pub_key.try_into().map_err(|_| { - AlgoKitTransactError::EncodingError( - format!( - "public key should be {} bytes", - ALGORAND_PUBLIC_KEY_BYTE_LENGTH - ) - .to_string(), - ) + AlgoKitTransactError::EncodingError { message: format!( + "public key should be {} bytes", + ALGORAND_PUBLIC_KEY_BYTE_LENGTH + ) } })?) .into(), ) diff --git a/crates/algokit_transact_ffi/src/multisig.rs b/crates/algokit_transact_ffi/src/multisig.rs index 3ae1dfd36..181720d30 100644 --- a/crates/algokit_transact_ffi/src/multisig.rs +++ b/crates/algokit_transact_ffi/src/multisig.rs @@ -69,12 +69,10 @@ impl TryFrom for algokit_transact::MultisigSubsignature { .signature .map(|sig| bytebuf_to_bytes(&sig)) .transpose() - .map_err(|e| { - AlgoKitTransactError::DecodingError(format!( - "Error while decoding a subsignature: {}", - e - )) - })?, + .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( + "Error while decoding a subsignature: {}", + e + )})?, }) } } @@ -143,12 +141,10 @@ pub fn apply_multisig_subsignature( let multisignature: algokit_transact::MultisigSignature = multisig_signature.try_into()?; let partially_signed_multisignature = multisignature.apply_subsignature( participant.parse()?, - subsignature.try_into().map_err(|_| { - AlgoKitTransactError::EncodingError(format!( - "signature should be {} bytes", - ALGORAND_SIGNATURE_BYTE_LENGTH - )) - })?, + subsignature.try_into().map_err(|_| AlgoKitTransactError::EncodingError { message: format!( + "signature should be {} bytes", + ALGORAND_SIGNATURE_BYTE_LENGTH + )})?, )?; Ok(partially_signed_multisignature.into()) } diff --git a/crates/algokit_transact_ffi/src/transactions/application_call.rs b/crates/algokit_transact_ffi/src/transactions/application_call.rs index 271c2f399..1f316409b 100644 --- a/crates/algokit_transact_ffi/src/transactions/application_call.rs +++ b/crates/algokit_transact_ffi/src/transactions/application_call.rs @@ -101,9 +101,7 @@ impl TryFrom for algokit_transact::ApplicationCallTransactionFields fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::ApplicationCall || tx.application_call.is_none() { - return Err(Self::Error::DecodingError( - "Application call data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Application call data missing".to_string() }); } let data = tx.clone().application_call.unwrap(); @@ -137,12 +135,10 @@ impl TryFrom for algokit_transact::ApplicationCallTransactionFields .map(|boxes| boxes.into_iter().map(Into::into).collect()), }; - transaction_fields.validate().map_err(|errors| { - AlgoKitTransactError::DecodingError(format!( - "Application call validation failed: {}", - errors.join("\n") - )) - })?; + transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( + "Application call validation failed: {}", + errors.join("\n") + )})?; Ok(transaction_fields) } diff --git a/crates/algokit_transact_ffi/src/transactions/asset_config.rs b/crates/algokit_transact_ffi/src/transactions/asset_config.rs index 781b34e03..282e6e47c 100644 --- a/crates/algokit_transact_ffi/src/transactions/asset_config.rs +++ b/crates/algokit_transact_ffi/src/transactions/asset_config.rs @@ -138,9 +138,7 @@ impl TryFrom for algokit_transact::AssetConfigTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetConfig || tx.asset_config.is_none() { - return Err(Self::Error::DecodingError( - "Asset configuration data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Asset configuration data missing".to_string() }); } let data = tx.clone().asset_config.unwrap(); @@ -167,12 +165,10 @@ impl TryFrom for algokit_transact::AssetConfigTransactionFields { clawback: data.clawback.map(|addr| addr.parse()).transpose()?, }; - transaction_fields.validate().map_err(|errors| { - AlgoKitTransactError::DecodingError(format!( - "Asset config validation failed: {}", - errors.join("\n") - )) - })?; + transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( + "Asset config validation failed: {}", + errors.join("\n") + )})?; Ok(transaction_fields) } diff --git a/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs b/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs index 6515499cf..77c5a9bbf 100644 --- a/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs +++ b/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs @@ -34,9 +34,7 @@ impl TryFrom for algokit_transact::AssetFreezeTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetFreeze || tx.asset_freeze.is_none() { - return Err(Self::Error::DecodingError( - "Asset Freeze data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Asset Freeze data missing".to_string() }); } let data = tx.clone().asset_freeze.unwrap(); diff --git a/crates/algokit_transact_ffi/src/transactions/keyreg.rs b/crates/algokit_transact_ffi/src/transactions/keyreg.rs index 3fa238480..67be468b6 100644 --- a/crates/algokit_transact_ffi/src/transactions/keyreg.rs +++ b/crates/algokit_transact_ffi/src/transactions/keyreg.rs @@ -54,9 +54,7 @@ impl TryFrom for algokit_transact::KeyRegistrationTransactio if tx.transaction_type != crate::TransactionType::KeyRegistration || tx.key_registration.is_none() { - return Err(Self::Error::DecodingError( - "Key Registration data missing".to_string(), - )); + return Err(Self::Error::DecodingError { message: "Key Registration data missing".to_string() }); } let data = tx.clone().key_registration.unwrap(); @@ -82,12 +80,10 @@ impl TryFrom for algokit_transact::KeyRegistrationTransactio non_participation: data.non_participation, }; - transaction_fields.validate().map_err(|errors| { - AlgoKitTransactError::DecodingError(format!( - "Key registration validation failed: {}", - errors.join("\n") - )) - })?; + transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( + "Key registration validation failed: {}", + errors.join("\n") + )})?; Ok(transaction_fields) } From 2fe4cca5097bb5bddd1c57ed614c98b12af6f546 Mon Sep 17 00:00:00 2001 From: Joe Polny Date: Mon, 18 Aug 2025 10:51:19 -0400 Subject: [PATCH 2/6] feat: migrate other crates from thiserror to snafu --- Cargo.lock | 6 +- Cargo.toml | 2 +- crates/algokit_abi/Cargo.toml | 2 +- crates/algokit_abi/src/abi_type.rs | 95 ++++++----- crates/algokit_abi/src/arc56_contract.rs | 30 ++-- crates/algokit_abi/src/error.rs | 16 +- crates/algokit_abi/src/method.rs | 40 ++--- .../src/types/collections/array_dynamic.rs | 16 +- .../src/types/collections/array_static.rs | 12 +- .../src/types/collections/tuple.rs | 68 +++----- .../src/types/primitives/address.rs | 28 ++- .../algokit_abi/src/types/primitives/bool.rs | 20 +-- .../algokit_abi/src/types/primitives/byte.rs | 16 +- .../src/types/primitives/string.rs | 22 +-- .../src/types/primitives/ufixed.rs | 20 +-- .../algokit_abi/src/types/primitives/uint.rs | 20 +-- crates/algokit_http_client/Cargo.toml | 2 +- crates/algokit_http_client/src/lib.rs | 23 +-- crates/algokit_utils/Cargo.toml | 2 +- .../algokit_utils/src/clients/app_manager.rs | 55 +++--- .../src/clients/asset_manager.rs | 41 ++--- .../src/testing/indexer_helpers.rs | 13 +- .../src/transactions/application_call.rs | 26 ++- .../src/transactions/composer.rs | 161 +++++++++--------- .../algokit_utils/src/transactions/creator.rs | 8 +- .../algokit_utils/src/transactions/sender.rs | 79 +++++---- .../src/transactions/sender_results.rs | 49 +++--- 27 files changed, 399 insertions(+), 473 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c596e652..5a05c8a3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.12", + "snafu", ] [[package]] @@ -84,7 +84,7 @@ dependencies = [ "reqwest", "serde", "serde-wasm-bindgen", - "thiserror 2.0.12", + "snafu", "tsify-next", "uniffi", "wasm-bindgen", @@ -167,7 +167,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.12", + "snafu", "tokio", "tokio-test", ] diff --git a/Cargo.toml b/Cargo.toml index 3857dcb85..f1027dd55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ [workspace.dependencies] uniffi = { version = "0.28.3" } -thiserror = { version = "2.0.7" } +snafu = { version = "0.8" } wasm-bindgen = { version = "0.2.99" } tsify-next = { version = "0.5.4", features = ["js"] } js-sys = { version = "0.3.77" } diff --git a/crates/algokit_abi/Cargo.toml b/crates/algokit_abi/Cargo.toml index 5aaac19e3..92f5a2978 100644 --- a/crates/algokit_abi/Cargo.toml +++ b/crates/algokit_abi/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -thiserror = { workspace = true } +snafu = { workspace = true } num-bigint = "0.4" regex = "1.0" base32 = { workspace = true } diff --git a/crates/algokit_abi/src/abi_type.rs b/crates/algokit_abi/src/abi_type.rs index 0ebb84635..8de6c2920 100644 --- a/crates/algokit_abi/src/abi_type.rs +++ b/crates/algokit_abi/src/abi_type.rs @@ -25,10 +25,10 @@ impl BitSize { /// A new [`BitSize`] if valid, or an [`ABIError`] if invalid. pub fn new(bits: u16) -> Result { if bits < BITS_PER_BYTE as u16 || bits > MAX_BIT_SIZE || bits % BITS_PER_BYTE as u16 != 0 { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Bit size must be between {} and {} and divisible by {}, got {}", BITS_PER_BYTE, MAX_BIT_SIZE, BITS_PER_BYTE, bits - ))); + )}); } Ok(BitSize(bits)) } @@ -56,10 +56,10 @@ impl Precision { /// A new [`Precision`] if valid, or an [`ABIError`] if invalid. pub fn new(precision: u8) -> Result { if precision > MAX_PRECISION { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Precision must be between 0 and {}, got {}", MAX_PRECISION, precision - ))); + )}); } Ok(Precision(precision)) } @@ -186,14 +186,14 @@ impl ABIType { } Ok(size) } - ABIType::String => Err(ABIError::DecodingError(format!( + ABIType::String => Err(ABIError::DecodingError { message: format!( "Failed to get size, {} is a dynamic type", abi_type - ))), - ABIType::DynamicArray(_) => Err(ABIError::DecodingError(format!( + )}), + ABIType::DynamicArray(_) => Err(ABIError::DecodingError { message: format!( "Failed to get size, {} is a dynamic type", abi_type - ))), + )}), } } } @@ -240,33 +240,43 @@ impl FromStr for ABIType { let element_type_str = &captures[1]; let length_str = &captures[2]; - let length = length_str.parse::().map_err(|_| { - ABIError::ValidationError(format!("Invalid array length: {}", length_str)) - })?; + let length = length_str + .parse::() + .map_err(|_| { + ABIError::ValidationError { message: format!( + "Invalid array length: {}", + length_str + ) } + })?; let element_type = ABIType::from_str(element_type_str)?; return Ok(ABIType::StaticArray(Box::new(element_type), length)); } else { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Malformed static array string: {}", s - ))); + )}); } } // Uint type if let Some(size_str) = s.strip_prefix("uint") { if size_str.chars().all(|c| c.is_ascii_digit()) { - let size = size_str.parse::().map_err(|_| { - ABIError::ValidationError(format!("Invalid uint size: {}", size_str)) - })?; + let size = size_str + .parse::() + .map_err(|_| { + ABIError::ValidationError { message: format!( + "Invalid uint size: {}", + size_str + ) } + })?; let bit_size = BitSize::new(size)?; return Ok(ABIType::Uint(bit_size)); } else { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Malformed uint string: {}", size_str - ))); + )}); } } @@ -277,24 +287,31 @@ impl FromStr for ABIType { let size_str = &captures[1]; let precision_str = &captures[2]; - let size = size_str.parse::().map_err(|_| { - ABIError::ValidationError(format!("Invalid ufixed size: {}", size_str)) - })?; - let precision = precision_str.parse::().map_err(|_| { - ABIError::ValidationError(format!( - "Invalid ufixed precision: {}", - precision_str - )) - })?; + let size = size_str + .parse::() + .map_err(|_| { + ABIError::ValidationError { message: format!( + "Invalid ufixed size: {}", + size_str + ) } + })?; + let precision = precision_str + .parse::() + .map_err(|_| { + ABIError::ValidationError { message: format!( + "Invalid ufixed precision: {}", + precision_str + ) } + })?; let bit_size = BitSize::new(size)?; let precision = Precision::new(precision)?; return Ok(ABIType::UFixed(bit_size, precision)); } else { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Malformed ufixed type: {}", s - ))); + )}); } } @@ -315,10 +332,10 @@ impl FromStr for ABIType { "bool" => Ok(ABIType::Bool), "address" => Ok(ABIType::Address), "string" => Ok(ABIType::String), - _ => Err(ABIError::ValidationError(format!( + _ => Err(ABIError::ValidationError { message: format!( "Cannot convert string '{}' to an ABI type", s - ))), + )}), } } } @@ -329,19 +346,13 @@ pub(crate) fn parse_tuple_content(content: &str) -> Result, ABIError } if content.starts_with(",") { - return Err(ABIError::ValidationError( - "Tuple name should not start with comma".to_string(), - )); + return Err(ABIError::ValidationError { message: "Tuple name should not start with comma".to_string() }); } if content.ends_with(",") { - return Err(ABIError::ValidationError( - "Tuple name should not start with comma".to_string(), - )); + return Err(ABIError::ValidationError { message: "Tuple name should not start with comma".to_string() }); } if content.contains(",,") { - return Err(ABIError::ValidationError( - "tuple string should not have consecutive commas".to_string(), - )); + return Err(ABIError::ValidationError { message: "tuple string should not have consecutive commas".to_string() }); } let mut tuple_strings: Vec = Vec::new(); @@ -365,9 +376,7 @@ pub(crate) fn parse_tuple_content(content: &str) -> Result, ABIError tuple_strings.push(word); } if depth != 0 { - return Err(ABIError::ValidationError( - "Tuple string has mismatched parentheses".to_string(), - )); + return Err(ABIError::ValidationError { message: "Tuple string has mismatched parentheses".to_string() }); } Ok(tuple_strings) diff --git a/crates/algokit_abi/src/arc56_contract.rs b/crates/algokit_abi/src/arc56_contract.rs index b194fca51..f20b80dcb 100644 --- a/crates/algokit_abi/src/arc56_contract.rs +++ b/crates/algokit_abi/src/arc56_contract.rs @@ -146,7 +146,7 @@ impl Source { fn decode_source(&self, b64_text: &str) -> Result { let decoded = general_purpose::STANDARD .decode(b64_text) - .map_err(|e| ABIError::ValidationError(format!("Failed to decode base64: {}", e)))?; + .map_err(|e| ABIError::ValidationError { message: format!("Failed to decode base64: {}", e) })?; Ok(String::from_utf8_lossy(&decoded).to_string()) } } @@ -515,7 +515,7 @@ impl Arc56Contract { /// Create Arc56Contract from JSON string pub fn from_json(json_str: &str) -> Result { serde_json::from_str(json_str) - .map_err(|e| ABIError::ValidationError(format!("Failed to parse ARC-56 JSON: {}", e))) + .map_err(|e| ABIError::ValidationError { message: format!("Failed to parse ARC-56 JSON: {}", e) }) } /// Convert Arc56Contract to JSON string with optional indentation @@ -527,16 +527,16 @@ impl Arc56Contract { None => { // Compact JSON serde_json::to_string(self).map_err(|e| { - ABIError::EncodingError(format!("Failed to serialize ARC-56 to JSON: {}", e)) + ABIError::EncodingError { message: format!("Failed to serialize ARC-56 to JSON: {}", e) } }) } Some(0) => { // Pretty JSON with default formatting serde_json::to_string_pretty(self).map_err(|e| { - ABIError::EncodingError(format!( + ABIError::EncodingError { message: format!( "Failed to serialize ARC-56 to pretty JSON: {}", e - )) + ) } }) } Some(indent_size) => { @@ -546,16 +546,16 @@ impl Arc56Contract { let mut buf = Vec::new(); let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); self.serialize(&mut ser).map_err(|e| { - ABIError::EncodingError(format!( + ABIError::EncodingError { message: format!( "Failed to serialize ARC-56 with indent: {}", e - )) + ) } })?; String::from_utf8(buf).map_err(|e| { - ABIError::EncodingError(format!( + ABIError::EncodingError { message: format!( "Failed to convert serialized JSON to string: {}", e - )) + ) } }) } } @@ -572,23 +572,23 @@ impl Arc56Contract { .collect(); if methods.is_empty() { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Unable to find method {} in {} app", method_name_or_signature, self.name - ))); + )}); } if methods.len() > 1 { let signatures: Result, ABIError> = methods.iter().map(|m| m.get_signature()).collect(); let signatures = signatures?; - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Received a call to method {} in contract {}, but this resolved to multiple methods; \ please pass in an ABI signature instead: {}", method_name_or_signature, self.name, signatures.join(", ") - ))); + )}); } Ok(methods[0]) @@ -601,10 +601,10 @@ impl Arc56Contract { .is_ok_and(|sig| sig == method_name_or_signature) }) .ok_or_else(|| { - ABIError::ValidationError(format!( + ABIError::ValidationError { message: format!( "Unable to find method {} in {} app", method_name_or_signature, self.name - )) + ) } }) } } diff --git a/crates/algokit_abi/src/error.rs b/crates/algokit_abi/src/error.rs index 5895021fe..fe875ea4c 100644 --- a/crates/algokit_abi/src/error.rs +++ b/crates/algokit_abi/src/error.rs @@ -1,17 +1,17 @@ -use thiserror::Error; +use snafu::Snafu; /// Represents an error that can occur during ABI operations. -#[derive(Debug, Error)] +#[derive(Debug, Snafu)] pub enum ABIError { /// An error that occurs during ABI type validation. - #[error("ABI validation failed: {0}")] - ValidationError(String), + #[snafu(display("ABI validation failed: {message}"))] + ValidationError { message: String }, /// An error that occurs during ABI encoding. - #[error("ABI encoding failed: {0}")] - EncodingError(String), + #[snafu(display("ABI encoding failed: {message}"))] + EncodingError { message: String }, /// An error that occurs during ABI decoding. - #[error("ABI decoding failed: {0}")] - DecodingError(String), + #[snafu(display("ABI decoding failed: {message}"))] + DecodingError { message: String }, } diff --git a/crates/algokit_abi/src/method.rs b/crates/algokit_abi/src/method.rs index 163f7eb52..d2d939f36 100644 --- a/crates/algokit_abi/src/method.rs +++ b/crates/algokit_abi/src/method.rs @@ -39,10 +39,10 @@ impl FromStr for ABITransactionType { "axfer" => Ok(ABITransactionType::AssetTransfer), "afrz" => Ok(ABITransactionType::AssetFreeze), "appl" => Ok(ABITransactionType::ApplicationCall), - _ => Err(ABIError::ValidationError(format!( + _ => Err(ABIError::ValidationError { message: format!( "Invalid transaction type: {}", s - ))), + )}), } } } @@ -92,10 +92,10 @@ impl FromStr for ABIReferenceType { "account" => Ok(ABIReferenceType::Account), "application" => Ok(ABIReferenceType::Application), "asset" => Ok(ABIReferenceType::Asset), - _ => Err(ABIError::ValidationError(format!( + _ => Err(ABIError::ValidationError { message: format!( "Invalid reference type: {}", s - ))), + )}), } } } @@ -227,9 +227,7 @@ impl ABIMethod { pub fn selector(&self) -> Result, ABIError> { let signature = self.signature()?; if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError( - "Method signature cannot contain whitespace".to_string(), - )); + return Err(ABIError::ValidationError { message: "Method signature cannot contain whitespace".to_string() }); } let mut hasher = Sha512_256::new(); @@ -242,9 +240,7 @@ impl ABIMethod { /// Returns the method signature as a string. pub fn signature(&self) -> Result { if self.name.is_empty() { - return Err(ABIError::ValidationError( - "Method name cannot be empty".to_string(), - )); + return Err(ABIError::ValidationError { message: "Method name cannot be empty".to_string() }); } let arg_types: Vec = self @@ -272,9 +268,7 @@ impl ABIMethod { let signature = format!("{}({}){}", self.name, args_str, return_type); if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError( - "Generated signature contains whitespace".to_string(), - )); + return Err(ABIError::ValidationError { message: "Generated signature contains whitespace".to_string() }); } Ok(signature) @@ -286,21 +280,15 @@ impl FromStr for ABIMethod { fn from_str(signature: &str) -> Result { if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError( - "Method signature cannot contain whitespace".to_string(), - )); + return Err(ABIError::ValidationError { message: "Method signature cannot contain whitespace".to_string() }); } let open_paren_pos = signature.find('(').ok_or_else(|| { - ABIError::ValidationError( - "Method signature must contain opening parenthesis".to_string(), - ) + ABIError::ValidationError { message: "Method signature must contain opening parenthesis".to_string() } })?; if open_paren_pos == 0 { - return Err(ABIError::ValidationError( - "Method name cannot be empty".to_string(), - )); + return Err(ABIError::ValidationError { message: "Method name cannot be empty".to_string() }); } let method_name = signature[..open_paren_pos].to_string(); @@ -387,9 +375,7 @@ fn find_matching_closing_paren(s: &str, open_pos: usize) -> Result Result, ABIError> { // Additional validation for method arguments: no empty arguments for arg in &arguments { if arg.trim().is_empty() { - return Err(ABIError::ValidationError( - "Empty argument in method signature".to_string(), - )); + return Err(ABIError::ValidationError { message: "Empty argument in method signature".to_string() }); } } diff --git a/crates/algokit_abi/src/types/collections/array_dynamic.rs b/crates/algokit_abi/src/types/collections/array_dynamic.rs index 0e03f99db..34000b185 100644 --- a/crates/algokit_abi/src/types/collections/array_dynamic.rs +++ b/crates/algokit_abi/src/types/collections/array_dynamic.rs @@ -9,18 +9,14 @@ impl ABIType { let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected an array of values".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); } }; let child_type = match self { ABIType::DynamicArray(child_type) => child_type, _ => { - return Err(ABIError::EncodingError( - "ABI type mismatch, expected dynamic array".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI type mismatch, expected dynamic array".to_string() }); } }; @@ -36,9 +32,7 @@ impl ABIType { pub(crate) fn decode_dynamic_array(&self, value: &[u8]) -> Result { if value.len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError( - "Byte array is too short to be decoded as dynamic array".to_string(), - )); + return Err(ABIError::DecodingError { message: "Byte array is too short to be decoded as dynamic array".to_string() }); } // The first 2 bytes in the value determines how many values in the array @@ -48,9 +42,7 @@ impl ABIType { let child_type = match self { ABIType::DynamicArray(child_type) => child_type, _ => { - return Err(ABIError::EncodingError( - "ABI type mismatch, expected dynamic array".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI type mismatch, expected dynamic array".to_string() }); } }; diff --git a/crates/algokit_abi/src/types/collections/array_static.rs b/crates/algokit_abi/src/types/collections/array_static.rs index 9de6fded2..3041fa211 100644 --- a/crates/algokit_abi/src/types/collections/array_static.rs +++ b/crates/algokit_abi/src/types/collections/array_static.rs @@ -8,18 +8,14 @@ impl ABIType { let child_types = match self { ABIType::StaticArray(child_type, size) => vec![child_type.as_ref(); *size], _ => { - return Err(ABIError::EncodingError( - "ABI type mismatch, expected static array".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI type mismatch, expected static array".to_string() }); } }; let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected an array of values".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); } }; @@ -30,9 +26,7 @@ impl ABIType { let child_types = match self { ABIType::StaticArray(child_type, size) => vec![child_type.as_ref(); *size], _ => { - return Err(ABIError::EncodingError( - "ABI type mismatch, expected static array".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI type mismatch, expected static array".to_string() }); } }; diff --git a/crates/algokit_abi/src/types/collections/tuple.rs b/crates/algokit_abi/src/types/collections/tuple.rs index ce8888783..4c4da6c58 100644 --- a/crates/algokit_abi/src/types/collections/tuple.rs +++ b/crates/algokit_abi/src/types/collections/tuple.rs @@ -15,18 +15,14 @@ impl ABIType { let child_types = match self { ABIType::Tuple(child_types) => child_types.iter().collect::>(), _ => { - return Err(ABIError::EncodingError( - "ABI type mismatch, expected tuple".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI type mismatch, expected tuple".to_string() }); } }; let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected an array of values".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); } }; @@ -37,9 +33,7 @@ impl ABIType { let child_types = match self { ABIType::Tuple(child_types) => child_types.iter().collect::>(), _ => { - return Err(ABIError::DecodingError( - "ABI type mismatch, expected tuple".to_string(), - )); + return Err(ABIError::DecodingError { message: "ABI type mismatch, expected tuple".to_string() }); } }; @@ -49,9 +43,7 @@ impl ABIType { pub fn encode_abi_types(abi_types: &[&ABIType], values: &[ABIValue]) -> Result, ABIError> { if abi_types.len() != values.len() { - return Err(ABIError::EncodingError( - "Mismatch lengths between the values and types".to_string(), - )); + return Err(ABIError::EncodingError { message: "Mismatch lengths between the values and types".to_string() }); } let mut heads: Vec> = Vec::new(); @@ -95,7 +87,7 @@ pub fn encode_abi_types(abi_types: &[&ABIType], values: &[ABIValue]) -> Result Result Result { if values.len() > 8 { - return Err(ABIError::EncodingError(format!( + return Err(ABIError::EncodingError { message: format!( "Expected no more than 8 bool values, received {}", values.len() - ))); + ) }); } let mut result: u8 = 0; @@ -138,9 +130,7 @@ fn compress_bools(values: &[ABIValue]) -> Result { } } _ => { - return Err(ABIError::EncodingError( - "Expected all values to be ABIValue::Bool".to_string(), - )); + return Err(ABIError::EncodingError { message: "Expected all values to be ABIValue::Bool".to_string() }); } } } @@ -158,18 +148,13 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, if child_type.is_dynamic() { if bytes[bytes_cursor..].len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError( - "Byte array is too short to be decoded".to_string(), - )); + return Err(ABIError::DecodingError { message: "Byte array is too short to be decoded".to_string() }); } let dynamic_index = u16::from_be_bytes([bytes[bytes_cursor], bytes[bytes_cursor + 1]]); if let Some(last_segment) = dynamic_segments.last_mut() { if dynamic_index < last_segment.left { - return Err(ABIError::DecodingError( - "Dynamic index segment miscalculation: left is greater than right index" - .to_string(), - )); + return Err(ABIError::DecodingError { message: "Dynamic index segment miscalculation: left is greater than right index".to_string() }); } last_segment.right = dynamic_index; } @@ -203,12 +188,13 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, let slice = bytes .get(bytes_cursor..bytes_cursor + child_type_size) .ok_or_else(|| { - ABIError::DecodingError(format!( - "Index out of bounds: trying to access bytes[{}..{}] but slice has length {}", - bytes_cursor, - bytes_cursor + child_type_size, - bytes.len() - ))})?; + ABIError::DecodingError { message: format!( + "Index out of bounds: trying to access bytes[{}..{}] but slice has length {}", + bytes_cursor, + bytes_cursor + child_type_size, + bytes.len() + ) } + })?; value_partitions.push(Some(slice.to_vec())); bytes_cursor += child_type_size; @@ -216,9 +202,7 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, } } if abi_types_cursor != abi_types.len() - 1 && bytes_cursor >= bytes.len() { - return Err(ABIError::DecodingError( - "Input bytes not enough to decode".to_string(), - )); + return Err(ABIError::DecodingError { message: "Input bytes not enough to decode".to_string() }); } abi_types_cursor += 1; } @@ -226,25 +210,19 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, if let Some(last_segment) = dynamic_segments.last_mut() { let bytes_length = bytes.len(); last_segment.right = u16::try_from(bytes_length).map_err(|_| { - ABIError::EncodingError(format!("Value {} cannot fit in u16", bytes_length)) + ABIError::EncodingError { message: format!("Value {} cannot fit in u16", bytes_length) } })?; } else if bytes_cursor < bytes.len() { - return Err(ABIError::DecodingError( - "Input bytes not fully consumed".to_string(), - )); + return Err(ABIError::DecodingError { message: "Input bytes not fully consumed".to_string() }); } for i in 0..dynamic_segments.len() { let segment = &dynamic_segments[i]; if segment.left > segment.right { - return Err(ABIError::DecodingError( - "Dynamic segment should display a [l, r] space with l <= r".to_string(), - )); + return Err(ABIError::DecodingError { message: "Dynamic segment should display a [l, r] space with l <= r".to_string() }); } if i != dynamic_segments.len() - 1 && segment.right != dynamic_segments[i + 1].left { - return Err(ABIError::DecodingError( - "Dynamic segments should be consecutive".to_string(), - )); + return Err(ABIError::DecodingError { message: "Dynamic segments should be consecutive".to_string() }); } } @@ -267,7 +245,7 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, .enumerate() .map(|(i, partition)| { partition.ok_or_else(|| { - ABIError::DecodingError(format!("Value partition at index {} is None", i)) + ABIError::DecodingError { message: format!("Value partition at index {} is None", i) } }) }) .collect::>, ABIError>>()?; diff --git a/crates/algokit_abi/src/types/primitives/address.rs b/crates/algokit_abi/src/types/primitives/address.rs index df55a6d13..8de6813c2 100644 --- a/crates/algokit_abi/src/types/primitives/address.rs +++ b/crates/algokit_abi/src/types/primitives/address.rs @@ -15,32 +15,26 @@ impl ABIType { let address_str = match value { ABIValue::Address(a) => a, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected address string".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected address string".to_string() }); } }; if address_str.len() != ALGORAND_ADDRESS_LENGTH { - return Err(ABIError::ValidationError(format!( + return Err(ABIError::ValidationError { message: format!( "Algorand address must be exactly {} characters", ALGORAND_ADDRESS_LENGTH - ))); + ) }); } let decoded_address = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, address_str) .ok_or_else(|| { - ABIError::ValidationError( - "Invalid base32 encoding for Algorand address".to_string(), - ) + ABIError::ValidationError { message: "Invalid base32 encoding for Algorand address".to_string() } })?[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH] .to_vec(); Ok(decoded_address) } - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected address".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected address".to_string() }), } } @@ -48,17 +42,17 @@ impl ABIType { match self { ABIType::Address => { if bytes.len() != ALGORAND_PUBLIC_KEY_BYTE_LENGTH { - return Err(ABIError::DecodingError(format!( + return Err(ABIError::DecodingError { message: format!( "Address byte string must be {} bytes long", ALGORAND_PUBLIC_KEY_BYTE_LENGTH - ))); + ) }); } let bytes: &[u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = bytes.try_into().map_err(|_| { - ABIError::DecodingError(format!( + ABIError::DecodingError { message: format!( "Failed to convert bytes to [u8; {}] for checksum", ALGORAND_PUBLIC_KEY_BYTE_LENGTH - )) + ) } })?; let mut buffer = @@ -73,9 +67,7 @@ impl ABIType { Ok(ABIValue::Address(address_str)) } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected address".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected address".to_string() }), } } } diff --git a/crates/algokit_abi/src/types/primitives/bool.rs b/crates/algokit_abi/src/types/primitives/bool.rs index 575bf36c7..74a10d6ab 100644 --- a/crates/algokit_abi/src/types/primitives/bool.rs +++ b/crates/algokit_abi/src/types/primitives/bool.rs @@ -10,9 +10,7 @@ impl ABIType { let bool_value = match value { ABIValue::Bool(b) => b, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected boolean".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected boolean".to_string() }); } }; @@ -21,9 +19,7 @@ impl ABIType { false => Ok(vec![BOOL_FALSE_BYTE]), // false -> 0 } } - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected bool".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected bool".to_string() }), } } @@ -31,22 +27,16 @@ impl ABIType { match self { ABIType::Bool => { if bytes.len() != 1 { - return Err(ABIError::DecodingError( - "Bool string must be 1 byte long".to_string(), - )); + return Err(ABIError::DecodingError { message: "Bool string must be 1 byte long".to_string() }); } match bytes[0] { BOOL_TRUE_BYTE => Ok(ABIValue::Bool(true)), BOOL_FALSE_BYTE => Ok(ABIValue::Bool(false)), - _ => Err(ABIError::DecodingError( - "Boolean could not be decoded from the byte string".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "Boolean could not be decoded from the byte string".to_string() }), } } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected bool".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected bool".to_string() }), } } } diff --git a/crates/algokit_abi/src/types/primitives/byte.rs b/crates/algokit_abi/src/types/primitives/byte.rs index b11e017ee..9c57f4484 100644 --- a/crates/algokit_abi/src/types/primitives/byte.rs +++ b/crates/algokit_abi/src/types/primitives/byte.rs @@ -5,13 +5,9 @@ impl ABIType { match self { ABIType::Byte => match value { ABIValue::Byte(n) => Ok(vec![*n]), - _ => Err(ABIError::EncodingError( - "ABI value mismatch, expected byte".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI value mismatch, expected byte".to_string() }), }, - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected byte".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected byte".to_string() }), } } @@ -19,16 +15,12 @@ impl ABIType { match self { ABIType::Byte => { if bytes.len() != 1 { - return Err(ABIError::DecodingError( - "Byte array must be 1 byte long".to_string(), - )); + return Err(ABIError::DecodingError { message: "Byte array must be 1 byte long".to_string() }); } Ok(ABIValue::Byte(bytes[0])) } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected byte".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected byte".to_string() }), } } } diff --git a/crates/algokit_abi/src/types/primitives/string.rs b/crates/algokit_abi/src/types/primitives/string.rs index a90645796..9b5732972 100644 --- a/crates/algokit_abi/src/types/primitives/string.rs +++ b/crates/algokit_abi/src/types/primitives/string.rs @@ -7,9 +7,7 @@ impl ABIType { let value = match value { ABIValue::String(s) => s, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected string".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected string".to_string() }); } }; @@ -21,9 +19,7 @@ impl ABIType { Ok(result) } - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected string".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected string".to_string() }), } } @@ -31,28 +27,24 @@ impl ABIType { match self { ABIType::String => { if value.len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError( - "Byte array is too short for string".to_string(), - )); + return Err(ABIError::DecodingError { message: "Byte array is too short for string".to_string() }); } let length = u16::from_be_bytes([value[0], value[1]]) as usize; let content_bytes = &value[LENGTH_ENCODE_BYTE_SIZE..]; if content_bytes.len() != length { - return Err(ABIError::DecodingError(format!( + return Err(ABIError::DecodingError { message: format!( "Invalid byte array length for string, expected {} value, got {}", length, content_bytes.len() - ))); + ) }); } let string_value = String::from_utf8(content_bytes.to_vec()) - .map_err(|_| ABIError::DecodingError("Invalid UTF-8 encoding".to_string()))?; + .map_err(|_| ABIError::DecodingError { message: "Invalid UTF-8 encoding".to_string() })?; Ok(ABIValue::String(string_value)) } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected string".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected string".to_string() }), } } } diff --git a/crates/algokit_abi/src/types/primitives/ufixed.rs b/crates/algokit_abi/src/types/primitives/ufixed.rs index 722cb8e16..43d086f48 100644 --- a/crates/algokit_abi/src/types/primitives/ufixed.rs +++ b/crates/algokit_abi/src/types/primitives/ufixed.rs @@ -12,24 +12,20 @@ impl ABIType { let value = match value { ABIValue::Uint(n) => n, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected uint".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected uint".to_string() }); } }; if value >= &BigUint::from(2u64).pow(bit_size as u32) { - return Err(ABIError::EncodingError(format!( + return Err(ABIError::EncodingError { message: format!( "{} is too big to fit in ufixed{}x{}", value, bit_size, precision - ))); + ) }); } Ok(utils::big_uint_to_bytes(value, (bit_size / 8) as usize)) } - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected ufixed".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected ufixed".to_string() }), } } @@ -39,18 +35,16 @@ impl ABIType { let bit_size = bit_size.value(); let expected_len = (bit_size / 8) as usize; if bytes.len() != expected_len { - return Err(ABIError::DecodingError(format!( + return Err(ABIError::DecodingError { message: format!( "Invalid byte array length, expected {} bytes, got {}", expected_len, bytes.len() - ))); + ) }); } Ok(ABIValue::Uint(BigUint::from_bytes_be(bytes))) } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected ufixed".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected ufixed".to_string() }), } } } diff --git a/crates/algokit_abi/src/types/primitives/uint.rs b/crates/algokit_abi/src/types/primitives/uint.rs index 29583c864..ae0a4a9c1 100644 --- a/crates/algokit_abi/src/types/primitives/uint.rs +++ b/crates/algokit_abi/src/types/primitives/uint.rs @@ -10,24 +10,20 @@ impl ABIType { let value = match value { ABIValue::Uint(n) => n, _ => { - return Err(ABIError::EncodingError( - "ABI value mismatch, expected uint".to_string(), - )); + return Err(ABIError::EncodingError { message: "ABI value mismatch, expected uint".to_string() }); } }; if value >= &BigUint::from(2u64).pow(bit_size as u32) { - return Err(ABIError::EncodingError(format!( + return Err(ABIError::EncodingError { message: format!( "{} is too big to fit in uint{}", value, bit_size - ))); + ) }); } Ok(utils::big_uint_to_bytes(value, (bit_size / 8) as usize)) } - _ => Err(ABIError::EncodingError( - "ABI type mismatch, expected uint".to_string(), - )), + _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected uint".to_string() }), } } @@ -37,18 +33,16 @@ impl ABIType { let bit_size = bit_size.value(); let expected_len = (bit_size / 8) as usize; if bytes.len() != expected_len { - return Err(ABIError::DecodingError(format!( + return Err(ABIError::DecodingError { message: format!( "Invalid byte array length, expected {} bytes, got {}", expected_len, bytes.len() - ))); + ) }); } Ok(ABIValue::Uint(BigUint::from_bytes_be(bytes))) } - _ => Err(ABIError::DecodingError( - "ABI type mismatch, expected uint".to_string(), - )), + _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected uint".to_string() }), } } } diff --git a/crates/algokit_http_client/Cargo.toml b/crates/algokit_http_client/Cargo.toml index 7fb8d26cb..8dc0f31c0 100644 --- a/crates/algokit_http_client/Cargo.toml +++ b/crates/algokit_http_client/Cargo.toml @@ -20,7 +20,7 @@ js-sys = { workspace = true, optional = true } reqwest = { version = "0.12.19", optional = true } serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = { version = "0.6", optional = true } -thiserror.workspace = true +snafu = { workspace = true } tsify-next = { workspace = true, optional = true } uniffi = { workspace = true, optional = true } wasm-bindgen = { workspace = true, optional = true } diff --git a/crates/algokit_http_client/src/lib.rs b/crates/algokit_http_client/src/lib.rs index dadadffa1..534e3f843 100644 --- a/crates/algokit_http_client/src/lib.rs +++ b/crates/algokit_http_client/src/lib.rs @@ -1,14 +1,15 @@ use async_trait::async_trait; use std::collections::HashMap; +use snafu::Snafu; #[cfg(feature = "ffi_uniffi")] uniffi::setup_scaffolding!(); -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] #[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Error))] pub enum HttpError { - #[error("HttpError: {0}")] - RequestError(String), + #[snafu(display("HttpError: {message}"))] + RequestError { message: String }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -92,16 +93,16 @@ impl DefaultHttpClient { let mut headers = reqwest::header::HeaderMap::new(); headers.insert( reqwest::header::HeaderName::from_bytes(header_name.as_bytes()).map_err(|e| { - HttpError::RequestError(format!("Invalid header name '{}': {}", header_name, e)) + HttpError::RequestError { message: format!("Invalid header name '{}': {}", header_name, e) } })?, reqwest::header::HeaderValue::from_str(header_value).map_err(|e| { - HttpError::RequestError(format!("Invalid header value '{}': {}", header_value, e)) + HttpError::RequestError { message: format!("Invalid header value '{}': {}", header_value, e) } })?, ); let client = reqwest::Client::builder() .default_headers(headers) .build() - .map_err(|e| HttpError::RequestError(format!("Failed to build HTTP client: {}", e)))?; + .map_err(|e| HttpError::RequestError { message: format!("Failed to build HTTP client: {}", e) })?; Ok(DefaultHttpClient { client, base_url: base_url.to_string(), @@ -123,7 +124,7 @@ impl HttpClient for DefaultHttpClient { ) -> Result { let url = format!("{}{}", self.base_url, path); let method = reqwest::Method::from_bytes(method.as_str().as_bytes()) - .map_err(|e| HttpError::RequestError(e.to_string()))?; + .map_err(|e| HttpError::RequestError { message: e.to_string() })?; let mut request_builder = self.client.request(method, &url); @@ -144,7 +145,7 @@ impl HttpClient for DefaultHttpClient { let response = request_builder .send() .await - .map_err(|e| HttpError::RequestError(e.to_string()))?; + .map_err(|e| HttpError::RequestError { message: e.to_string() })?; if !response.status().is_success() { let status = response.status(); @@ -152,10 +153,10 @@ impl HttpClient for DefaultHttpClient { .text() .await .unwrap_or_else(|_| "Failed to read error response text".to_string()); - return Err(HttpError::RequestError(format!( + return Err(HttpError::RequestError { message: format!( "Request failed with status {}: {}", status, text - ))); + )}); } let response_headers = response @@ -167,7 +168,7 @@ impl HttpClient for DefaultHttpClient { let body = response .bytes() .await - .map_err(|e| HttpError::RequestError(e.to_string()))? + .map_err(|e| HttpError::RequestError { message: e.to_string() })? .to_vec(); Ok(HttpResponse { diff --git a/crates/algokit_utils/Cargo.toml b/crates/algokit_utils/Cargo.toml index 10a3e8d0d..249ce926e 100644 --- a/crates/algokit_utils/Cargo.toml +++ b/crates/algokit_utils/Cargo.toml @@ -25,7 +25,7 @@ derive_more = { version = "2.0.1", features = ["full"] } dotenvy = "0.15" log = "0.4.27" reqwest = { version = "0.12.19", features = ["blocking"] } -thiserror.workspace = true +snafu = { workspace = true } tokio = { version = "1.45.1", features = ["time"] } # Dependencies used in algod client integrations tests diff --git a/crates/algokit_utils/src/clients/app_manager.rs b/crates/algokit_utils/src/clients/app_manager.rs index 504c44c8b..f199c3d25 100644 --- a/crates/algokit_utils/src/clients/app_manager.rs +++ b/crates/algokit_utils/src/clients/app_manager.rs @@ -8,6 +8,7 @@ use base64::{Engine, engine::general_purpose::STANDARD as Base64}; use sha2::{Digest, Sha256}; use std::collections::HashMap; use std::sync::{Arc, Mutex}; +use snafu::Snafu; #[derive(Debug, Clone)] pub enum TealTemplateValue { @@ -132,7 +133,7 @@ impl AppManager { .algod_client .teal_compile(teal_code.as_bytes().to_vec(), Some(true)) .await - .map_err(AppManagerError::AlgodClientError)?; + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; let result = CompiledTeal { teal: teal_code.to_string(), @@ -182,17 +183,17 @@ impl AppManager { .algod_client .get_application_by_id(app_id) .await - .map_err(AppManagerError::AlgodClientError)?; + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; Ok(AppInformation { app_id, app_address: Address::from_app_id(&app_id), approval_program: Base64 .decode(&app.params.approval_program) - .map_err(|e| AppManagerError::DecodingError(e.to_string()))?, + .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?, clear_state_program: Base64 .decode(&app.params.clear_state_program) - .map_err(|e| AppManagerError::DecodingError(e.to_string()))?, + .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?, creator: app.params.creator, local_ints: app .params @@ -244,7 +245,7 @@ impl AppManager { .algod_client .account_application_information(address, app_id, None) .await - .map_err(AppManagerError::AlgodClientError)?; + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; let local_state = app_info .app_local_state @@ -260,7 +261,7 @@ impl AppManager { .algod_client .get_application_boxes(app_id, None) .await - .map_err(AppManagerError::AlgodClientError)?; + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; let mut box_names = Vec::new(); for b in box_result.boxes { @@ -291,11 +292,11 @@ impl AppManager { .algod_client .get_application_box_by_name(app_id, &name_base64) .await - .map_err(AppManagerError::AlgodClientError)?; + .map_err(|e| AppManagerError::AlgodClientError { source: e })?; Base64 .decode(&box_result.value) - .map_err(|e| AppManagerError::DecodingError(e.to_string())) + .map_err(|e| AppManagerError::DecodingError { message: e.to_string() }) } /// Get values for multiple boxes. @@ -333,7 +334,7 @@ impl AppManager { let raw_value = self.get_box_value(app_id, box_name).await?; let decoded_value = abi_type .decode(&raw_value) - .map_err(|e| AppManagerError::ABIDecodeError(e.to_string()))?; + .map_err(|e| AppManagerError::ABIDecodeError { message: e.to_string() })?; Ok(decoded_value) } @@ -374,7 +375,7 @@ impl AppManager { if let Some(return_type) = &method.returns { let return_value = return_type .decode(confirmation_data) - .map_err(|e| AppManagerError::ABIDecodeError(e.to_string()))?; + .map_err(|e| AppManagerError::ABIDecodeError { message: e.to_string() })?; Ok(Some(ABIReturn { method: method.clone(), @@ -401,7 +402,7 @@ impl AppManager { for state_val in state { let key_raw = Base64 .decode(&state_val.key) - .map_err(|e| AppManagerError::DecodingError(e.to_string()))?; + .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?; // TODO(stabilization): Consider r#type pattern consistency across API vs ABI types (PR #229 comment) let (value_raw, value_base64, value) = match state_val.value.r#type { @@ -419,10 +420,10 @@ impl AppManager { } 2 => (None, None, AppStateValue::Uint(state_val.value.uint)), _ => { - return Err(AppManagerError::DecodingError(format!( + return Err(AppManagerError::DecodingError { message: format!( "Unknown state data type: {}", state_val.value.r#type - ))); + ) }); } }; @@ -549,20 +550,20 @@ impl AppManager { if let Some(updatable) = params.updatable { if !teal_template_code.contains(UPDATABLE_TEMPLATE_NAME) { - return Err(AppManagerError::TemplateVariableNotFound(format!( + return Err(AppManagerError::TemplateVariableNotFound { message: format!( "Deploy-time updatability control requested, but {} not present in TEAL code", UPDATABLE_TEMPLATE_NAME - ))); + ) }); } result = result.replace(UPDATABLE_TEMPLATE_NAME, &(updatable as u8).to_string()); } if let Some(deletable) = params.deletable { if !teal_template_code.contains(DELETABLE_TEMPLATE_NAME) { - return Err(AppManagerError::TemplateVariableNotFound(format!( + return Err(AppManagerError::TemplateVariableNotFound { message: format!( "Deploy-time deletability control requested, but {} not present in TEAL code", DELETABLE_TEMPLATE_NAME - ))); + ) }); } result = result.replace(DELETABLE_TEMPLATE_NAME, &(deletable as u8).to_string()); } @@ -630,20 +631,20 @@ impl AppManager { } /// Errors that can occur during app manager operations. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum AppManagerError { - #[error("Algod client error: {0}")] - AlgodClientError(AlgodError), + #[snafu(display("Algod client error: {source}"))] + AlgodClientError { source: AlgodError }, - #[error("Template variable not found: {0}")] - TemplateVariableNotFound(String), + #[snafu(display("Template variable not found: {message}"))] + TemplateVariableNotFound { message: String }, - #[error("Decoding error: {0}")] - DecodingError(String), + #[snafu(display("Decoding error: {message}"))] + DecodingError { message: String }, - #[error("State not found")] + #[snafu(display("State not found"))] StateNotFound, - #[error("ABI decode error: {0}")] - ABIDecodeError(String), + #[snafu(display("ABI decode error: {message}"))] + ABIDecodeError { message: String }, } diff --git a/crates/algokit_utils/src/clients/asset_manager.rs b/crates/algokit_utils/src/clients/asset_manager.rs index ee9855cc8..04753a27a 100644 --- a/crates/algokit_utils/src/clients/asset_manager.rs +++ b/crates/algokit_utils/src/clients/asset_manager.rs @@ -2,6 +2,7 @@ use algod_client::apis::{AlgodClient, Error as AlgodError}; use algod_client::models::{AccountAssetInformation as AlgodAccountAssetInformation, Asset}; use algokit_transact::Address; use std::{str::FromStr, sync::Arc}; +use snafu::Snafu; use crate::transactions::{ AssetOptInParams, AssetOptOutParams, CommonParams, Composer, ComposerError, @@ -168,7 +169,7 @@ impl AssetManager { .algod_client .get_asset_by_id(asset_id) .await - .map_err(AssetManagerError::AlgodClientError)?; + .map_err(|e| AssetManagerError::AlgodClientError { source: e })?; Ok(asset.into()) } @@ -184,7 +185,7 @@ impl AssetManager { self.algod_client .account_asset_information(&sender.to_string(), asset_id, None) .await - .map_err(AssetManagerError::AlgodClientError) + .map_err(|e| AssetManagerError::AlgodClientError { source: e }) } pub async fn bulk_opt_in( @@ -211,14 +212,14 @@ impl AssetManager { composer .add_asset_opt_in(opt_in_params) - .map_err(AssetManagerError::ComposerError)?; + .map_err(|e| AssetManagerError::ComposerError { source: e })?; } // Send the transaction group let results = composer .send(Default::default()) .await - .map_err(AssetManagerError::ComposerError)?; + .map_err(|e| AssetManagerError::ComposerError { source: e })?; // Map transaction IDs back to assets let bulk_results: Vec = asset_ids @@ -270,7 +271,7 @@ impl AssetManager { for &asset_id in asset_ids { let asset_info = self.get_by_id(asset_id).await?; let creator = Address::from_str(&asset_info.creator) - .map_err(|_| AssetManagerError::AssetNotFound(asset_id))?; + .map_err(|_| AssetManagerError::AssetNotFound { asset_id })?; asset_creators.push(creator); } @@ -289,14 +290,14 @@ impl AssetManager { composer .add_asset_opt_out(opt_out_params) - .map_err(AssetManagerError::ComposerError)?; + .map_err(|e| AssetManagerError::ComposerError { source: e })?; } // Send the transaction group let results = composer .send(Default::default()) .await - .map_err(AssetManagerError::ComposerError)?; + .map_err(|e| AssetManagerError::ComposerError { source: e })?; // Map transaction IDs back to assets let bulk_results: Vec = asset_ids @@ -312,33 +313,33 @@ impl AssetManager { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum AssetManagerError { - #[error("Algod client error: {0}")] - AlgodClientError(AlgodError), + #[snafu(display("Algod client error: {source}"))] + AlgodClientError { source: AlgodError }, - #[error("Composer error: {0}")] - ComposerError(ComposerError), + #[snafu(display("Composer error: {source}"))] + ComposerError { source: ComposerError }, - #[error("Asset not found: {0}")] - AssetNotFound(u64), + #[snafu(display("Asset not found: {asset_id}"))] + AssetNotFound { asset_id: u64 }, - #[error("Account not found: {0}")] - AccountNotFound(String), + #[snafu(display("Account not found: {address}"))] + AccountNotFound { address: String }, - #[error("Account {address} is not opted into asset {asset_id}")] + #[snafu(display("Account {address} is not opted into asset {asset_id}"))] NotOptedIn { address: String, asset_id: u64 }, - #[error("Account {address} has non-zero balance {balance} for asset {asset_id}")] + #[snafu(display("Account {address} has non-zero balance {balance} for asset {asset_id}"))] NonZeroBalance { address: String, asset_id: u64, balance: u64, }, - #[error("Asset {asset_id} is frozen for account {address}")] + #[snafu(display("Asset {asset_id} is frozen for account {address}"))] AssetFrozen { address: String, asset_id: u64 }, - #[error("Method '{method}' not implemented: {reason}")] + #[snafu(display("Method '{method}' not implemented: {reason}"))] NotImplemented { method: String, reason: String }, } diff --git a/crates/algokit_utils/src/testing/indexer_helpers.rs b/crates/algokit_utils/src/testing/indexer_helpers.rs index e0ed4e864..6d6d4d8fd 100644 --- a/crates/algokit_utils/src/testing/indexer_helpers.rs +++ b/crates/algokit_utils/src/testing/indexer_helpers.rs @@ -2,6 +2,7 @@ use indexer_client::{IndexerClient, apis::Error as IndexerError}; use std::future::Future; use std::time::Duration; use tokio::time::sleep; +use snafu::Snafu; /// Configuration for indexer wait operations #[derive(Debug, Clone)] @@ -22,13 +23,13 @@ impl Default for IndexerWaitConfig { } /// Error types for indexer wait operations -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum IndexerWaitError { - #[error("Indexer operation failed after {attempts} attempts: {last_error}")] + #[snafu(display("Indexer operation failed after {attempts} attempts: {last_error}"))] MaxAttemptsExceeded { attempts: u32, last_error: String }, - #[error("Indexer client error: {0}")] - ClientError(String), - #[error("Transaction {tx_id} not found after {attempts} attempts")] + #[snafu(display("Indexer client error: {message}"))] + ClientError { message: String }, + #[snafu(display("Transaction {tx_id} not found after {attempts} attempts"))] TransactionNotFound { tx_id: String, attempts: u32 }, } @@ -58,7 +59,7 @@ where // If it's not a 404-like error, fail immediately if !is_not_found { - return Err(IndexerWaitError::ClientError(last_error)); + return Err(IndexerWaitError::ClientError { message: last_error }); } // If we've reached max attempts, break out of the loop diff --git a/crates/algokit_utils/src/transactions/application_call.rs b/crates/algokit_utils/src/transactions/application_call.rs index 90fb5d085..f68719829 100644 --- a/crates/algokit_utils/src/transactions/application_call.rs +++ b/crates/algokit_utils/src/transactions/application_call.rs @@ -570,7 +570,7 @@ fn populate_method_args_into_reference_arrays( match value { ABIReferenceValue::Account(addr_str) => { let address = Address::from_str(addr_str).map_err(|_e| { - ComposerError::TransactionError(format!("Invalid address {}", addr_str)) + ComposerError::TransactionError { message: format!("Invalid address {}", addr_str) } })?; if address != *sender && !account_references.contains(&address) { @@ -605,7 +605,7 @@ fn calculate_method_arg_reference_array_index( match ref_value { ABIReferenceValue::Account(addr_str) => { let address = Address::from_str(addr_str).map_err(|_e| { - ComposerError::TransactionError(format!("Invalid address {}", addr_str)) + ComposerError::TransactionError { message: format!("Invalid address {}", addr_str) } })?; if address == *sender { @@ -618,10 +618,10 @@ fn calculate_method_arg_reference_array_index( // If address already exists in account_references, use existing index + 1 Ok((existing_index + 1) as u8) } else { - Err(ComposerError::ABIEncodingError(format!( + Err(ComposerError::ABIEncodingError { message: format!( "Account {} not found in reference array", addr_str - ))) + ) }) } } ABIReferenceValue::Asset(asset_id) => { @@ -632,10 +632,10 @@ fn calculate_method_arg_reference_array_index( // If asset already exists in asset_references, use existing index Ok(existing_index as u8) } else { - Err(ComposerError::ABIEncodingError(format!( + Err(ComposerError::ABIEncodingError { message: format!( "Asset {} not found in reference array", asset_id - ))) + ) }) } } ABIReferenceValue::Application(app_id_ref) => { @@ -649,10 +649,10 @@ fn calculate_method_arg_reference_array_index( // If application already exists in app_references, use existing index + 1 Ok((existing_index + 1) as u8) } else { - Err(ComposerError::ABIEncodingError(format!( + Err(ComposerError::ABIEncodingError { message: format!( "Application {} not found in reference array", app_id_ref - ))) + ) }) } } } @@ -703,9 +703,7 @@ fn encode_arguments( .collect::, _>>()?; if abi_values.len() != abi_types.len() { - return Err(ComposerError::ABIEncodingError( - "Mismatch in length of non-transaction arguments".to_string(), - )); + return Err(ComposerError::ABIEncodingError { message: "Mismatch in length of non-transaction arguments".to_string() }); } // Apply ARC-4 tuple packing for methods with more than 14 arguments @@ -733,7 +731,7 @@ fn encode_args_with_tuple_packing( let tuple_type = ABIType::Tuple(remaining_abi_types.to_vec()); let tuple_value = ABIValue::Array(remaining_abi_values.to_vec()); let tuple_encoded = tuple_type.encode(&tuple_value).map_err(|e| { - ComposerError::ABIEncodingError(format!("Failed to encode ABI value: {}", e)) + ComposerError::ABIEncodingError { message: format!("Failed to encode ABI value: {}", e) } })?; encoded_args.push(tuple_encoded); @@ -749,7 +747,7 @@ fn encode_args_individually( for (abi_value, abi_type) in abi_values.iter().zip(abi_types.iter()) { let encoded = abi_type.encode(abi_value).map_err(|e| { - ComposerError::ABIEncodingError(format!("Failed to encode ABI value: {}", e)) + ComposerError::ABIEncodingError { message: format!("Failed to encode ABI value: {}", e) } })?; encoded_args.push(encoded); } @@ -878,7 +876,7 @@ where // Insert method selector at the front let method_selector = params.method().selector().map_err(|e| { - ComposerError::ABIEncodingError(format!("Failed to get method selector: {}", e)) + ComposerError::ABIEncodingError { message: format!("Failed to get method selector: {}", e) } })?; encoded_args.insert(0, method_selector); diff --git a/crates/algokit_utils/src/transactions/composer.rs b/crates/algokit_utils/src/transactions/composer.rs index 2c1179fd3..6793b3cec 100644 --- a/crates/algokit_utils/src/transactions/composer.rs +++ b/crates/algokit_utils/src/transactions/composer.rs @@ -15,6 +15,7 @@ use algokit_transact::{ }; use derive_more::Debug; use std::{collections::HashMap, sync::Arc}; +use snafu::Snafu; use crate::{ AppMethodCallArg, @@ -55,30 +56,38 @@ use super::payment::{AccountCloseParams, PaymentParams, build_account_close, bui // ABI return values are stored in logs with the prefix 0x151f7c75 const ABI_RETURN_PREFIX: &[u8] = &[0x15, 0x1f, 0x7c, 0x75]; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum ComposerError { - #[error("Algod client error: {0}")] - AlgodClientError(#[from] AlgodError), - #[error("AlgoKit Transact error: {0}")] - TransactError(#[from] AlgoKitTransactError), - #[error("Decode Error: {0}")] - DecodeError(String), - #[error("Transaction Error: {0}")] - TransactionError(String), - #[error("Signing Error: {0}")] - SigningError(String), - #[error("Composer State Error: {0}")] - StateError(String), - #[error("Transaction pool error: {0}")] - PoolError(String), - #[error("Transaction group size exceeds the max limit of: {max}", max = MAX_TX_GROUP_SIZE)] - GroupSizeError(), - #[error("Max wait round expired: {0}")] - MaxWaitRoundExpired(String), - #[error("ABI argument encoding error: {0}")] - ABIEncodingError(String), - #[error("ABI argument decoding error: {0}")] - ABIDecodingError(String), + #[snafu(display("Algod client error: {source}"))] + AlgodClientError { source: AlgodError }, + #[snafu(display("AlgoKit Transact error: {source}"))] + TransactError { source: AlgoKitTransactError }, + #[snafu(display("Decode Error: {message}"))] + DecodeError { message: String }, + #[snafu(display("Transaction Error: {message}"))] + TransactionError { message: String }, + #[snafu(display("Signing Error: {message}"))] + SigningError { message: String }, + #[snafu(display("Composer State Error: {message}"))] + StateError { message: String }, + #[snafu(display("Transaction pool error: {message}"))] + PoolError { message: String }, + #[snafu(display("Transaction group size exceeds the max limit of: {max}", max = MAX_TX_GROUP_SIZE))] + GroupSizeError, + #[snafu(display("Max wait round expired: {message}"))] + MaxWaitRoundExpired { message: String }, + #[snafu(display("ABI argument encoding error: {message}"))] + ABIEncodingError { message: String }, + #[snafu(display("ABI argument decoding error: {message}"))] + ABIDecodingError { message: String }, +} + +impl From for ComposerError { + fn from(e: AlgodError) -> Self { Self::AlgodClientError { source: e } } +} + +impl From for ComposerError { + fn from(e: AlgoKitTransactError) -> Self { Self::TransactError { source: e } } } #[derive(Debug)] @@ -337,7 +346,7 @@ impl Composer { fn push(&mut self, txn: ComposerTransaction) -> Result<(), ComposerError> { if self.transactions.len() >= MAX_TX_GROUP_SIZE { - return Err(ComposerError::GroupSizeError()); + return Err(ComposerError::GroupSizeError); } self.transactions.push(txn); Ok(()) @@ -603,7 +612,7 @@ impl Composer { composer_transactions.push(create_transaction()); if self.transactions.len() + composer_transactions.len() > MAX_TX_GROUP_SIZE { - return Err(ComposerError::GroupSizeError()); + return Err(ComposerError::GroupSizeError); } for composer_transaction in composer_transactions { @@ -682,19 +691,19 @@ impl Composer { let last_log = match confirmation.logs.as_ref().and_then(|logs| logs.last()) { Some(log) => log, None => { - return Err(ComposerError::ABIDecodingError(format!( + return Err(ComposerError::ABIDecodingError { message: format!( "No logs found for method {} which requires a return type", method.name - ))); + ) }); } }; // Check if the last log entry has the ABI return prefix if !last_log.starts_with(ABI_RETURN_PREFIX) { - return Err(ComposerError::ABIDecodingError(format!( + return Err(ComposerError::ABIDecodingError { message: format!( "Transaction log for method {} doesn't match with ABI return value format", method.name - ))); + ) }); } // Extract the return value bytes (skip the prefix) @@ -707,10 +716,10 @@ impl Composer { raw_return_value: return_bytes.to_vec(), return_value, })), - Err(e) => Err(ComposerError::ABIDecodingError(format!( + Err(e) => Err(ComposerError::ABIDecodingError { message: format!( "Failed to decode ABI return value for method {}: {}", method.name, e - ))), + ) }), } } @@ -723,7 +732,7 @@ impl Composer { transactions: Vec, ) -> Result<(), ComposerError> { if self.transactions.len() + transactions.len() > MAX_TX_GROUP_SIZE { - return Err(ComposerError::GroupSizeError()); + return Err(ComposerError::GroupSizeError); } transactions @@ -743,7 +752,7 @@ impl Composer { transactions: Vec, ) -> Result<(), ComposerError> { if self.transactions.len() + transactions.len() > MAX_TX_GROUP_SIZE { - return Err(ComposerError::GroupSizeError()); + return Err(ComposerError::GroupSizeError); } transactions @@ -797,7 +806,7 @@ impl Composer { // Regroup the transactions, as the transactions have likely been adjusted if transactions.len() > 1 { transactions = transactions.assign_group().map_err(|e| { - ComposerError::TransactionError(format!("Failed to assign group: {}", e)) + ComposerError::TransactionError { message: format!("Failed to assign group: {}", e) } })?; } @@ -812,14 +821,14 @@ impl Composer { .collect(); if cover_inner_fees && !app_call_indexes_without_max_fees.is_empty() { - return Err(ComposerError::StateError(format!( + return Err(ComposerError::StateError { message: format!( "Please provide a max fee for each application call transaction when inner transaction fee coverage is enabled. Required for transaction {}", app_call_indexes_without_max_fees .iter() .map(|i| i.to_string()) .collect::>() .join(", ") - ))); + ) }); } let txn_group = SimulateRequestTransactionGroup { @@ -837,16 +846,14 @@ impl Composer { .algod_client .simulate_transaction(simulate_request, Some(Format::Msgpack)) .await - .map_err(ComposerError::AlgodClientError)?; + .map_err(|e| ComposerError::AlgodClientError { source: e })?; let group_response = &response.txn_groups[0]; // Handle any simulation failures if let Some(failure_message) = &group_response.failure_message { if cover_inner_fees && failure_message.contains("fee too small") { - return Err(ComposerError::StateError( - "Fees were too small to analyze group requirements via simulate. You may need to increase an application call transaction max fee.".to_string() - )); + return Err(ComposerError::StateError { message: "Fees were too small to analyze group requirements via simulate. You may need to increase an application call transaction max fee.".to_string() }); } let failed_at = group_response @@ -861,10 +868,10 @@ impl Composer { }) .unwrap_or_else(|| "unknown".to_string()); - return Err(ComposerError::StateError(format!( + return Err(ComposerError::StateError { message: format!( "Error analyzing group requirements via simulate in transaction {}: {}", failed_at, failure_message - ))); + ) }); } let txn_analysis_results: Result, ComposerError> = group_response @@ -882,10 +889,10 @@ impl Composer { ..Default::default() }) .map_err(|e| { - ComposerError::TransactionError(format!( + ComposerError::TransactionError { message: format!( "Failed to calculate min transaction fee: {}", e - )) + ) } })?; let txn_fee = btxn.header().fee.unwrap_or(0); @@ -975,7 +982,7 @@ impl Composer { .genesis_hash .clone() .try_into() - .map_err(|_e| ComposerError::DecodeError("Invalid genesis hash".to_string()))?, + .map_err(|_e| ComposerError::DecodeError { message: "Invalid genesis hash".to_string() })?, ), first_valid, last_valid: common_params.last_valid_round.unwrap_or_else(|| { @@ -1032,7 +1039,7 @@ impl Composer { build_asset_clawback(params, header) } ComposerTransaction::AssetCreate(params) => build_asset_create(params, header) - .map_err(ComposerError::TransactionError)?, + .map_err(|e| ComposerError::TransactionError { message: e.to_string() })?, ComposerTransaction::AssetReconfigure(params) => { build_asset_reconfigure(params, header) } @@ -1044,7 +1051,7 @@ impl Composer { build_asset_unfreeze(params, header) } ComposerTransaction::AppCall(params) => { - build_app_call(params, header).map_err(ComposerError::TransactionError)? + build_app_call(params, header).map_err(|e| ComposerError::TransactionError { message: e.to_string() })? } ComposerTransaction::AppCreateCall(params) => { build_app_create_call(params, header) @@ -1086,7 +1093,7 @@ impl Composer { extra_fee: common_params.extra_fee, max_fee: common_params.max_fee, }) - .map_err(|e| ComposerError::TransactionError(e.to_string()))?; + .map_err(|e| ComposerError::TransactionError { message: e.to_string() })?; } Ok(transaction) @@ -1173,21 +1180,21 @@ impl Composer { if logical_max_fee.is_none() || transaction_fee > logical_max_fee.unwrap() { - return Err(ComposerError::TransactionError(format!( + return Err(ComposerError::TransactionError { message: format!( "Calculated transaction fee {} µALGO is greater than max of {} for transaction {}", transaction_fee, logical_max_fee.unwrap_or(0), group_index - ))); + ) }); } txn_header.fee = Some(transaction_fee); } _ => { - return Err(ComposerError::TransactionError(format!( + return Err(ComposerError::TransactionError { message: format!( "An additional fee of {} µALGO is required for non application call transaction {}", deficit_amount, group_index - ))); + ) }); } } } @@ -1197,7 +1204,7 @@ impl Composer { if transactions.len() > 1 { transactions = transactions.assign_group().map_err(|e| { - ComposerError::TransactionError(format!("Failed to assign group: {}", e)) + ComposerError::TransactionError { message: format!("Failed to assign group: {}", e) } })?; } @@ -1262,10 +1269,10 @@ impl Composer { } else { let sender_address = txn.header().sender.clone(); self.get_signer(sender_address.clone()) - .ok_or(ComposerError::SigningError(format!( + .ok_or(ComposerError::SigningError { message: format!( "No signer found for address: {}", sender_address - )))? + ) })? }; Ok(TransactionWithSigner { transaction: txn, @@ -1281,9 +1288,7 @@ impl Composer { } let transactions_with_signers = - self.built_group.as_ref().ok_or(ComposerError::StateError( - "Cannot gather signatures before building the transaction group".to_string(), - ))?; + self.built_group.as_ref().ok_or(ComposerError::StateError { message: "Cannot gather signatures before building the transaction group".to_string() })?; // Group transactions by signer let mut transactions = Vec::new(); @@ -1304,7 +1309,7 @@ impl Composer { let signed_txns = signer .sign_transactions(&transactions, &indices) .await - .map_err(ComposerError::SigningError)?; + .map_err(|e| ComposerError::SigningError { message: e.to_string() })?; for (i, &index) in indices.iter().enumerate() { signed_transactions[index] = Some(signed_txns[i].to_owned()); @@ -1316,10 +1321,10 @@ impl Composer { .enumerate() .map(|(i, signed_transaction)| { signed_transaction.ok_or_else(|| { - ComposerError::SigningError(format!( + ComposerError::SigningError { message: format!( "Transaction at index {} was not signed", i - )) + ) } }) }) .collect(); @@ -1335,7 +1340,7 @@ impl Composer { max_rounds: u64, ) -> Result { let status = self.algod_client.get_status().await.map_err(|e| { - ComposerError::TransactionError(format!("Failed to get status: {:?}", e)) + ComposerError::TransactionError { message: format!("Failed to get status: {:?}", e) } })?; let start_round = status.last_round + 1; @@ -1350,7 +1355,7 @@ impl Composer { Ok(response) => { // Check for pool errors first - transaction was kicked out of pool if !response.pool_error.is_empty() { - return Err(ComposerError::PoolError(response.pool_error.clone())); + return Err(ComposerError::PoolError { message: response.pool_error.clone() }); } // Check if transaction is confirmed @@ -1374,7 +1379,7 @@ impl Composer { current_round += 1; continue; } else { - return Err(ComposerError::AlgodClientError(error)); + return Err(ComposerError::AlgodClientError { source: error }); } } }; @@ -1383,10 +1388,10 @@ impl Composer { current_round += 1; } - Err(ComposerError::MaxWaitRoundExpired(format!( + Err(ComposerError::MaxWaitRoundExpired { message: format!( "Transaction {} unconfirmed after {} rounds", tx_id, max_rounds - ))) + ) }) } pub async fn send( @@ -1399,22 +1404,18 @@ impl Composer { let group_id = { let transactions_with_signers = self.built_group.as_ref().ok_or( - ComposerError::StateError("No transactions built".to_string()), + ComposerError::StateError { message: "No transactions built".to_string() }, )?; if transactions_with_signers.is_empty() { - return Err(ComposerError::StateError( - "No transactions to send".to_string(), - )); + return Err(ComposerError::StateError { message: "No transactions to send".to_string() }); } transactions_with_signers[0].transaction.header().group }; self.gather_signatures().await?; - let signed_transactions = self.signed_group.as_ref().ok_or(ComposerError::StateError( - "No signed transactions".to_string(), - ))?; + let signed_transactions = self.signed_group.as_ref().ok_or(ComposerError::StateError { message: "No signed transactions".to_string() })?; let wait_rounds = if let Some(max_rounds_to_wait_for_confirmation) = params.and_then(|p| p.max_rounds_to_wait_for_confirmation) @@ -1425,17 +1426,13 @@ impl Composer { .iter() .map(|signed_transaction| signed_transaction.transaction.header().first_valid) .min() - .ok_or(ComposerError::StateError( - "Failed to calculate first valid round".to_string(), - ))?; + .ok_or(ComposerError::StateError { message: "Failed to calculate first valid round".to_string() })?; let last_round: u64 = signed_transactions .iter() .map(|signed_transaction| signed_transaction.transaction.header().last_valid) .max() - .ok_or(ComposerError::StateError( - "Failed to calculate last valid round".to_string(), - ))?; + .ok_or(ComposerError::StateError { message: "Failed to calculate last valid round".to_string() })?; last_round - first_round }; @@ -1445,10 +1442,10 @@ impl Composer { for signed_txn in signed_transactions { let encoded_txn = signed_txn.encode().map_err(|e| { - ComposerError::TransactionError(format!( + ComposerError::TransactionError { message: format!( "Failed to encode signed transaction: {}", e - )) + ) } })?; encoded_bytes.extend_from_slice(&encoded_txn); } @@ -1458,7 +1455,7 @@ impl Composer { .raw_transaction(encoded_bytes) .await .map_err(|e| { - ComposerError::TransactionError(format!("Failed to submit transaction(s): {:?}", e)) + ComposerError::TransactionError { message: format!("Failed to submit transaction(s): {:?}", e) } })?; let transaction_ids: Vec = signed_transactions diff --git a/crates/algokit_utils/src/transactions/creator.rs b/crates/algokit_utils/src/transactions/creator.rs index a1067eaa2..363353cbc 100644 --- a/crates/algokit_utils/src/transactions/creator.rs +++ b/crates/algokit_utils/src/transactions/creator.rs @@ -48,9 +48,7 @@ impl TransactionCreator { built_transactions .last() .map(|tx_with_signer| tx_with_signer.transaction.clone()) - .ok_or(ComposerError::StateError( - "No transactions were built by the composer".to_string(), - )) + .ok_or(ComposerError::StateError { message: "No transactions were built by the composer".to_string() }) } pub async fn payment(&self, params: PaymentParams) -> Result { @@ -72,9 +70,7 @@ impl TransactionCreator { ) -> Result { // Enhanced parameter validation if params.asset_id == 0 { - return Err(ComposerError::TransactionError( - "Asset ID must be greater than 0".to_string(), - )); + return Err(ComposerError::TransactionError { message: "Asset ID must be greater than 0".to_string() }); } // Note: amount can be 0 for opt-in transactions, so we don't validate it here diff --git a/crates/algokit_utils/src/transactions/sender.rs b/crates/algokit_utils/src/transactions/sender.rs index 2b0bc2127..ac590eb9f 100644 --- a/crates/algokit_utils/src/transactions/sender.rs +++ b/crates/algokit_utils/src/transactions/sender.rs @@ -19,25 +19,46 @@ use crate::clients::asset_manager::{AssetManager, AssetManagerError}; use algod_client::apis::AlgodApiError; use algokit_abi::{ABIMethod, ABIReturn}; use algokit_transact::Address; +use snafu::Snafu; use std::{str::FromStr, sync::Arc}; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum TransactionSenderError { - #[error("Algod client error: {0}")] - AlgodClientError(#[from] AlgodApiError), - #[error("Composer error: {0}")] - ComposerError(#[from] ComposerError), - #[error("Asset manager error: {0}")] - AssetManagerError(#[from] AssetManagerError), - #[error("App manager error: {0}")] - AppManagerError(#[from] AppManagerError), - #[error("Transaction result error: {0}")] - TransactionResultError(#[from] TransactionResultError), - #[error("Invalid parameters: {0}")] - InvalidParameters(String), - #[error("Transaction validation error: {0}")] - ValidationError(String), + #[snafu(display("Algod client error: {source}"))] + AlgodClientError { source: AlgodApiError }, + #[snafu(display("Composer error: {source}"))] + ComposerError { source: ComposerError }, + #[snafu(display("Asset manager error: {source}"))] + AssetManagerError { source: AssetManagerError }, + #[snafu(display("App manager error: {source}"))] + AppManagerError { source: AppManagerError }, + #[snafu(display("Transaction result error: {source}"))] + TransactionResultError { source: TransactionResultError }, + #[snafu(display("Invalid parameters: {message}"))] + InvalidParameters { message: String }, + #[snafu(display("Transaction validation error: {message}"))] + ValidationError { message: String }, +} + +impl From for TransactionSenderError { + fn from(e: AlgodApiError) -> Self { Self::AlgodClientError { source: e } } +} + +impl From for TransactionSenderError { + fn from(e: ComposerError) -> Self { Self::ComposerError { source: e } } +} + +impl From for TransactionSenderError { + fn from(e: AssetManagerError) -> Self { Self::AssetManagerError { source: e } } +} + +impl From for TransactionSenderError { + fn from(e: AppManagerError) -> Self { Self::AppManagerError { source: e } } +} + +impl From for TransactionSenderError { + fn from(e: TransactionResultError) -> Self { Self::TransactionResultError { source: e } } } /// Sends transactions and groups with validation and result processing. @@ -182,7 +203,7 @@ impl TransactionSender { let returns: Result, _> = composer_results .abi_returns .into_iter() - .map(|result| result.map_err(TransactionSenderError::ComposerError)) + .map(|result| result.map_err(|e| TransactionSenderError::ComposerError { source: e })) .collect(); match returns { Ok(returns) => { @@ -341,9 +362,7 @@ impl TransactionSender { ) -> Result { // Enhanced parameter validation if params.asset_id == 0 { - return Err(TransactionSenderError::InvalidParameters( - "Asset ID must be greater than 0".to_string(), - )); + return Err(TransactionSenderError::InvalidParameters { message: "Asset ID must be greater than 0".to_string() }); } // Note: amount can be 0 for opt-in transactions, so we don't validate it here @@ -375,17 +394,17 @@ impl TransactionSender { .get_by_id(params.asset_id) .await .map_err(|e| { - TransactionSenderError::ValidationError(format!( + TransactionSenderError::ValidationError { message: format!( "Failed to get asset {} information: {}", params.asset_id, e - )) + ) } })?; let creator = Address::from_str(&asset_info.creator).map_err(|e| { - TransactionSenderError::ValidationError(format!( + TransactionSenderError::ValidationError { message: format!( "Invalid creator address for asset {}: {}", params.asset_id, e - )) + ) } })?; AssetOptOutParams { @@ -403,10 +422,10 @@ impl TransactionSender { .get_account_information(¶ms.common_params.sender, params.asset_id) .await .map_err(|e| { - TransactionSenderError::ValidationError(format!( + TransactionSenderError::ValidationError { message: format!( "Account {} validation failed for Asset {}: {}", params.common_params.sender, params.asset_id, e - )) + ) } })?; let balance = account_info @@ -415,10 +434,10 @@ impl TransactionSender { .map(|h| h.amount) .unwrap_or(0); if balance != 0 { - return Err(TransactionSenderError::ValidationError(format!( + return Err(TransactionSenderError::ValidationError { message: format!( "Account {} does not have a zero balance for Asset {}; can't opt-out.", params.common_params.sender, params.asset_id - ))); + ) }); } } @@ -437,7 +456,7 @@ impl TransactionSender { |composer| composer.add_asset_create(params), |base_result| { SendAssetCreateResult::new(base_result) - .map_err(TransactionSenderError::TransactionResultError) + .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) }, ) .await @@ -513,7 +532,7 @@ impl TransactionSender { let clear_bytes = compiled_clear.map(|ct| ct.compiled_base64_to_bytes); SendAppCreateResult::new(base_result, None, approval_bytes, clear_bytes) - .map_err(TransactionSenderError::TransactionResultError) + .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) }, ) .await @@ -598,7 +617,7 @@ impl TransactionSender { let clear_bytes = compiled_clear.map(|ct| ct.compiled_base64_to_bytes); SendAppCreateResult::new(base_result, abi_return, approval_bytes, clear_bytes) - .map_err(TransactionSenderError::TransactionResultError) + .map_err(|e| TransactionSenderError::TransactionResultError { source: e }) }, ) .await diff --git a/crates/algokit_utils/src/transactions/sender_results.rs b/crates/algokit_utils/src/transactions/sender_results.rs index e10d045c2..39aafd7af 100644 --- a/crates/algokit_utils/src/transactions/sender_results.rs +++ b/crates/algokit_utils/src/transactions/sender_results.rs @@ -3,6 +3,7 @@ use algokit_abi::ABIReturn; use algokit_transact::{ Address, ApplicationCallTransactionFields, AssetConfigTransactionFields, Transaction, }; +use snafu::Snafu; /// The unified, comprehensive result of sending a single transaction or transaction group. /// @@ -91,14 +92,14 @@ pub struct SendAppCallResult { } /// Errors that can occur when constructing transaction results -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum TransactionResultError { - #[error("Missing confirmation data: {0}")] - MissingConfirmation(String), - #[error("Invalid confirmation data: {0}")] - InvalidConfirmation(String), - #[error("Transaction parsing error: {0}")] - ParsingError(String), + #[snafu(display("Missing confirmation data: {message}"))] + MissingConfirmation { message: String }, + #[snafu(display("Invalid confirmation data: {message}"))] + InvalidConfirmation { message: String }, + #[snafu(display("Transaction parsing error: {message}"))] + ParsingError { message: String }, } impl SendTransactionResult { @@ -114,21 +115,21 @@ impl SendTransactionResult { abi_returns: Option>, ) -> Result { if transactions.is_empty() { - return Err(TransactionResultError::MissingConfirmation( - "No transactions provided".to_string(), - )); + return Err(TransactionResultError::MissingConfirmation { + message: "No transactions provided".to_string(), + }); } if confirmations.is_empty() { - return Err(TransactionResultError::MissingConfirmation( - "No confirmations provided".to_string(), - )); + return Err(TransactionResultError::MissingConfirmation { + message: "No confirmations provided".to_string(), + }); } if tx_ids.len() != transactions.len() || tx_ids.len() != confirmations.len() { - return Err(TransactionResultError::InvalidConfirmation( - "Mismatched transaction, confirmation, and ID counts".to_string(), - )); + return Err(TransactionResultError::InvalidConfirmation { + message: "Mismatched transaction, confirmation, and ID counts".to_string(), + }); } // The primary transaction is the last one in the group @@ -253,9 +254,9 @@ impl SendAssetCreateResult { pub fn new(common_params: SendTransactionResult) -> Result { // Extract asset ID from the confirmation let asset_id = common_params.confirmation.asset_id.ok_or_else(|| { - TransactionResultError::InvalidConfirmation( - "Asset creation confirmation missing asset-index".to_string(), - ) + TransactionResultError::InvalidConfirmation { + message: "Asset creation confirmation missing asset-index".to_string(), + } })?; Ok(SendAssetCreateResult { @@ -284,9 +285,9 @@ impl SendAppCreateResult { ) -> Result { // Extract app ID from the confirmation let app_id = common_params.confirmation.app_id.ok_or_else(|| { - TransactionResultError::InvalidConfirmation( - "Application creation confirmation missing application-index".to_string(), - ) + TransactionResultError::InvalidConfirmation { + message: "Application creation confirmation missing application-index".to_string(), + } })?; // Calculate app address @@ -432,8 +433,8 @@ mod tests { assert!(result.is_err()); match result.unwrap_err() { - TransactionResultError::MissingConfirmation(msg) => { - assert!(msg.contains("No transactions provided")); + TransactionResultError::MissingConfirmation { message } => { + assert!(message.contains("No transactions provided")); } _ => panic!("Expected MissingConfirmation error"), } From 5952efd8f261de7da8ec22b66b2bff29f70d4af3 Mon Sep 17 00:00:00 2001 From: Joe Polny Date: Mon, 18 Aug 2025 11:03:36 -0400 Subject: [PATCH 3/6] feat: migrate apis from thiserror to snafu --- Cargo.lock | 4 +- .../templates/apis/endpoint.rs.j2 | 26 +- .../templates/apis/mod.rs.j2 | 28 +- .../templates/base/Cargo.toml.j2 | 3 +- crates/algod_client/Cargo.toml | 4 +- crates/algod_client/src/apis/abort_catchup.rs | 26 +- .../apis/account_application_information.rs | 28 +- .../src/apis/account_asset_information.rs | 28 +- .../src/apis/account_assets_information.rs | 26 +- .../src/apis/account_information.rs | 28 +- .../src/apis/add_participation_key.rs | 28 +- crates/algod_client/src/apis/append_keys.rs | 28 +- .../apis/delete_participation_key_by_id.rs | 2 +- .../src/apis/experimental_check.rs | 2 +- .../src/apis/generate_participation_keys.rs | 26 +- .../src/apis/get_application_box_by_name.rs | 26 +- .../src/apis/get_application_boxes.rs | 26 +- .../src/apis/get_application_by_id.rs | 26 +- .../algod_client/src/apis/get_asset_by_id.rs | 26 +- crates/algod_client/src/apis/get_block.rs | 28 +- .../algod_client/src/apis/get_block_hash.rs | 26 +- .../algod_client/src/apis/get_block_logs.rs | 26 +- .../src/apis/get_block_time_stamp_offset.rs | 26 +- .../algod_client/src/apis/get_block_txids.rs | 26 +- crates/algod_client/src/apis/get_config.rs | 26 +- .../src/apis/get_debug_settings_prof.rs | 26 +- crates/algod_client/src/apis/get_genesis.rs | 26 +- .../src/apis/get_ledger_state_delta.rs | 28 +- ...edger_state_delta_for_transaction_group.rs | 28 +- .../src/apis/get_light_block_header_proof.rs | 26 +- .../src/apis/get_participation_key_by_id.rs | 26 +- .../src/apis/get_participation_keys.rs | 26 +- .../src/apis/get_pending_transactions.rs | 28 +- .../get_pending_transactions_by_address.rs | 28 +- crates/algod_client/src/apis/get_ready.rs | 2 +- .../algod_client/src/apis/get_state_proof.rs | 26 +- crates/algod_client/src/apis/get_status.rs | 26 +- crates/algod_client/src/apis/get_supply.rs | 26 +- .../algod_client/src/apis/get_sync_round.rs | 26 +- ...ion_group_ledger_state_deltas_for_round.rs | 28 +- .../src/apis/get_transaction_proof.rs | 26 +- crates/algod_client/src/apis/get_version.rs | 26 +- crates/algod_client/src/apis/health_check.rs | 2 +- crates/algod_client/src/apis/metrics.rs | 2 +- crates/algod_client/src/apis/mod.rs | 354 +++++++++--------- .../apis/pending_transaction_information.rs | 28 +- .../src/apis/put_debug_settings_prof.rs | 26 +- .../algod_client/src/apis/raw_transaction.rs | 28 +- .../src/apis/raw_transaction_async.rs | 2 +- .../src/apis/set_block_time_stamp_offset.rs | 2 +- .../algod_client/src/apis/set_sync_round.rs | 2 +- crates/algod_client/src/apis/shutdown_node.rs | 26 +- .../src/apis/simulate_transaction.rs | 38 +- crates/algod_client/src/apis/start_catchup.rs | 26 +- crates/algod_client/src/apis/swagger_json.rs | 26 +- crates/algod_client/src/apis/teal_compile.rs | 26 +- .../algod_client/src/apis/teal_disassemble.rs | 32 +- crates/algod_client/src/apis/teal_dryrun.rs | 34 +- .../src/apis/transaction_params.rs | 26 +- .../algod_client/src/apis/unset_sync_round.rs | 2 +- .../algod_client/src/apis/wait_for_block.rs | 26 +- .../src/testing/indexer_helpers.rs | 2 +- .../src/transactions/composer.rs | 10 +- crates/indexer_client/Cargo.toml | 4 +- .../apis/lookup_account_app_local_states.rs | 26 +- .../src/apis/lookup_account_assets.rs | 26 +- .../src/apis/lookup_account_by_id.rs | 26 +- .../lookup_account_created_applications.rs | 26 +- .../src/apis/lookup_account_created_assets.rs | 26 +- .../src/apis/lookup_account_transactions.rs | 26 +- .../lookup_application_box_by_id_and_name.rs | 26 +- .../src/apis/lookup_application_by_id.rs | 26 +- .../src/apis/lookup_application_logs_by_id.rs | 26 +- .../src/apis/lookup_asset_balances.rs | 26 +- .../src/apis/lookup_asset_by_id.rs | 26 +- .../src/apis/lookup_asset_transactions.rs | 26 +- .../indexer_client/src/apis/lookup_block.rs | 26 +- .../src/apis/lookup_transaction.rs | 26 +- .../src/apis/make_health_check.rs | 26 +- crates/indexer_client/src/apis/mod.rs | 196 ++++++---- .../src/apis/search_for_accounts.rs | 26 +- .../src/apis/search_for_application_boxes.rs | 26 +- .../src/apis/search_for_applications.rs | 26 +- .../src/apis/search_for_assets.rs | 26 +- .../src/apis/search_for_block_headers.rs | 26 +- .../src/apis/search_for_transactions.rs | 26 +- 86 files changed, 1436 insertions(+), 1007 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a05c8a3d..00b2c014f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,7 +50,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_with", - "thiserror 1.0.69", + "snafu", "tokio", "tokio-test", "url", @@ -2065,7 +2065,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_with", - "thiserror 1.0.69", + "snafu", "tokio", "tokio-test", "url", diff --git a/api/oas_generator/rust_oas_generator/templates/apis/endpoint.rs.j2 b/api/oas_generator/rust_oas_generator/templates/apis/endpoint.rs.j2 index 248d69a78..b684d0c60 100644 --- a/api/oas_generator/rust_oas_generator/templates/apis/endpoint.rs.j2 +++ b/api/oas_generator/rust_oas_generator/templates/apis/endpoint.rs.j2 @@ -123,7 +123,7 @@ pub async fn {{ operation.rust_function_name }}( headers.insert("Accept".to_string(), "application/json".to_string()); let body = {% if has_request_body(operation) %} - Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) {% else %} None {% endif %}; @@ -137,10 +137,10 @@ pub async fn {{ operation.rust_function_name }}( {% if request_body_type == "Vec" %} Some(p_{{ get_request_body_name(operation) }}) {% else %} - Some(rmp_serde::to_vec_named(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(rmp_serde::to_vec_named(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) {% endif %} {% else %} - Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) {% endif %} {% else %} None @@ -163,13 +163,13 @@ pub async fn {{ operation.rust_function_name }}( {% if request_body_type == "Vec" %} Some(p_{{ get_request_body_name(operation) }}) {% else %} - Some(rmp_serde::to_vec_named(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(rmp_serde::to_vec_named(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) {% endif %} } else { - Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) } {% else %} - Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde(e.to_string()))?) + Some(serde_json::to_vec(&p_{{ get_request_body_name(operation) }}).map_err(|e| Error::Serde { message: e.to_string() })?) {% endif %} {% else %} None @@ -185,7 +185,7 @@ pub async fn {{ operation.rust_function_name }}( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; {% if get_success_response_type(operation) %} let content_type = response @@ -195,17 +195,17 @@ pub async fn {{ operation.rust_function_name }}( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { message: e.to_string() }), {% if operation.supports_msgpack %} - ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { message: e.to_string() }), {% else %} - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::MsgPack => Err(Error::Serde { message: "MsgPack not supported".to_string() }), {% endif %} ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { message: e.to_string() })?; + Err(Error::Serde { message: format!("Unexpected text response: {}", text) }) }, - ContentType::Unsupported(ct) => Err(Error::Serde(format!("Unsupported content type: {}", ct))), + ContentType::Unsupported(ct) => Err(Error::Serde { message: format!("Unsupported content type: {}", ct) }), } {% else %} let _ = response; diff --git a/api/oas_generator/rust_oas_generator/templates/apis/mod.rs.j2 b/api/oas_generator/rust_oas_generator/templates/apis/mod.rs.j2 index c8ae45a0c..d1f05d216 100644 --- a/api/oas_generator/rust_oas_generator/templates/apis/mod.rs.j2 +++ b/api/oas_generator/rust_oas_generator/templates/apis/mod.rs.j2 @@ -23,35 +23,37 @@ pub mod parameter_enums; pub mod {{ operation.rust_function_name }}; {% endfor %} +use snafu::Snafu; + /// Unified error type that can represent any API error from any endpoint -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum {{ client_type }}ApiError { {% for operation in operations %} - #[error("{{ operation.rust_function_name | title }} error: {0:?}")] - {{ operation.rust_function_name | pascal_case }}({{ operation.rust_function_name }}::{{ operation.rust_error_enum }}), + #[snafu(display("{{ operation.rust_function_name | title }} error: {error:?}"))] + {{ operation.rust_function_name | pascal_case }} { error: {{ operation.rust_function_name }}::{{ operation.rust_error_enum }} }, {% endfor %} - #[error("Unknown API error: {0}")] - Unknown(String), + #[snafu(display("Unknown API error: {message}"))] + Unknown { message: String }, } {% for operation in operations %} impl From<{{ operation.rust_function_name }}::{{ operation.rust_error_enum }}> for {{ client_type }}ApiError { fn from(err: {{ operation.rust_function_name }}::{{ operation.rust_error_enum }}) -> Self { - {{ client_type }}ApiError::{{ operation.rust_function_name | pascal_case }}(err) + {{ client_type }}ApiError::{{ operation.rust_function_name | pascal_case }} { error: err } } } {% endfor %} /// The main error type for all {{ client_type | lower }} client operations -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum Error { - #[error("HTTP error: {0}")] - Http(#[from] algokit_http_client::HttpError), - #[error("Serialization error: {0}")] - Serde(String), - #[error("API error: {0}")] - Api(#[from] {{ client_type }}ApiError), + #[snafu(display("HTTP error: {source}"))] + Http { source: algokit_http_client::HttpError }, + #[snafu(display("Serialization error: {message}"))] + Serde { message: String }, + #[snafu(display("API error: {source}"))] + Api { source: {{ client_type }}ApiError }, } #[derive(Debug, PartialEq, Eq)] diff --git a/api/oas_generator/rust_oas_generator/templates/base/Cargo.toml.j2 b/api/oas_generator/rust_oas_generator/templates/base/Cargo.toml.j2 index 1df4249e2..f04046e15 100644 --- a/api/oas_generator/rust_oas_generator/templates/base/Cargo.toml.j2 +++ b/api/oas_generator/rust_oas_generator/templates/base/Cargo.toml.j2 @@ -34,7 +34,7 @@ rmp-serde = "^1.1" {% endif %} # Error handling -thiserror = "^1.0" +snafu = { workspace = true } # Utilities base64 = "^0.22" @@ -43,4 +43,3 @@ uuid = { version = "^1.0", features = ["v4"] } [dev-dependencies] tokio = { version = "1.0", features = ["full"] } tokio-test = "^0.4" - diff --git a/crates/algod_client/Cargo.toml b/crates/algod_client/Cargo.toml index 6631db553..bdaed1885 100644 --- a/crates/algod_client/Cargo.toml +++ b/crates/algod_client/Cargo.toml @@ -32,7 +32,7 @@ algokit_transact = { path = "../algokit_transact" } rmp-serde = "^1.1" # Error handling -thiserror = "^1.0" +snafu = { workspace = true } # Utilities base64 = "^0.22" @@ -40,4 +40,4 @@ uuid = { version = "^1.0", features = ["v4"] } [dev-dependencies] tokio = { version = "1.0", features = ["full"] } -tokio-test = "^0.4" +tokio-test = "^0.4" \ No newline at end of file diff --git a/crates/algod_client/src/apis/abort_catchup.rs b/crates/algod_client/src/apis/abort_catchup.rs index 728e860f8..614a37393 100644 --- a/crates/algod_client/src/apis/abort_catchup.rs +++ b/crates/algod_client/src/apis/abort_catchup.rs @@ -60,7 +60,7 @@ pub async fn abort_catchup( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -69,16 +69,22 @@ pub async fn abort_catchup( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/account_application_information.rs b/crates/algod_client/src/apis/account_application_information.rs index 4e8c0dc1e..b8cb05e74 100644 --- a/crates/algod_client/src/apis/account_application_information.rs +++ b/crates/algod_client/src/apis/account_application_information.rs @@ -80,7 +80,7 @@ pub async fn account_application_information( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -89,18 +89,22 @@ pub async fn account_application_information( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/account_asset_information.rs b/crates/algod_client/src/apis/account_asset_information.rs index 6e3b2bcfa..828470241 100644 --- a/crates/algod_client/src/apis/account_asset_information.rs +++ b/crates/algod_client/src/apis/account_asset_information.rs @@ -80,7 +80,7 @@ pub async fn account_asset_information( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -89,18 +89,22 @@ pub async fn account_asset_information( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/account_assets_information.rs b/crates/algod_client/src/apis/account_assets_information.rs index 8e0ff0bb2..8649f3508 100644 --- a/crates/algod_client/src/apis/account_assets_information.rs +++ b/crates/algod_client/src/apis/account_assets_information.rs @@ -70,7 +70,7 @@ pub async fn account_assets_information( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -79,16 +79,22 @@ pub async fn account_assets_information( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/account_information.rs b/crates/algod_client/src/apis/account_information.rs index 7ad667766..8f1262559 100644 --- a/crates/algod_client/src/apis/account_information.rs +++ b/crates/algod_client/src/apis/account_information.rs @@ -82,7 +82,7 @@ pub async fn account_information( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -91,18 +91,22 @@ pub async fn account_information( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/add_participation_key.rs b/crates/algod_client/src/apis/add_participation_key.rs index 0af23ae7e..d7c75c86f 100644 --- a/crates/algod_client/src/apis/add_participation_key.rs +++ b/crates/algod_client/src/apis/add_participation_key.rs @@ -63,7 +63,7 @@ pub async fn add_participation_key( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -72,18 +72,22 @@ pub async fn add_participation_key( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/append_keys.rs b/crates/algod_client/src/apis/append_keys.rs index 5c1759b13..810a09954 100644 --- a/crates/algod_client/src/apis/append_keys.rs +++ b/crates/algod_client/src/apis/append_keys.rs @@ -67,7 +67,7 @@ pub async fn append_keys( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -76,18 +76,22 @@ pub async fn append_keys( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/delete_participation_key_by_id.rs b/crates/algod_client/src/apis/delete_participation_key_by_id.rs index 8079b759c..7a88f6d3d 100644 --- a/crates/algod_client/src/apis/delete_participation_key_by_id.rs +++ b/crates/algod_client/src/apis/delete_participation_key_by_id.rs @@ -61,7 +61,7 @@ pub async fn delete_participation_key_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/experimental_check.rs b/crates/algod_client/src/apis/experimental_check.rs index 33f667c96..3e3065ae7 100644 --- a/crates/algod_client/src/apis/experimental_check.rs +++ b/crates/algod_client/src/apis/experimental_check.rs @@ -49,7 +49,7 @@ pub async fn experimental_check(http_client: &dyn HttpClient) -> Result<(), Erro Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/generate_participation_keys.rs b/crates/algod_client/src/apis/generate_participation_keys.rs index 7f67abecb..8bd1ed726 100644 --- a/crates/algod_client/src/apis/generate_participation_keys.rs +++ b/crates/algod_client/src/apis/generate_participation_keys.rs @@ -72,7 +72,7 @@ pub async fn generate_participation_keys( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -81,16 +81,22 @@ pub async fn generate_participation_keys( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_application_box_by_name.rs b/crates/algod_client/src/apis/get_application_box_by_name.rs index 1e96b049d..0e3cad199 100644 --- a/crates/algod_client/src/apis/get_application_box_by_name.rs +++ b/crates/algod_client/src/apis/get_application_box_by_name.rs @@ -64,7 +64,7 @@ pub async fn get_application_box_by_name( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -73,16 +73,22 @@ pub async fn get_application_box_by_name( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_application_boxes.rs b/crates/algod_client/src/apis/get_application_boxes.rs index 225b33d74..52640f462 100644 --- a/crates/algod_client/src/apis/get_application_boxes.rs +++ b/crates/algod_client/src/apis/get_application_boxes.rs @@ -65,7 +65,7 @@ pub async fn get_application_boxes( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -74,16 +74,22 @@ pub async fn get_application_boxes( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_application_by_id.rs b/crates/algod_client/src/apis/get_application_by_id.rs index 21bd3c99f..9697d846f 100644 --- a/crates/algod_client/src/apis/get_application_by_id.rs +++ b/crates/algod_client/src/apis/get_application_by_id.rs @@ -61,7 +61,7 @@ pub async fn get_application_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -70,16 +70,22 @@ pub async fn get_application_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_asset_by_id.rs b/crates/algod_client/src/apis/get_asset_by_id.rs index 27decacb2..bc55c1b49 100644 --- a/crates/algod_client/src/apis/get_asset_by_id.rs +++ b/crates/algod_client/src/apis/get_asset_by_id.rs @@ -55,7 +55,7 @@ pub async fn get_asset_by_id(http_client: &dyn HttpClient, asset_id: u64) -> Res Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -64,16 +64,22 @@ pub async fn get_asset_by_id(http_client: &dyn HttpClient, asset_id: u64) -> Res .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_block.rs b/crates/algod_client/src/apis/get_block.rs index 11b41218d..3f5efe178 100644 --- a/crates/algod_client/src/apis/get_block.rs +++ b/crates/algod_client/src/apis/get_block.rs @@ -80,7 +80,7 @@ pub async fn get_block( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -89,18 +89,22 @@ pub async fn get_block( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_block_hash.rs b/crates/algod_client/src/apis/get_block_hash.rs index edf519ee7..89945d889 100644 --- a/crates/algod_client/src/apis/get_block_hash.rs +++ b/crates/algod_client/src/apis/get_block_hash.rs @@ -58,7 +58,7 @@ pub async fn get_block_hash( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -67,16 +67,22 @@ pub async fn get_block_hash( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_block_logs.rs b/crates/algod_client/src/apis/get_block_logs.rs index 04553d2c6..f2b1d4e53 100644 --- a/crates/algod_client/src/apis/get_block_logs.rs +++ b/crates/algod_client/src/apis/get_block_logs.rs @@ -57,7 +57,7 @@ pub async fn get_block_logs( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -66,16 +66,22 @@ pub async fn get_block_logs( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_block_time_stamp_offset.rs b/crates/algod_client/src/apis/get_block_time_stamp_offset.rs index 14a0ba69e..de956e7ec 100644 --- a/crates/algod_client/src/apis/get_block_time_stamp_offset.rs +++ b/crates/algod_client/src/apis/get_block_time_stamp_offset.rs @@ -52,7 +52,7 @@ pub async fn get_block_time_stamp_offset( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -61,16 +61,22 @@ pub async fn get_block_time_stamp_offset( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_block_txids.rs b/crates/algod_client/src/apis/get_block_txids.rs index 0fa6ff526..768704f4a 100644 --- a/crates/algod_client/src/apis/get_block_txids.rs +++ b/crates/algod_client/src/apis/get_block_txids.rs @@ -58,7 +58,7 @@ pub async fn get_block_txids( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -67,16 +67,22 @@ pub async fn get_block_txids( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_config.rs b/crates/algod_client/src/apis/get_config.rs index b1673402a..88213c067 100644 --- a/crates/algod_client/src/apis/get_config.rs +++ b/crates/algod_client/src/apis/get_config.rs @@ -48,7 +48,7 @@ pub async fn get_config(http_client: &dyn HttpClient) -> Result { Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -57,16 +57,22 @@ pub async fn get_config(http_client: &dyn HttpClient) -> Result { .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_debug_settings_prof.rs b/crates/algod_client/src/apis/get_debug_settings_prof.rs index f5760d532..402232f5d 100644 --- a/crates/algod_client/src/apis/get_debug_settings_prof.rs +++ b/crates/algod_client/src/apis/get_debug_settings_prof.rs @@ -50,7 +50,7 @@ pub async fn get_debug_settings_prof( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -59,16 +59,22 @@ pub async fn get_debug_settings_prof( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_genesis.rs b/crates/algod_client/src/apis/get_genesis.rs index 692d06c74..107e9612d 100644 --- a/crates/algod_client/src/apis/get_genesis.rs +++ b/crates/algod_client/src/apis/get_genesis.rs @@ -49,7 +49,7 @@ pub async fn get_genesis(http_client: &dyn HttpClient) -> Result Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -58,16 +58,22 @@ pub async fn get_genesis(http_client: &dyn HttpClient) -> Result .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_ledger_state_delta.rs b/crates/algod_client/src/apis/get_ledger_state_delta.rs index 6c4017bd6..1cdf332ab 100644 --- a/crates/algod_client/src/apis/get_ledger_state_delta.rs +++ b/crates/algod_client/src/apis/get_ledger_state_delta.rs @@ -76,7 +76,7 @@ pub async fn get_ledger_state_delta( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -85,18 +85,22 @@ pub async fn get_ledger_state_delta( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_ledger_state_delta_for_transaction_group.rs b/crates/algod_client/src/apis/get_ledger_state_delta_for_transaction_group.rs index b447bc516..d73795dfd 100644 --- a/crates/algod_client/src/apis/get_ledger_state_delta_for_transaction_group.rs +++ b/crates/algod_client/src/apis/get_ledger_state_delta_for_transaction_group.rs @@ -79,7 +79,7 @@ pub async fn get_ledger_state_delta_for_transaction_group( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -88,18 +88,22 @@ pub async fn get_ledger_state_delta_for_transaction_group( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_light_block_header_proof.rs b/crates/algod_client/src/apis/get_light_block_header_proof.rs index fab093982..058868f52 100644 --- a/crates/algod_client/src/apis/get_light_block_header_proof.rs +++ b/crates/algod_client/src/apis/get_light_block_header_proof.rs @@ -59,7 +59,7 @@ pub async fn get_light_block_header_proof( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -68,16 +68,22 @@ pub async fn get_light_block_header_proof( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_participation_key_by_id.rs b/crates/algod_client/src/apis/get_participation_key_by_id.rs index cfceecc88..a1c8f0780 100644 --- a/crates/algod_client/src/apis/get_participation_key_by_id.rs +++ b/crates/algod_client/src/apis/get_participation_key_by_id.rs @@ -61,7 +61,7 @@ pub async fn get_participation_key_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -70,16 +70,22 @@ pub async fn get_participation_key_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_participation_keys.rs b/crates/algod_client/src/apis/get_participation_keys.rs index 20863021f..f0b983d58 100644 --- a/crates/algod_client/src/apis/get_participation_keys.rs +++ b/crates/algod_client/src/apis/get_participation_keys.rs @@ -55,7 +55,7 @@ pub async fn get_participation_keys( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -64,16 +64,22 @@ pub async fn get_participation_keys( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_pending_transactions.rs b/crates/algod_client/src/apis/get_pending_transactions.rs index 8c75b5137..5e6ccb9f8 100644 --- a/crates/algod_client/src/apis/get_pending_transactions.rs +++ b/crates/algod_client/src/apis/get_pending_transactions.rs @@ -77,7 +77,7 @@ pub async fn get_pending_transactions( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -86,18 +86,22 @@ pub async fn get_pending_transactions( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_pending_transactions_by_address.rs b/crates/algod_client/src/apis/get_pending_transactions_by_address.rs index d22cf754a..0342cdbe1 100644 --- a/crates/algod_client/src/apis/get_pending_transactions_by_address.rs +++ b/crates/algod_client/src/apis/get_pending_transactions_by_address.rs @@ -83,7 +83,7 @@ pub async fn get_pending_transactions_by_address( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -92,18 +92,22 @@ pub async fn get_pending_transactions_by_address( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_ready.rs b/crates/algod_client/src/apis/get_ready.rs index 1d8378272..4c1f4f016 100644 --- a/crates/algod_client/src/apis/get_ready.rs +++ b/crates/algod_client/src/apis/get_ready.rs @@ -50,7 +50,7 @@ pub async fn get_ready(http_client: &dyn HttpClient) -> Result<(), Error> { Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/get_state_proof.rs b/crates/algod_client/src/apis/get_state_proof.rs index 24413fe77..8ee9276d0 100644 --- a/crates/algod_client/src/apis/get_state_proof.rs +++ b/crates/algod_client/src/apis/get_state_proof.rs @@ -59,7 +59,7 @@ pub async fn get_state_proof( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -68,16 +68,22 @@ pub async fn get_state_proof( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_status.rs b/crates/algod_client/src/apis/get_status.rs index 2cd9c1d4c..6f354e6ba 100644 --- a/crates/algod_client/src/apis/get_status.rs +++ b/crates/algod_client/src/apis/get_status.rs @@ -51,7 +51,7 @@ pub async fn get_status(http_client: &dyn HttpClient) -> Result Result { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_supply.rs b/crates/algod_client/src/apis/get_supply.rs index 2a33ce387..8c6ad007d 100644 --- a/crates/algod_client/src/apis/get_supply.rs +++ b/crates/algod_client/src/apis/get_supply.rs @@ -50,7 +50,7 @@ pub async fn get_supply(http_client: &dyn HttpClient) -> Result Result { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_sync_round.rs b/crates/algod_client/src/apis/get_sync_round.rs index f026466e5..afecd2af2 100644 --- a/crates/algod_client/src/apis/get_sync_round.rs +++ b/crates/algod_client/src/apis/get_sync_round.rs @@ -53,7 +53,7 @@ pub async fn get_sync_round(http_client: &dyn HttpClient) -> Result Result { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_transaction_group_ledger_state_deltas_for_round.rs b/crates/algod_client/src/apis/get_transaction_group_ledger_state_deltas_for_round.rs index 690e50ec4..53f39d6d3 100644 --- a/crates/algod_client/src/apis/get_transaction_group_ledger_state_deltas_for_round.rs +++ b/crates/algod_client/src/apis/get_transaction_group_ledger_state_deltas_for_round.rs @@ -76,7 +76,7 @@ pub async fn get_transaction_group_ledger_state_deltas_for_round( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -85,18 +85,22 @@ pub async fn get_transaction_group_ledger_state_deltas_for_round( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_transaction_proof.rs b/crates/algod_client/src/apis/get_transaction_proof.rs index b8a86c6ed..a29b638af 100644 --- a/crates/algod_client/src/apis/get_transaction_proof.rs +++ b/crates/algod_client/src/apis/get_transaction_proof.rs @@ -75,7 +75,7 @@ pub async fn get_transaction_proof( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -84,16 +84,22 @@ pub async fn get_transaction_proof( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/get_version.rs b/crates/algod_client/src/apis/get_version.rs index dbed0f25d..93c2aba6d 100644 --- a/crates/algod_client/src/apis/get_version.rs +++ b/crates/algod_client/src/apis/get_version.rs @@ -48,7 +48,7 @@ pub async fn get_version(http_client: &dyn HttpClient) -> Result Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -57,16 +57,22 @@ pub async fn get_version(http_client: &dyn HttpClient) -> Result .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/health_check.rs b/crates/algod_client/src/apis/health_check.rs index 1cc348a48..25402601b 100644 --- a/crates/algod_client/src/apis/health_check.rs +++ b/crates/algod_client/src/apis/health_check.rs @@ -48,7 +48,7 @@ pub async fn health_check(http_client: &dyn HttpClient) -> Result<(), Error> { Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/metrics.rs b/crates/algod_client/src/apis/metrics.rs index d0178f25f..a9d031c46 100644 --- a/crates/algod_client/src/apis/metrics.rs +++ b/crates/algod_client/src/apis/metrics.rs @@ -48,7 +48,7 @@ pub async fn metrics(http_client: &dyn HttpClient) -> Result<(), Error> { Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/mod.rs b/crates/algod_client/src/apis/mod.rs index 5bb3cd62f..c9d6ae3e2 100644 --- a/crates/algod_client/src/apis/mod.rs +++ b/crates/algod_client/src/apis/mod.rs @@ -71,198 +71,200 @@ pub mod transaction_params; pub mod unset_sync_round; pub mod wait_for_block; +use snafu::Snafu; + /// Unified error type that can represent any API error from any endpoint -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum AlgodApiError { - #[error("Health_check error: {0:?}")] - HealthCheck(health_check::HealthCheckError), - #[error("Get_ready error: {0:?}")] - GetReady(get_ready::GetReadyError), - #[error("Metrics error: {0:?}")] - Metrics(metrics::MetricsError), - #[error("Get_genesis error: {0:?}")] - GetGenesis(get_genesis::GetGenesisError), - #[error("Swagger_json error: {0:?}")] - SwaggerJson(swagger_json::SwaggerJsonError), - #[error("Get_version error: {0:?}")] - GetVersion(get_version::GetVersionError), - #[error("Get_debug_settings_prof error: {0:?}")] - GetDebugSettingsProf(get_debug_settings_prof::GetDebugSettingsProfError), - #[error("Put_debug_settings_prof error: {0:?}")] - PutDebugSettingsProf(put_debug_settings_prof::PutDebugSettingsProfError), - #[error("Get_config error: {0:?}")] - GetConfig(get_config::GetConfigError), - #[error("Account_information error: {0:?}")] - AccountInformation(account_information::AccountInformationError), - #[error("Account_asset_information error: {0:?}")] - AccountAssetInformation(account_asset_information::AccountAssetInformationError), - #[error("Account_assets_information error: {0:?}")] - AccountAssetsInformation(account_assets_information::AccountAssetsInformationError), - #[error("Account_application_information error: {0:?}")] - AccountApplicationInformation(account_application_information::AccountApplicationInformationError), - #[error("Get_pending_transactions_by_address error: {0:?}")] - GetPendingTransactionsByAddress(get_pending_transactions_by_address::GetPendingTransactionsByAddressError), - #[error("Get_block error: {0:?}")] - GetBlock(get_block::GetBlockError), - #[error("Get_block_txids error: {0:?}")] - GetBlockTxids(get_block_txids::GetBlockTxidsError), - #[error("Get_block_hash error: {0:?}")] - GetBlockHash(get_block_hash::GetBlockHashError), - #[error("Get_transaction_proof error: {0:?}")] - GetTransactionProof(get_transaction_proof::GetTransactionProofError), - #[error("Get_block_logs error: {0:?}")] - GetBlockLogs(get_block_logs::GetBlockLogsError), - #[error("Get_supply error: {0:?}")] - GetSupply(get_supply::GetSupplyError), - #[error("Get_participation_keys error: {0:?}")] - GetParticipationKeys(get_participation_keys::GetParticipationKeysError), - #[error("Add_participation_key error: {0:?}")] - AddParticipationKey(add_participation_key::AddParticipationKeyError), - #[error("Generate_participation_keys error: {0:?}")] - GenerateParticipationKeys(generate_participation_keys::GenerateParticipationKeysError), - #[error("Get_participation_key_by_id error: {0:?}")] - GetParticipationKeyById(get_participation_key_by_id::GetParticipationKeyByIdError), - #[error("Append_keys error: {0:?}")] - AppendKeys(append_keys::AppendKeysError), - #[error("Delete_participation_key_by_id error: {0:?}")] - DeleteParticipationKeyById(delete_participation_key_by_id::DeleteParticipationKeyByIdError), - #[error("Shutdown_node error: {0:?}")] - ShutdownNode(shutdown_node::ShutdownNodeError), - #[error("Get_status error: {0:?}")] - GetStatus(get_status::GetStatusError), - #[error("Wait_for_block error: {0:?}")] - WaitForBlock(wait_for_block::WaitForBlockError), - #[error("Raw_transaction error: {0:?}")] - RawTransaction(raw_transaction::RawTransactionError), - #[error("Raw_transaction_async error: {0:?}")] - RawTransactionAsync(raw_transaction_async::RawTransactionAsyncError), - #[error("Simulate_transaction error: {0:?}")] - SimulateTransaction(simulate_transaction::SimulateTransactionError), - #[error("Transaction_params error: {0:?}")] - TransactionParams(transaction_params::TransactionParamsError), - #[error("Get_pending_transactions error: {0:?}")] - GetPendingTransactions(get_pending_transactions::GetPendingTransactionsError), - #[error("Pending_transaction_information error: {0:?}")] - PendingTransactionInformation(pending_transaction_information::PendingTransactionInformationError), - #[error("Get_ledger_state_delta error: {0:?}")] - GetLedgerStateDelta(get_ledger_state_delta::GetLedgerStateDeltaError), - #[error("Get_transaction_group_ledger_state_deltas_for_round error: {0:?}")] - GetTransactionGroupLedgerStateDeltasForRound(get_transaction_group_ledger_state_deltas_for_round::GetTransactionGroupLedgerStateDeltasForRoundError), - #[error("Get_ledger_state_delta_for_transaction_group error: {0:?}")] - GetLedgerStateDeltaForTransactionGroup(get_ledger_state_delta_for_transaction_group::GetLedgerStateDeltaForTransactionGroupError), - #[error("Get_state_proof error: {0:?}")] - GetStateProof(get_state_proof::GetStateProofError), - #[error("Get_light_block_header_proof error: {0:?}")] - GetLightBlockHeaderProof(get_light_block_header_proof::GetLightBlockHeaderProofError), - #[error("Get_application_by_id error: {0:?}")] - GetApplicationById(get_application_by_id::GetApplicationByIdError), - #[error("Get_application_boxes error: {0:?}")] - GetApplicationBoxes(get_application_boxes::GetApplicationBoxesError), - #[error("Get_application_box_by_name error: {0:?}")] - GetApplicationBoxByName(get_application_box_by_name::GetApplicationBoxByNameError), - #[error("Get_asset_by_id error: {0:?}")] - GetAssetById(get_asset_by_id::GetAssetByIdError), - #[error("Get_sync_round error: {0:?}")] - GetSyncRound(get_sync_round::GetSyncRoundError), - #[error("Unset_sync_round error: {0:?}")] - UnsetSyncRound(unset_sync_round::UnsetSyncRoundError), - #[error("Set_sync_round error: {0:?}")] - SetSyncRound(set_sync_round::SetSyncRoundError), - #[error("Teal_compile error: {0:?}")] - TealCompile(teal_compile::TealCompileError), - #[error("Teal_disassemble error: {0:?}")] - TealDisassemble(teal_disassemble::TealDisassembleError), - #[error("Start_catchup error: {0:?}")] - StartCatchup(start_catchup::StartCatchupError), - #[error("Abort_catchup error: {0:?}")] - AbortCatchup(abort_catchup::AbortCatchupError), - #[error("Teal_dryrun error: {0:?}")] - TealDryrun(teal_dryrun::TealDryrunError), - #[error("Experimental_check error: {0:?}")] - ExperimentalCheck(experimental_check::ExperimentalCheckError), - #[error("Get_block_time_stamp_offset error: {0:?}")] - GetBlockTimeStampOffset(get_block_time_stamp_offset::GetBlockTimeStampOffsetError), - #[error("Set_block_time_stamp_offset error: {0:?}")] - SetBlockTimeStampOffset(set_block_time_stamp_offset::SetBlockTimeStampOffsetError), - #[error("Unknown API error: {0}")] - Unknown(String), + #[snafu(display("Health_check error: {error:?}"))] + HealthCheck { error: health_check::HealthCheckError }, + #[snafu(display("Get_ready error: {error:?}"))] + GetReady { error: get_ready::GetReadyError }, + #[snafu(display("Metrics error: {error:?}"))] + Metrics { error: metrics::MetricsError }, + #[snafu(display("Get_genesis error: {error:?}"))] + GetGenesis { error: get_genesis::GetGenesisError }, + #[snafu(display("Swagger_json error: {error:?}"))] + SwaggerJson { error: swagger_json::SwaggerJsonError }, + #[snafu(display("Get_version error: {error:?}"))] + GetVersion { error: get_version::GetVersionError }, + #[snafu(display("Get_debug_settings_prof error: {error:?}"))] + GetDebugSettingsProf { error: get_debug_settings_prof::GetDebugSettingsProfError }, + #[snafu(display("Put_debug_settings_prof error: {error:?}"))] + PutDebugSettingsProf { error: put_debug_settings_prof::PutDebugSettingsProfError }, + #[snafu(display("Get_config error: {error:?}"))] + GetConfig { error: get_config::GetConfigError }, + #[snafu(display("Account_information error: {error:?}"))] + AccountInformation { error: account_information::AccountInformationError }, + #[snafu(display("Account_asset_information error: {error:?}"))] + AccountAssetInformation { error: account_asset_information::AccountAssetInformationError }, + #[snafu(display("Account_assets_information error: {error:?}"))] + AccountAssetsInformation { error: account_assets_information::AccountAssetsInformationError }, + #[snafu(display("Account_application_information error: {error:?}"))] + AccountApplicationInformation { error: account_application_information::AccountApplicationInformationError }, + #[snafu(display("Get_pending_transactions_by_address error: {error:?}"))] + GetPendingTransactionsByAddress { error: get_pending_transactions_by_address::GetPendingTransactionsByAddressError }, + #[snafu(display("Get_block error: {error:?}"))] + GetBlock { error: get_block::GetBlockError }, + #[snafu(display("Get_block_txids error: {error:?}"))] + GetBlockTxids { error: get_block_txids::GetBlockTxidsError }, + #[snafu(display("Get_block_hash error: {error:?}"))] + GetBlockHash { error: get_block_hash::GetBlockHashError }, + #[snafu(display("Get_transaction_proof error: {error:?}"))] + GetTransactionProof { error: get_transaction_proof::GetTransactionProofError }, + #[snafu(display("Get_block_logs error: {error:?}"))] + GetBlockLogs { error: get_block_logs::GetBlockLogsError }, + #[snafu(display("Get_supply error: {error:?}"))] + GetSupply { error: get_supply::GetSupplyError }, + #[snafu(display("Get_participation_keys error: {error:?}"))] + GetParticipationKeys { error: get_participation_keys::GetParticipationKeysError }, + #[snafu(display("Add_participation_key error: {error:?}"))] + AddParticipationKey { error: add_participation_key::AddParticipationKeyError }, + #[snafu(display("Generate_participation_keys error: {error:?}"))] + GenerateParticipationKeys { error: generate_participation_keys::GenerateParticipationKeysError }, + #[snafu(display("Get_participation_key_by_id error: {error:?}"))] + GetParticipationKeyById { error: get_participation_key_by_id::GetParticipationKeyByIdError }, + #[snafu(display("Append_keys error: {error:?}"))] + AppendKeys { error: append_keys::AppendKeysError }, + #[snafu(display("Delete_participation_key_by_id error: {error:?}"))] + DeleteParticipationKeyById { error: delete_participation_key_by_id::DeleteParticipationKeyByIdError }, + #[snafu(display("Shutdown_node error: {error:?}"))] + ShutdownNode { error: shutdown_node::ShutdownNodeError }, + #[snafu(display("Get_status error: {error:?}"))] + GetStatus { error: get_status::GetStatusError }, + #[snafu(display("Wait_for_block error: {error:?}"))] + WaitForBlock { error: wait_for_block::WaitForBlockError }, + #[snafu(display("Raw_transaction error: {error:?}"))] + RawTransaction { error: raw_transaction::RawTransactionError }, + #[snafu(display("Raw_transaction_async error: {error:?}"))] + RawTransactionAsync { error: raw_transaction_async::RawTransactionAsyncError }, + #[snafu(display("Simulate_transaction error: {error:?}"))] + SimulateTransaction { error: simulate_transaction::SimulateTransactionError }, + #[snafu(display("Transaction_params error: {error:?}"))] + TransactionParams { error: transaction_params::TransactionParamsError }, + #[snafu(display("Get_pending_transactions error: {error:?}"))] + GetPendingTransactions { error: get_pending_transactions::GetPendingTransactionsError }, + #[snafu(display("Pending_transaction_information error: {error:?}"))] + PendingTransactionInformation { error: pending_transaction_information::PendingTransactionInformationError }, + #[snafu(display("Get_ledger_state_delta error: {error:?}"))] + GetLedgerStateDelta { error: get_ledger_state_delta::GetLedgerStateDeltaError }, + #[snafu(display("Get_transaction_group_ledger_state_deltas_for_round error: {error:?}"))] + GetTransactionGroupLedgerStateDeltasForRound { error: get_transaction_group_ledger_state_deltas_for_round::GetTransactionGroupLedgerStateDeltasForRoundError }, + #[snafu(display("Get_ledger_state_delta_for_transaction_group error: {error:?}"))] + GetLedgerStateDeltaForTransactionGroup { error: get_ledger_state_delta_for_transaction_group::GetLedgerStateDeltaForTransactionGroupError }, + #[snafu(display("Get_state_proof error: {error:?}"))] + GetStateProof { error: get_state_proof::GetStateProofError }, + #[snafu(display("Get_light_block_header_proof error: {error:?}"))] + GetLightBlockHeaderProof { error: get_light_block_header_proof::GetLightBlockHeaderProofError }, + #[snafu(display("Get_application_by_id error: {error:?}"))] + GetApplicationById { error: get_application_by_id::GetApplicationByIdError }, + #[snafu(display("Get_application_boxes error: {error:?}"))] + GetApplicationBoxes { error: get_application_boxes::GetApplicationBoxesError }, + #[snafu(display("Get_application_box_by_name error: {error:?}"))] + GetApplicationBoxByName { error: get_application_box_by_name::GetApplicationBoxByNameError }, + #[snafu(display("Get_asset_by_id error: {error:?}"))] + GetAssetById { error: get_asset_by_id::GetAssetByIdError }, + #[snafu(display("Get_sync_round error: {error:?}"))] + GetSyncRound { error: get_sync_round::GetSyncRoundError }, + #[snafu(display("Unset_sync_round error: {error:?}"))] + UnsetSyncRound { error: unset_sync_round::UnsetSyncRoundError }, + #[snafu(display("Set_sync_round error: {error:?}"))] + SetSyncRound { error: set_sync_round::SetSyncRoundError }, + #[snafu(display("Teal_compile error: {error:?}"))] + TealCompile { error: teal_compile::TealCompileError }, + #[snafu(display("Teal_disassemble error: {error:?}"))] + TealDisassemble { error: teal_disassemble::TealDisassembleError }, + #[snafu(display("Start_catchup error: {error:?}"))] + StartCatchup { error: start_catchup::StartCatchupError }, + #[snafu(display("Abort_catchup error: {error:?}"))] + AbortCatchup { error: abort_catchup::AbortCatchupError }, + #[snafu(display("Teal_dryrun error: {error:?}"))] + TealDryrun { error: teal_dryrun::TealDryrunError }, + #[snafu(display("Experimental_check error: {error:?}"))] + ExperimentalCheck { error: experimental_check::ExperimentalCheckError }, + #[snafu(display("Get_block_time_stamp_offset error: {error:?}"))] + GetBlockTimeStampOffset { error: get_block_time_stamp_offset::GetBlockTimeStampOffsetError }, + #[snafu(display("Set_block_time_stamp_offset error: {error:?}"))] + SetBlockTimeStampOffset { error: set_block_time_stamp_offset::SetBlockTimeStampOffsetError }, + #[snafu(display("Unknown API error: {message}"))] + Unknown { message: String }, } impl From for AlgodApiError { fn from(err: health_check::HealthCheckError) -> Self { - AlgodApiError::HealthCheck(err) + AlgodApiError::HealthCheck { error: err } } } impl From for AlgodApiError { fn from(err: get_ready::GetReadyError) -> Self { - AlgodApiError::GetReady(err) + AlgodApiError::GetReady { error: err } } } impl From for AlgodApiError { fn from(err: metrics::MetricsError) -> Self { - AlgodApiError::Metrics(err) + AlgodApiError::Metrics { error: err } } } impl From for AlgodApiError { fn from(err: get_genesis::GetGenesisError) -> Self { - AlgodApiError::GetGenesis(err) + AlgodApiError::GetGenesis { error: err } } } impl From for AlgodApiError { fn from(err: swagger_json::SwaggerJsonError) -> Self { - AlgodApiError::SwaggerJson(err) + AlgodApiError::SwaggerJson { error: err } } } impl From for AlgodApiError { fn from(err: get_version::GetVersionError) -> Self { - AlgodApiError::GetVersion(err) + AlgodApiError::GetVersion { error: err } } } impl From for AlgodApiError { fn from(err: get_debug_settings_prof::GetDebugSettingsProfError) -> Self { - AlgodApiError::GetDebugSettingsProf(err) + AlgodApiError::GetDebugSettingsProf { error: err } } } impl From for AlgodApiError { fn from(err: put_debug_settings_prof::PutDebugSettingsProfError) -> Self { - AlgodApiError::PutDebugSettingsProf(err) + AlgodApiError::PutDebugSettingsProf { error: err } } } impl From for AlgodApiError { fn from(err: get_config::GetConfigError) -> Self { - AlgodApiError::GetConfig(err) + AlgodApiError::GetConfig { error: err } } } impl From for AlgodApiError { fn from(err: account_information::AccountInformationError) -> Self { - AlgodApiError::AccountInformation(err) + AlgodApiError::AccountInformation { error: err } } } impl From for AlgodApiError { fn from(err: account_asset_information::AccountAssetInformationError) -> Self { - AlgodApiError::AccountAssetInformation(err) + AlgodApiError::AccountAssetInformation { error: err } } } impl From for AlgodApiError { fn from(err: account_assets_information::AccountAssetsInformationError) -> Self { - AlgodApiError::AccountAssetsInformation(err) + AlgodApiError::AccountAssetsInformation { error: err } } } impl From for AlgodApiError { fn from(err: account_application_information::AccountApplicationInformationError) -> Self { - AlgodApiError::AccountApplicationInformation(err) + AlgodApiError::AccountApplicationInformation { error: err } } } @@ -272,145 +274,145 @@ impl From Self { - AlgodApiError::GetPendingTransactionsByAddress(err) + AlgodApiError::GetPendingTransactionsByAddress { error: err } } } impl From for AlgodApiError { fn from(err: get_block::GetBlockError) -> Self { - AlgodApiError::GetBlock(err) + AlgodApiError::GetBlock { error: err } } } impl From for AlgodApiError { fn from(err: get_block_txids::GetBlockTxidsError) -> Self { - AlgodApiError::GetBlockTxids(err) + AlgodApiError::GetBlockTxids { error: err } } } impl From for AlgodApiError { fn from(err: get_block_hash::GetBlockHashError) -> Self { - AlgodApiError::GetBlockHash(err) + AlgodApiError::GetBlockHash { error: err } } } impl From for AlgodApiError { fn from(err: get_transaction_proof::GetTransactionProofError) -> Self { - AlgodApiError::GetTransactionProof(err) + AlgodApiError::GetTransactionProof { error: err } } } impl From for AlgodApiError { fn from(err: get_block_logs::GetBlockLogsError) -> Self { - AlgodApiError::GetBlockLogs(err) + AlgodApiError::GetBlockLogs { error: err } } } impl From for AlgodApiError { fn from(err: get_supply::GetSupplyError) -> Self { - AlgodApiError::GetSupply(err) + AlgodApiError::GetSupply { error: err } } } impl From for AlgodApiError { fn from(err: get_participation_keys::GetParticipationKeysError) -> Self { - AlgodApiError::GetParticipationKeys(err) + AlgodApiError::GetParticipationKeys { error: err } } } impl From for AlgodApiError { fn from(err: add_participation_key::AddParticipationKeyError) -> Self { - AlgodApiError::AddParticipationKey(err) + AlgodApiError::AddParticipationKey { error: err } } } impl From for AlgodApiError { fn from(err: generate_participation_keys::GenerateParticipationKeysError) -> Self { - AlgodApiError::GenerateParticipationKeys(err) + AlgodApiError::GenerateParticipationKeys { error: err } } } impl From for AlgodApiError { fn from(err: get_participation_key_by_id::GetParticipationKeyByIdError) -> Self { - AlgodApiError::GetParticipationKeyById(err) + AlgodApiError::GetParticipationKeyById { error: err } } } impl From for AlgodApiError { fn from(err: append_keys::AppendKeysError) -> Self { - AlgodApiError::AppendKeys(err) + AlgodApiError::AppendKeys { error: err } } } impl From for AlgodApiError { fn from(err: delete_participation_key_by_id::DeleteParticipationKeyByIdError) -> Self { - AlgodApiError::DeleteParticipationKeyById(err) + AlgodApiError::DeleteParticipationKeyById { error: err } } } impl From for AlgodApiError { fn from(err: shutdown_node::ShutdownNodeError) -> Self { - AlgodApiError::ShutdownNode(err) + AlgodApiError::ShutdownNode { error: err } } } impl From for AlgodApiError { fn from(err: get_status::GetStatusError) -> Self { - AlgodApiError::GetStatus(err) + AlgodApiError::GetStatus { error: err } } } impl From for AlgodApiError { fn from(err: wait_for_block::WaitForBlockError) -> Self { - AlgodApiError::WaitForBlock(err) + AlgodApiError::WaitForBlock { error: err } } } impl From for AlgodApiError { fn from(err: raw_transaction::RawTransactionError) -> Self { - AlgodApiError::RawTransaction(err) + AlgodApiError::RawTransaction { error: err } } } impl From for AlgodApiError { fn from(err: raw_transaction_async::RawTransactionAsyncError) -> Self { - AlgodApiError::RawTransactionAsync(err) + AlgodApiError::RawTransactionAsync { error: err } } } impl From for AlgodApiError { fn from(err: simulate_transaction::SimulateTransactionError) -> Self { - AlgodApiError::SimulateTransaction(err) + AlgodApiError::SimulateTransaction { error: err } } } impl From for AlgodApiError { fn from(err: transaction_params::TransactionParamsError) -> Self { - AlgodApiError::TransactionParams(err) + AlgodApiError::TransactionParams { error: err } } } impl From for AlgodApiError { fn from(err: get_pending_transactions::GetPendingTransactionsError) -> Self { - AlgodApiError::GetPendingTransactions(err) + AlgodApiError::GetPendingTransactions { error: err } } } impl From for AlgodApiError { fn from(err: pending_transaction_information::PendingTransactionInformationError) -> Self { - AlgodApiError::PendingTransactionInformation(err) + AlgodApiError::PendingTransactionInformation { error: err } } } impl From for AlgodApiError { fn from(err: get_ledger_state_delta::GetLedgerStateDeltaError) -> Self { - AlgodApiError::GetLedgerStateDelta(err) + AlgodApiError::GetLedgerStateDelta { error: err } } } impl From for AlgodApiError { fn from(err: get_transaction_group_ledger_state_deltas_for_round::GetTransactionGroupLedgerStateDeltasForRoundError) -> Self { - AlgodApiError::GetTransactionGroupLedgerStateDeltasForRound(err) + AlgodApiError::GetTransactionGroupLedgerStateDeltasForRound { error: err } } } @@ -420,121 +422,123 @@ impl From Self { - AlgodApiError::GetLedgerStateDeltaForTransactionGroup(err) + AlgodApiError::GetLedgerStateDeltaForTransactionGroup { error: err } } } impl From for AlgodApiError { fn from(err: get_state_proof::GetStateProofError) -> Self { - AlgodApiError::GetStateProof(err) + AlgodApiError::GetStateProof { error: err } } } impl From for AlgodApiError { fn from(err: get_light_block_header_proof::GetLightBlockHeaderProofError) -> Self { - AlgodApiError::GetLightBlockHeaderProof(err) + AlgodApiError::GetLightBlockHeaderProof { error: err } } } impl From for AlgodApiError { fn from(err: get_application_by_id::GetApplicationByIdError) -> Self { - AlgodApiError::GetApplicationById(err) + AlgodApiError::GetApplicationById { error: err } } } impl From for AlgodApiError { fn from(err: get_application_boxes::GetApplicationBoxesError) -> Self { - AlgodApiError::GetApplicationBoxes(err) + AlgodApiError::GetApplicationBoxes { error: err } } } impl From for AlgodApiError { fn from(err: get_application_box_by_name::GetApplicationBoxByNameError) -> Self { - AlgodApiError::GetApplicationBoxByName(err) + AlgodApiError::GetApplicationBoxByName { error: err } } } impl From for AlgodApiError { fn from(err: get_asset_by_id::GetAssetByIdError) -> Self { - AlgodApiError::GetAssetById(err) + AlgodApiError::GetAssetById { error: err } } } impl From for AlgodApiError { fn from(err: get_sync_round::GetSyncRoundError) -> Self { - AlgodApiError::GetSyncRound(err) + AlgodApiError::GetSyncRound { error: err } } } impl From for AlgodApiError { fn from(err: unset_sync_round::UnsetSyncRoundError) -> Self { - AlgodApiError::UnsetSyncRound(err) + AlgodApiError::UnsetSyncRound { error: err } } } impl From for AlgodApiError { fn from(err: set_sync_round::SetSyncRoundError) -> Self { - AlgodApiError::SetSyncRound(err) + AlgodApiError::SetSyncRound { error: err } } } impl From for AlgodApiError { fn from(err: teal_compile::TealCompileError) -> Self { - AlgodApiError::TealCompile(err) + AlgodApiError::TealCompile { error: err } } } impl From for AlgodApiError { fn from(err: teal_disassemble::TealDisassembleError) -> Self { - AlgodApiError::TealDisassemble(err) + AlgodApiError::TealDisassemble { error: err } } } impl From for AlgodApiError { fn from(err: start_catchup::StartCatchupError) -> Self { - AlgodApiError::StartCatchup(err) + AlgodApiError::StartCatchup { error: err } } } impl From for AlgodApiError { fn from(err: abort_catchup::AbortCatchupError) -> Self { - AlgodApiError::AbortCatchup(err) + AlgodApiError::AbortCatchup { error: err } } } impl From for AlgodApiError { fn from(err: teal_dryrun::TealDryrunError) -> Self { - AlgodApiError::TealDryrun(err) + AlgodApiError::TealDryrun { error: err } } } impl From for AlgodApiError { fn from(err: experimental_check::ExperimentalCheckError) -> Self { - AlgodApiError::ExperimentalCheck(err) + AlgodApiError::ExperimentalCheck { error: err } } } impl From for AlgodApiError { fn from(err: get_block_time_stamp_offset::GetBlockTimeStampOffsetError) -> Self { - AlgodApiError::GetBlockTimeStampOffset(err) + AlgodApiError::GetBlockTimeStampOffset { error: err } } } impl From for AlgodApiError { fn from(err: set_block_time_stamp_offset::SetBlockTimeStampOffsetError) -> Self { - AlgodApiError::SetBlockTimeStampOffset(err) + AlgodApiError::SetBlockTimeStampOffset { error: err } } } /// The main error type for all algod client operations -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum Error { - #[error("HTTP error: {0}")] - Http(#[from] algokit_http_client::HttpError), - #[error("Serialization error: {0}")] - Serde(String), - #[error("API error: {0}")] - Api(#[from] AlgodApiError), + #[snafu(display("HTTP error: {source}"))] + Http { + source: algokit_http_client::HttpError, + }, + #[snafu(display("Serialization error: {message}"))] + Serde { message: String }, + #[snafu(display("API error: {source}"))] + Api { source: AlgodApiError }, } #[derive(Debug, PartialEq, Eq)] diff --git a/crates/algod_client/src/apis/pending_transaction_information.rs b/crates/algod_client/src/apis/pending_transaction_information.rs index 275fdfdf0..ed19de0b3 100644 --- a/crates/algod_client/src/apis/pending_transaction_information.rs +++ b/crates/algod_client/src/apis/pending_transaction_information.rs @@ -82,7 +82,7 @@ pub async fn pending_transaction_information( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -91,18 +91,22 @@ pub async fn pending_transaction_information( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/put_debug_settings_prof.rs b/crates/algod_client/src/apis/put_debug_settings_prof.rs index b69b91a61..9a27ce7bc 100644 --- a/crates/algod_client/src/apis/put_debug_settings_prof.rs +++ b/crates/algod_client/src/apis/put_debug_settings_prof.rs @@ -50,7 +50,7 @@ pub async fn put_debug_settings_prof( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -59,16 +59,22 @@ pub async fn put_debug_settings_prof( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/raw_transaction.rs b/crates/algod_client/src/apis/raw_transaction.rs index 00c7d82cf..6e008d238 100644 --- a/crates/algod_client/src/apis/raw_transaction.rs +++ b/crates/algod_client/src/apis/raw_transaction.rs @@ -62,7 +62,7 @@ pub async fn raw_transaction( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -71,18 +71,22 @@ pub async fn raw_transaction( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/raw_transaction_async.rs b/crates/algod_client/src/apis/raw_transaction_async.rs index 3869f405f..ca7d8fa92 100644 --- a/crates/algod_client/src/apis/raw_transaction_async.rs +++ b/crates/algod_client/src/apis/raw_transaction_async.rs @@ -63,7 +63,7 @@ pub async fn raw_transaction_async( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/set_block_time_stamp_offset.rs b/crates/algod_client/src/apis/set_block_time_stamp_offset.rs index f568f1f80..8de07abd6 100644 --- a/crates/algod_client/src/apis/set_block_time_stamp_offset.rs +++ b/crates/algod_client/src/apis/set_block_time_stamp_offset.rs @@ -57,7 +57,7 @@ pub async fn set_block_time_stamp_offset( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/set_sync_round.rs b/crates/algod_client/src/apis/set_sync_round.rs index 4d92c3b96..305f5d5a2 100644 --- a/crates/algod_client/src/apis/set_sync_round.rs +++ b/crates/algod_client/src/apis/set_sync_round.rs @@ -55,7 +55,7 @@ pub async fn set_sync_round(http_client: &dyn HttpClient, round: u64) -> Result< Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/shutdown_node.rs b/crates/algod_client/src/apis/shutdown_node.rs index 9d425d2df..e06d1021f 100644 --- a/crates/algod_client/src/apis/shutdown_node.rs +++ b/crates/algod_client/src/apis/shutdown_node.rs @@ -55,7 +55,7 @@ pub async fn shutdown_node( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -64,16 +64,22 @@ pub async fn shutdown_node( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/simulate_transaction.rs b/crates/algod_client/src/apis/simulate_transaction.rs index 370bca786..d3668bef0 100644 --- a/crates/algod_client/src/apis/simulate_transaction.rs +++ b/crates/algod_client/src/apis/simulate_transaction.rs @@ -66,9 +66,15 @@ pub async fn simulate_transaction( } let body = if use_msgpack { - Some(rmp_serde::to_vec_named(&p_request).map_err(|e| Error::Serde(e.to_string()))?) + Some( + rmp_serde::to_vec_named(&p_request).map_err(|e| Error::Serde { + message: e.to_string(), + })?, + ) } else { - Some(serde_json::to_vec(&p_request).map_err(|e| Error::Serde(e.to_string()))?) + Some(serde_json::to_vec(&p_request).map_err(|e| Error::Serde { + message: e.to_string(), + })?) }; let response = http_client @@ -80,7 +86,7 @@ pub async fn simulate_transaction( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -89,18 +95,22 @@ pub async fn simulate_transaction( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/start_catchup.rs b/crates/algod_client/src/apis/start_catchup.rs index 01a74a16a..eb3d63eb6 100644 --- a/crates/algod_client/src/apis/start_catchup.rs +++ b/crates/algod_client/src/apis/start_catchup.rs @@ -66,7 +66,7 @@ pub async fn start_catchup( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -75,16 +75,22 @@ pub async fn start_catchup( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/swagger_json.rs b/crates/algod_client/src/apis/swagger_json.rs index c8d0be433..726f9abf3 100644 --- a/crates/algod_client/src/apis/swagger_json.rs +++ b/crates/algod_client/src/apis/swagger_json.rs @@ -48,7 +48,7 @@ pub async fn swagger_json(http_client: &dyn HttpClient) -> Result Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -57,16 +57,22 @@ pub async fn swagger_json(http_client: &dyn HttpClient) -> Result .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/teal_compile.rs b/crates/algod_client/src/apis/teal_compile.rs index fffd40a80..cdb774e07 100644 --- a/crates/algod_client/src/apis/teal_compile.rs +++ b/crates/algod_client/src/apis/teal_compile.rs @@ -63,7 +63,7 @@ pub async fn teal_compile( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -72,16 +72,22 @@ pub async fn teal_compile( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/teal_disassemble.rs b/crates/algod_client/src/apis/teal_disassemble.rs index 36eea64ce..4478dd1a4 100644 --- a/crates/algod_client/src/apis/teal_disassemble.rs +++ b/crates/algod_client/src/apis/teal_disassemble.rs @@ -51,7 +51,9 @@ pub async fn teal_disassemble( ); headers.insert("Accept".to_string(), "application/msgpack".to_string()); - let body = Some(serde_json::to_vec(&p_request).map_err(|e| Error::Serde(e.to_string()))?); + let body = Some(serde_json::to_vec(&p_request).map_err(|e| Error::Serde { + message: e.to_string(), + })?); let response = http_client .request( @@ -62,7 +64,7 @@ pub async fn teal_disassemble( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -71,18 +73,22 @@ pub async fn teal_disassemble( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/teal_dryrun.rs b/crates/algod_client/src/apis/teal_dryrun.rs index 05d0822df..c51702d58 100644 --- a/crates/algod_client/src/apis/teal_dryrun.rs +++ b/crates/algod_client/src/apis/teal_dryrun.rs @@ -52,7 +52,11 @@ pub async fn teal_dryrun( ); headers.insert("Accept".to_string(), "application/msgpack".to_string()); - let body = Some(rmp_serde::to_vec_named(&p_request).map_err(|e| Error::Serde(e.to_string()))?); + let body = Some( + rmp_serde::to_vec_named(&p_request).map_err(|e| Error::Serde { + message: e.to_string(), + })?, + ); let response = http_client .request( @@ -63,7 +67,7 @@ pub async fn teal_dryrun( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -72,18 +76,22 @@ pub async fn teal_dryrun( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => { - rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => rmp_serde::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/transaction_params.rs b/crates/algod_client/src/apis/transaction_params.rs index d1f7b820a..57e485009 100644 --- a/crates/algod_client/src/apis/transaction_params.rs +++ b/crates/algod_client/src/apis/transaction_params.rs @@ -52,7 +52,7 @@ pub async fn transaction_params(http_client: &dyn HttpClient) -> Result Result { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algod_client/src/apis/unset_sync_round.rs b/crates/algod_client/src/apis/unset_sync_round.rs index cfd64a36e..f1c840d8c 100644 --- a/crates/algod_client/src/apis/unset_sync_round.rs +++ b/crates/algod_client/src/apis/unset_sync_round.rs @@ -53,7 +53,7 @@ pub async fn unset_sync_round(http_client: &dyn HttpClient) -> Result<(), Error> Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let _ = response; Ok(()) diff --git a/crates/algod_client/src/apis/wait_for_block.rs b/crates/algod_client/src/apis/wait_for_block.rs index 6c2b77b90..da405d5a6 100644 --- a/crates/algod_client/src/apis/wait_for_block.rs +++ b/crates/algod_client/src/apis/wait_for_block.rs @@ -58,7 +58,7 @@ pub async fn wait_for_block( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -67,16 +67,22 @@ pub async fn wait_for_block( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/algokit_utils/src/testing/indexer_helpers.rs b/crates/algokit_utils/src/testing/indexer_helpers.rs index 6d6d4d8fd..040d4df8a 100644 --- a/crates/algokit_utils/src/testing/indexer_helpers.rs +++ b/crates/algokit_utils/src/testing/indexer_helpers.rs @@ -121,7 +121,7 @@ pub async fn wait_for_indexer_transaction( .and_then(|response| { if response.transactions.is_empty() { // Return a string error that will be treated as "not found" - Err(IndexerError::Serde("Transaction not found".to_string())) + Err(IndexerError::Serde { message: "Transaction not found".to_string() }) } else { Ok(()) } diff --git a/crates/algokit_utils/src/transactions/composer.rs b/crates/algokit_utils/src/transactions/composer.rs index 6793b3cec..c26f4dae5 100644 --- a/crates/algokit_utils/src/transactions/composer.rs +++ b/crates/algokit_utils/src/transactions/composer.rs @@ -1368,11 +1368,11 @@ impl Composer { // All other errors indicate permanent issues and should fail fast let is_retryable = matches!( &error, - algod_client::apis::Error::Api( - algod_client::apis::AlgodApiError::PendingTransactionInformation( - algod_client::apis::pending_transaction_information::PendingTransactionInformationError::Status404(_) - ) - ) + algod_client::apis::Error::Api { + source: algod_client::apis::AlgodApiError::PendingTransactionInformation { + error: algod_client::apis::pending_transaction_information::PendingTransactionInformationError::Status404(_) + } + } ) || error.to_string().contains("404"); if is_retryable { diff --git a/crates/indexer_client/Cargo.toml b/crates/indexer_client/Cargo.toml index 636d67284..4ad45d66f 100644 --- a/crates/indexer_client/Cargo.toml +++ b/crates/indexer_client/Cargo.toml @@ -28,7 +28,7 @@ url = "^2.5" # Error handling -thiserror = "^1.0" +snafu = { workspace = true } # Utilities base64 = "^0.22" @@ -36,4 +36,4 @@ uuid = { version = "^1.0", features = ["v4"] } [dev-dependencies] tokio = { version = "1.0", features = ["full"] } -tokio-test = "^0.4" +tokio-test = "^0.4" \ No newline at end of file diff --git a/crates/indexer_client/src/apis/lookup_account_app_local_states.rs b/crates/indexer_client/src/apis/lookup_account_app_local_states.rs index c8a6f3d2b..be3ec4169 100644 --- a/crates/indexer_client/src/apis/lookup_account_app_local_states.rs +++ b/crates/indexer_client/src/apis/lookup_account_app_local_states.rs @@ -78,7 +78,7 @@ pub async fn lookup_account_app_local_states( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -87,16 +87,22 @@ pub async fn lookup_account_app_local_states( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_account_assets.rs b/crates/indexer_client/src/apis/lookup_account_assets.rs index 8ec98059d..360716779 100644 --- a/crates/indexer_client/src/apis/lookup_account_assets.rs +++ b/crates/indexer_client/src/apis/lookup_account_assets.rs @@ -78,7 +78,7 @@ pub async fn lookup_account_assets( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -87,16 +87,22 @@ pub async fn lookup_account_assets( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_account_by_id.rs b/crates/indexer_client/src/apis/lookup_account_by_id.rs index ef1209ad4..1580c0e24 100644 --- a/crates/indexer_client/src/apis/lookup_account_by_id.rs +++ b/crates/indexer_client/src/apis/lookup_account_by_id.rs @@ -73,7 +73,7 @@ pub async fn lookup_account_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -82,16 +82,22 @@ pub async fn lookup_account_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_account_created_applications.rs b/crates/indexer_client/src/apis/lookup_account_created_applications.rs index 3174c7a8f..cdfa81dc0 100644 --- a/crates/indexer_client/src/apis/lookup_account_created_applications.rs +++ b/crates/indexer_client/src/apis/lookup_account_created_applications.rs @@ -78,7 +78,7 @@ pub async fn lookup_account_created_applications( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -87,16 +87,22 @@ pub async fn lookup_account_created_applications( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_account_created_assets.rs b/crates/indexer_client/src/apis/lookup_account_created_assets.rs index 00d28a1d4..8f2986e9e 100644 --- a/crates/indexer_client/src/apis/lookup_account_created_assets.rs +++ b/crates/indexer_client/src/apis/lookup_account_created_assets.rs @@ -78,7 +78,7 @@ pub async fn lookup_account_created_assets( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -87,16 +87,22 @@ pub async fn lookup_account_created_assets( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_account_transactions.rs b/crates/indexer_client/src/apis/lookup_account_transactions.rs index bfe974801..982becd65 100644 --- a/crates/indexer_client/src/apis/lookup_account_transactions.rs +++ b/crates/indexer_client/src/apis/lookup_account_transactions.rs @@ -133,7 +133,7 @@ pub async fn lookup_account_transactions( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -142,16 +142,22 @@ pub async fn lookup_account_transactions( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_application_box_by_id_and_name.rs b/crates/indexer_client/src/apis/lookup_application_box_by_id_and_name.rs index 19acfed78..4fb0b5a7a 100644 --- a/crates/indexer_client/src/apis/lookup_application_box_by_id_and_name.rs +++ b/crates/indexer_client/src/apis/lookup_application_box_by_id_and_name.rs @@ -61,7 +61,7 @@ pub async fn lookup_application_box_by_id_and_name( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -70,16 +70,22 @@ pub async fn lookup_application_box_by_id_and_name( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_application_by_id.rs b/crates/indexer_client/src/apis/lookup_application_by_id.rs index ae614e49b..ef612bb54 100644 --- a/crates/indexer_client/src/apis/lookup_application_by_id.rs +++ b/crates/indexer_client/src/apis/lookup_application_by_id.rs @@ -62,7 +62,7 @@ pub async fn lookup_application_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -71,16 +71,22 @@ pub async fn lookup_application_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_application_logs_by_id.rs b/crates/indexer_client/src/apis/lookup_application_logs_by_id.rs index 73b3f030d..994fc7630 100644 --- a/crates/indexer_client/src/apis/lookup_application_logs_by_id.rs +++ b/crates/indexer_client/src/apis/lookup_application_logs_by_id.rs @@ -85,7 +85,7 @@ pub async fn lookup_application_logs_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -94,16 +94,22 @@ pub async fn lookup_application_logs_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_asset_balances.rs b/crates/indexer_client/src/apis/lookup_asset_balances.rs index cfa71506b..724f921cb 100644 --- a/crates/indexer_client/src/apis/lookup_asset_balances.rs +++ b/crates/indexer_client/src/apis/lookup_asset_balances.rs @@ -79,7 +79,7 @@ pub async fn lookup_asset_balances( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -88,16 +88,22 @@ pub async fn lookup_asset_balances( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_asset_by_id.rs b/crates/indexer_client/src/apis/lookup_asset_by_id.rs index 16bdcb568..bb063dfd2 100644 --- a/crates/indexer_client/src/apis/lookup_asset_by_id.rs +++ b/crates/indexer_client/src/apis/lookup_asset_by_id.rs @@ -60,7 +60,7 @@ pub async fn lookup_asset_by_id( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -69,16 +69,22 @@ pub async fn lookup_asset_by_id( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_asset_transactions.rs b/crates/indexer_client/src/apis/lookup_asset_transactions.rs index c28092402..fc327756f 100644 --- a/crates/indexer_client/src/apis/lookup_asset_transactions.rs +++ b/crates/indexer_client/src/apis/lookup_asset_transactions.rs @@ -140,7 +140,7 @@ pub async fn lookup_asset_transactions( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -149,16 +149,22 @@ pub async fn lookup_asset_transactions( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_block.rs b/crates/indexer_client/src/apis/lookup_block.rs index c7636b4f0..d9b98f587 100644 --- a/crates/indexer_client/src/apis/lookup_block.rs +++ b/crates/indexer_client/src/apis/lookup_block.rs @@ -59,7 +59,7 @@ pub async fn lookup_block( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -68,16 +68,22 @@ pub async fn lookup_block( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/lookup_transaction.rs b/crates/indexer_client/src/apis/lookup_transaction.rs index 89057339e..18e7e1d7a 100644 --- a/crates/indexer_client/src/apis/lookup_transaction.rs +++ b/crates/indexer_client/src/apis/lookup_transaction.rs @@ -58,7 +58,7 @@ pub async fn lookup_transaction( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -67,16 +67,22 @@ pub async fn lookup_transaction( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/make_health_check.rs b/crates/indexer_client/src/apis/make_health_check.rs index 2b1019658..4fbf440f9 100644 --- a/crates/indexer_client/src/apis/make_health_check.rs +++ b/crates/indexer_client/src/apis/make_health_check.rs @@ -48,7 +48,7 @@ pub async fn make_health_check(http_client: &dyn HttpClient) -> Result Result { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/mod.rs b/crates/indexer_client/src/apis/mod.rs index 77828e48a..898bae13d 100644 --- a/crates/indexer_client/src/apis/mod.rs +++ b/crates/indexer_client/src/apis/mod.rs @@ -36,92 +36,132 @@ pub mod search_for_assets; pub mod search_for_block_headers; pub mod search_for_transactions; +use snafu::Snafu; + /// Unified error type that can represent any API error from any endpoint -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum IndexerApiError { - #[error("Make_health_check error: {0:?}")] - MakeHealthCheck(make_health_check::MakeHealthCheckError), - #[error("Search_for_accounts error: {0:?}")] - SearchForAccounts(search_for_accounts::SearchForAccountsError), - #[error("Lookup_account_by_id error: {0:?}")] - LookupAccountById(lookup_account_by_id::LookupAccountByIdError), - #[error("Lookup_account_assets error: {0:?}")] - LookupAccountAssets(lookup_account_assets::LookupAccountAssetsError), - #[error("Lookup_account_created_assets error: {0:?}")] - LookupAccountCreatedAssets(lookup_account_created_assets::LookupAccountCreatedAssetsError), - #[error("Lookup_account_app_local_states error: {0:?}")] - LookupAccountAppLocalStates(lookup_account_app_local_states::LookupAccountAppLocalStatesError), - #[error("Lookup_account_created_applications error: {0:?}")] - LookupAccountCreatedApplications( - lookup_account_created_applications::LookupAccountCreatedApplicationsError, - ), - #[error("Lookup_account_transactions error: {0:?}")] - LookupAccountTransactions(lookup_account_transactions::LookupAccountTransactionsError), - #[error("Search_for_applications error: {0:?}")] - SearchForApplications(search_for_applications::SearchForApplicationsError), - #[error("Lookup_application_by_id error: {0:?}")] - LookupApplicationById(lookup_application_by_id::LookupApplicationByIdError), - #[error("Search_for_application_boxes error: {0:?}")] - SearchForApplicationBoxes(search_for_application_boxes::SearchForApplicationBoxesError), - #[error("Lookup_application_box_by_id_and_name error: {0:?}")] - LookupApplicationBoxByIdAndName( - lookup_application_box_by_id_and_name::LookupApplicationBoxByIdAndNameError, - ), - #[error("Lookup_application_logs_by_id error: {0:?}")] - LookupApplicationLogsById(lookup_application_logs_by_id::LookupApplicationLogsByIdError), - #[error("Search_for_assets error: {0:?}")] - SearchForAssets(search_for_assets::SearchForAssetsError), - #[error("Lookup_asset_by_id error: {0:?}")] - LookupAssetById(lookup_asset_by_id::LookupAssetByIdError), - #[error("Lookup_asset_balances error: {0:?}")] - LookupAssetBalances(lookup_asset_balances::LookupAssetBalancesError), - #[error("Lookup_asset_transactions error: {0:?}")] - LookupAssetTransactions(lookup_asset_transactions::LookupAssetTransactionsError), - #[error("Search_for_block_headers error: {0:?}")] - SearchForBlockHeaders(search_for_block_headers::SearchForBlockHeadersError), - #[error("Lookup_block error: {0:?}")] - LookupBlock(lookup_block::LookupBlockError), - #[error("Lookup_transaction error: {0:?}")] - LookupTransaction(lookup_transaction::LookupTransactionError), - #[error("Search_for_transactions error: {0:?}")] - SearchForTransactions(search_for_transactions::SearchForTransactionsError), - #[error("Unknown API error: {0}")] - Unknown(String), + #[snafu(display("Make_health_check error: {error:?}"))] + MakeHealthCheck { + error: make_health_check::MakeHealthCheckError, + }, + #[snafu(display("Search_for_accounts error: {error:?}"))] + SearchForAccounts { + error: search_for_accounts::SearchForAccountsError, + }, + #[snafu(display("Lookup_account_by_id error: {error:?}"))] + LookupAccountById { + error: lookup_account_by_id::LookupAccountByIdError, + }, + #[snafu(display("Lookup_account_assets error: {error:?}"))] + LookupAccountAssets { + error: lookup_account_assets::LookupAccountAssetsError, + }, + #[snafu(display("Lookup_account_created_assets error: {error:?}"))] + LookupAccountCreatedAssets { + error: lookup_account_created_assets::LookupAccountCreatedAssetsError, + }, + #[snafu(display("Lookup_account_app_local_states error: {error:?}"))] + LookupAccountAppLocalStates { + error: lookup_account_app_local_states::LookupAccountAppLocalStatesError, + }, + #[snafu(display("Lookup_account_created_applications error: {error:?}"))] + LookupAccountCreatedApplications { + error: lookup_account_created_applications::LookupAccountCreatedApplicationsError, + }, + #[snafu(display("Lookup_account_transactions error: {error:?}"))] + LookupAccountTransactions { + error: lookup_account_transactions::LookupAccountTransactionsError, + }, + #[snafu(display("Search_for_applications error: {error:?}"))] + SearchForApplications { + error: search_for_applications::SearchForApplicationsError, + }, + #[snafu(display("Lookup_application_by_id error: {error:?}"))] + LookupApplicationById { + error: lookup_application_by_id::LookupApplicationByIdError, + }, + #[snafu(display("Search_for_application_boxes error: {error:?}"))] + SearchForApplicationBoxes { + error: search_for_application_boxes::SearchForApplicationBoxesError, + }, + #[snafu(display("Lookup_application_box_by_id_and_name error: {error:?}"))] + LookupApplicationBoxByIdAndName { + error: lookup_application_box_by_id_and_name::LookupApplicationBoxByIdAndNameError, + }, + #[snafu(display("Lookup_application_logs_by_id error: {error:?}"))] + LookupApplicationLogsById { + error: lookup_application_logs_by_id::LookupApplicationLogsByIdError, + }, + #[snafu(display("Search_for_assets error: {error:?}"))] + SearchForAssets { + error: search_for_assets::SearchForAssetsError, + }, + #[snafu(display("Lookup_asset_by_id error: {error:?}"))] + LookupAssetById { + error: lookup_asset_by_id::LookupAssetByIdError, + }, + #[snafu(display("Lookup_asset_balances error: {error:?}"))] + LookupAssetBalances { + error: lookup_asset_balances::LookupAssetBalancesError, + }, + #[snafu(display("Lookup_asset_transactions error: {error:?}"))] + LookupAssetTransactions { + error: lookup_asset_transactions::LookupAssetTransactionsError, + }, + #[snafu(display("Search_for_block_headers error: {error:?}"))] + SearchForBlockHeaders { + error: search_for_block_headers::SearchForBlockHeadersError, + }, + #[snafu(display("Lookup_block error: {error:?}"))] + LookupBlock { + error: lookup_block::LookupBlockError, + }, + #[snafu(display("Lookup_transaction error: {error:?}"))] + LookupTransaction { + error: lookup_transaction::LookupTransactionError, + }, + #[snafu(display("Search_for_transactions error: {error:?}"))] + SearchForTransactions { + error: search_for_transactions::SearchForTransactionsError, + }, + #[snafu(display("Unknown API error: {message}"))] + Unknown { message: String }, } impl From for IndexerApiError { fn from(err: make_health_check::MakeHealthCheckError) -> Self { - IndexerApiError::MakeHealthCheck(err) + IndexerApiError::MakeHealthCheck { error: err } } } impl From for IndexerApiError { fn from(err: search_for_accounts::SearchForAccountsError) -> Self { - IndexerApiError::SearchForAccounts(err) + IndexerApiError::SearchForAccounts { error: err } } } impl From for IndexerApiError { fn from(err: lookup_account_by_id::LookupAccountByIdError) -> Self { - IndexerApiError::LookupAccountById(err) + IndexerApiError::LookupAccountById { error: err } } } impl From for IndexerApiError { fn from(err: lookup_account_assets::LookupAccountAssetsError) -> Self { - IndexerApiError::LookupAccountAssets(err) + IndexerApiError::LookupAccountAssets { error: err } } } impl From for IndexerApiError { fn from(err: lookup_account_created_assets::LookupAccountCreatedAssetsError) -> Self { - IndexerApiError::LookupAccountCreatedAssets(err) + IndexerApiError::LookupAccountCreatedAssets { error: err } } } impl From for IndexerApiError { fn from(err: lookup_account_app_local_states::LookupAccountAppLocalStatesError) -> Self { - IndexerApiError::LookupAccountAppLocalStates(err) + IndexerApiError::LookupAccountAppLocalStates { error: err } } } @@ -131,31 +171,31 @@ impl From Self { - IndexerApiError::LookupAccountCreatedApplications(err) + IndexerApiError::LookupAccountCreatedApplications { error: err } } } impl From for IndexerApiError { fn from(err: lookup_account_transactions::LookupAccountTransactionsError) -> Self { - IndexerApiError::LookupAccountTransactions(err) + IndexerApiError::LookupAccountTransactions { error: err } } } impl From for IndexerApiError { fn from(err: search_for_applications::SearchForApplicationsError) -> Self { - IndexerApiError::SearchForApplications(err) + IndexerApiError::SearchForApplications { error: err } } } impl From for IndexerApiError { fn from(err: lookup_application_by_id::LookupApplicationByIdError) -> Self { - IndexerApiError::LookupApplicationById(err) + IndexerApiError::LookupApplicationById { error: err } } } impl From for IndexerApiError { fn from(err: search_for_application_boxes::SearchForApplicationBoxesError) -> Self { - IndexerApiError::SearchForApplicationBoxes(err) + IndexerApiError::SearchForApplicationBoxes { error: err } } } @@ -165,73 +205,75 @@ impl From Self { - IndexerApiError::LookupApplicationBoxByIdAndName(err) + IndexerApiError::LookupApplicationBoxByIdAndName { error: err } } } impl From for IndexerApiError { fn from(err: lookup_application_logs_by_id::LookupApplicationLogsByIdError) -> Self { - IndexerApiError::LookupApplicationLogsById(err) + IndexerApiError::LookupApplicationLogsById { error: err } } } impl From for IndexerApiError { fn from(err: search_for_assets::SearchForAssetsError) -> Self { - IndexerApiError::SearchForAssets(err) + IndexerApiError::SearchForAssets { error: err } } } impl From for IndexerApiError { fn from(err: lookup_asset_by_id::LookupAssetByIdError) -> Self { - IndexerApiError::LookupAssetById(err) + IndexerApiError::LookupAssetById { error: err } } } impl From for IndexerApiError { fn from(err: lookup_asset_balances::LookupAssetBalancesError) -> Self { - IndexerApiError::LookupAssetBalances(err) + IndexerApiError::LookupAssetBalances { error: err } } } impl From for IndexerApiError { fn from(err: lookup_asset_transactions::LookupAssetTransactionsError) -> Self { - IndexerApiError::LookupAssetTransactions(err) + IndexerApiError::LookupAssetTransactions { error: err } } } impl From for IndexerApiError { fn from(err: search_for_block_headers::SearchForBlockHeadersError) -> Self { - IndexerApiError::SearchForBlockHeaders(err) + IndexerApiError::SearchForBlockHeaders { error: err } } } impl From for IndexerApiError { fn from(err: lookup_block::LookupBlockError) -> Self { - IndexerApiError::LookupBlock(err) + IndexerApiError::LookupBlock { error: err } } } impl From for IndexerApiError { fn from(err: lookup_transaction::LookupTransactionError) -> Self { - IndexerApiError::LookupTransaction(err) + IndexerApiError::LookupTransaction { error: err } } } impl From for IndexerApiError { fn from(err: search_for_transactions::SearchForTransactionsError) -> Self { - IndexerApiError::SearchForTransactions(err) + IndexerApiError::SearchForTransactions { error: err } } } /// The main error type for all indexer client operations -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Snafu)] pub enum Error { - #[error("HTTP error: {0}")] - Http(#[from] algokit_http_client::HttpError), - #[error("Serialization error: {0}")] - Serde(String), - #[error("API error: {0}")] - Api(#[from] IndexerApiError), + #[snafu(display("HTTP error: {source}"))] + Http { + source: algokit_http_client::HttpError, + }, + #[snafu(display("Serialization error: {message}"))] + Serde { message: String }, + #[snafu(display("API error: {source}"))] + Api { source: IndexerApiError }, } #[derive(Debug, PartialEq, Eq)] diff --git a/crates/indexer_client/src/apis/search_for_accounts.rs b/crates/indexer_client/src/apis/search_for_accounts.rs index 55ec766c3..62be621e5 100644 --- a/crates/indexer_client/src/apis/search_for_accounts.rs +++ b/crates/indexer_client/src/apis/search_for_accounts.rs @@ -107,7 +107,7 @@ pub async fn search_for_accounts( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -116,16 +116,22 @@ pub async fn search_for_accounts( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/search_for_application_boxes.rs b/crates/indexer_client/src/apis/search_for_application_boxes.rs index dfa89cc7f..d490330b4 100644 --- a/crates/indexer_client/src/apis/search_for_application_boxes.rs +++ b/crates/indexer_client/src/apis/search_for_application_boxes.rs @@ -68,7 +68,7 @@ pub async fn search_for_application_boxes( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -77,16 +77,22 @@ pub async fn search_for_application_boxes( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/search_for_applications.rs b/crates/indexer_client/src/apis/search_for_applications.rs index 86c91b128..5ed2f9778 100644 --- a/crates/indexer_client/src/apis/search_for_applications.rs +++ b/crates/indexer_client/src/apis/search_for_applications.rs @@ -76,7 +76,7 @@ pub async fn search_for_applications( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -85,16 +85,22 @@ pub async fn search_for_applications( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/search_for_assets.rs b/crates/indexer_client/src/apis/search_for_assets.rs index 52944fd7b..0f445f857 100644 --- a/crates/indexer_client/src/apis/search_for_assets.rs +++ b/crates/indexer_client/src/apis/search_for_assets.rs @@ -87,7 +87,7 @@ pub async fn search_for_assets( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -96,16 +96,22 @@ pub async fn search_for_assets( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/search_for_block_headers.rs b/crates/indexer_client/src/apis/search_for_block_headers.rs index 35acd59c9..7be0830ea 100644 --- a/crates/indexer_client/src/apis/search_for_block_headers.rs +++ b/crates/indexer_client/src/apis/search_for_block_headers.rs @@ -97,7 +97,7 @@ pub async fn search_for_block_headers( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -106,16 +106,22 @@ pub async fn search_for_block_headers( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } diff --git a/crates/indexer_client/src/apis/search_for_transactions.rs b/crates/indexer_client/src/apis/search_for_transactions.rs index fee0923da..869f349e8 100644 --- a/crates/indexer_client/src/apis/search_for_transactions.rs +++ b/crates/indexer_client/src/apis/search_for_transactions.rs @@ -153,7 +153,7 @@ pub async fn search_for_transactions( Some(headers), ) .await - .map_err(Error::Http)?; + .map_err(|e| Error::Http { source: e })?; let content_type = response .headers @@ -162,16 +162,22 @@ pub async fn search_for_transactions( .unwrap_or("application/json"); match ContentType::from(content_type) { - ContentType::Json => { - serde_json::from_slice(&response.body).map_err(|e| Error::Serde(e.to_string())) - } - ContentType::MsgPack => Err(Error::Serde("MsgPack not supported".to_string())), + ContentType::Json => serde_json::from_slice(&response.body).map_err(|e| Error::Serde { + message: e.to_string(), + }), + ContentType::MsgPack => Err(Error::Serde { + message: "MsgPack not supported".to_string(), + }), ContentType::Text => { - let text = String::from_utf8(response.body).map_err(|e| Error::Serde(e.to_string()))?; - Err(Error::Serde(format!("Unexpected text response: {}", text))) - } - ContentType::Unsupported(ct) => { - Err(Error::Serde(format!("Unsupported content type: {}", ct))) + let text = String::from_utf8(response.body).map_err(|e| Error::Serde { + message: e.to_string(), + })?; + Err(Error::Serde { + message: format!("Unexpected text response: {}", text), + }) } + ContentType::Unsupported(ct) => Err(Error::Serde { + message: format!("Unsupported content type: {}", ct), + }), } } From 4ae9154c18dc887ee442c4ea5e4a91c489357c4b Mon Sep 17 00:00:00 2001 From: Joe Polny Date: Mon, 18 Aug 2025 11:04:47 -0400 Subject: [PATCH 4/6] chore: cargo fmt --- crates/algokit_abi/src/abi_type.rs | 120 +++++----- crates/algokit_abi/src/arc56_contract.rs | 72 +++--- crates/algokit_abi/src/method.rs | 50 ++-- .../src/types/collections/array_dynamic.rs | 16 +- .../src/types/collections/array_static.rs | 12 +- .../src/types/collections/tuple.rs | 71 ++++-- .../src/types/primitives/address.rs | 42 ++-- .../algokit_abi/src/types/primitives/bool.rs | 20 +- .../algokit_abi/src/types/primitives/byte.rs | 16 +- .../src/types/primitives/string.rs | 35 ++- .../src/types/primitives/ufixed.rs | 34 ++- .../algokit_abi/src/types/primitives/uint.rs | 31 ++- crates/algokit_http_client/src/lib.rs | 36 ++- .../algokit_transact/src/transactions/mod.rs | 30 ++- crates/algokit_transact_ffi/src/lib.rs | 83 ++++--- crates/algokit_transact_ffi/src/multisig.rs | 19 +- .../src/transactions/application_call.rs | 13 +- .../src/transactions/asset_config.rs | 13 +- .../src/transactions/asset_freeze.rs | 4 +- .../src/transactions/keyreg.rs | 13 +- .../algokit_utils/src/clients/app_manager.rs | 71 +++--- .../src/clients/asset_manager.rs | 2 +- .../src/testing/indexer_helpers.rs | 10 +- .../src/transactions/application_call.rs | 63 +++-- .../src/transactions/composer.rs | 224 +++++++++++------- .../algokit_utils/src/transactions/creator.rs | 8 +- .../algokit_utils/src/transactions/sender.rs | 61 +++-- 27 files changed, 719 insertions(+), 450 deletions(-) diff --git a/crates/algokit_abi/src/abi_type.rs b/crates/algokit_abi/src/abi_type.rs index 8de6c2920..424f429bd 100644 --- a/crates/algokit_abi/src/abi_type.rs +++ b/crates/algokit_abi/src/abi_type.rs @@ -25,10 +25,12 @@ impl BitSize { /// A new [`BitSize`] if valid, or an [`ABIError`] if invalid. pub fn new(bits: u16) -> Result { if bits < BITS_PER_BYTE as u16 || bits > MAX_BIT_SIZE || bits % BITS_PER_BYTE as u16 != 0 { - return Err(ABIError::ValidationError { message: format!( - "Bit size must be between {} and {} and divisible by {}, got {}", - BITS_PER_BYTE, MAX_BIT_SIZE, BITS_PER_BYTE, bits - )}); + return Err(ABIError::ValidationError { + message: format!( + "Bit size must be between {} and {} and divisible by {}, got {}", + BITS_PER_BYTE, MAX_BIT_SIZE, BITS_PER_BYTE, bits + ), + }); } Ok(BitSize(bits)) } @@ -56,10 +58,12 @@ impl Precision { /// A new [`Precision`] if valid, or an [`ABIError`] if invalid. pub fn new(precision: u8) -> Result { if precision > MAX_PRECISION { - return Err(ABIError::ValidationError { message: format!( - "Precision must be between 0 and {}, got {}", - MAX_PRECISION, precision - )}); + return Err(ABIError::ValidationError { + message: format!( + "Precision must be between 0 and {}, got {}", + MAX_PRECISION, precision + ), + }); } Ok(Precision(precision)) } @@ -186,14 +190,12 @@ impl ABIType { } Ok(size) } - ABIType::String => Err(ABIError::DecodingError { message: format!( - "Failed to get size, {} is a dynamic type", - abi_type - )}), - ABIType::DynamicArray(_) => Err(ABIError::DecodingError { message: format!( - "Failed to get size, {} is a dynamic type", - abi_type - )}), + ABIType::String => Err(ABIError::DecodingError { + message: format!("Failed to get size, {} is a dynamic type", abi_type), + }), + ABIType::DynamicArray(_) => Err(ABIError::DecodingError { + message: format!("Failed to get size, {} is a dynamic type", abi_type), + }), } } } @@ -240,22 +242,19 @@ impl FromStr for ABIType { let element_type_str = &captures[1]; let length_str = &captures[2]; - let length = length_str - .parse::() - .map_err(|_| { - ABIError::ValidationError { message: format!( - "Invalid array length: {}", - length_str - ) } - })?; + let length = + length_str + .parse::() + .map_err(|_| ABIError::ValidationError { + message: format!("Invalid array length: {}", length_str), + })?; let element_type = ABIType::from_str(element_type_str)?; return Ok(ABIType::StaticArray(Box::new(element_type), length)); } else { - return Err(ABIError::ValidationError { message: format!( - "Malformed static array string: {}", - s - )}); + return Err(ABIError::ValidationError { + message: format!("Malformed static array string: {}", s), + }); } } @@ -264,19 +263,15 @@ impl FromStr for ABIType { if size_str.chars().all(|c| c.is_ascii_digit()) { let size = size_str .parse::() - .map_err(|_| { - ABIError::ValidationError { message: format!( - "Invalid uint size: {}", - size_str - ) } + .map_err(|_| ABIError::ValidationError { + message: format!("Invalid uint size: {}", size_str), })?; let bit_size = BitSize::new(size)?; return Ok(ABIType::Uint(bit_size)); } else { - return Err(ABIError::ValidationError { message: format!( - "Malformed uint string: {}", - size_str - )}); + return Err(ABIError::ValidationError { + message: format!("Malformed uint string: {}", size_str), + }); } } @@ -289,29 +284,23 @@ impl FromStr for ABIType { let size = size_str .parse::() - .map_err(|_| { - ABIError::ValidationError { message: format!( - "Invalid ufixed size: {}", - size_str - ) } - })?; - let precision = precision_str - .parse::() - .map_err(|_| { - ABIError::ValidationError { message: format!( - "Invalid ufixed precision: {}", - precision_str - ) } + .map_err(|_| ABIError::ValidationError { + message: format!("Invalid ufixed size: {}", size_str), })?; + let precision = + precision_str + .parse::() + .map_err(|_| ABIError::ValidationError { + message: format!("Invalid ufixed precision: {}", precision_str), + })?; let bit_size = BitSize::new(size)?; let precision = Precision::new(precision)?; return Ok(ABIType::UFixed(bit_size, precision)); } else { - return Err(ABIError::ValidationError { message: format!( - "Malformed ufixed type: {}", - s - )}); + return Err(ABIError::ValidationError { + message: format!("Malformed ufixed type: {}", s), + }); } } @@ -332,10 +321,9 @@ impl FromStr for ABIType { "bool" => Ok(ABIType::Bool), "address" => Ok(ABIType::Address), "string" => Ok(ABIType::String), - _ => Err(ABIError::ValidationError { message: format!( - "Cannot convert string '{}' to an ABI type", - s - )}), + _ => Err(ABIError::ValidationError { + message: format!("Cannot convert string '{}' to an ABI type", s), + }), } } } @@ -346,13 +334,19 @@ pub(crate) fn parse_tuple_content(content: &str) -> Result, ABIError } if content.starts_with(",") { - return Err(ABIError::ValidationError { message: "Tuple name should not start with comma".to_string() }); + return Err(ABIError::ValidationError { + message: "Tuple name should not start with comma".to_string(), + }); } if content.ends_with(",") { - return Err(ABIError::ValidationError { message: "Tuple name should not start with comma".to_string() }); + return Err(ABIError::ValidationError { + message: "Tuple name should not start with comma".to_string(), + }); } if content.contains(",,") { - return Err(ABIError::ValidationError { message: "tuple string should not have consecutive commas".to_string() }); + return Err(ABIError::ValidationError { + message: "tuple string should not have consecutive commas".to_string(), + }); } let mut tuple_strings: Vec = Vec::new(); @@ -376,7 +370,9 @@ pub(crate) fn parse_tuple_content(content: &str) -> Result, ABIError tuple_strings.push(word); } if depth != 0 { - return Err(ABIError::ValidationError { message: "Tuple string has mismatched parentheses".to_string() }); + return Err(ABIError::ValidationError { + message: "Tuple string has mismatched parentheses".to_string(), + }); } Ok(tuple_strings) diff --git a/crates/algokit_abi/src/arc56_contract.rs b/crates/algokit_abi/src/arc56_contract.rs index f20b80dcb..7f5d580f9 100644 --- a/crates/algokit_abi/src/arc56_contract.rs +++ b/crates/algokit_abi/src/arc56_contract.rs @@ -144,9 +144,12 @@ impl Source { } fn decode_source(&self, b64_text: &str) -> Result { - let decoded = general_purpose::STANDARD - .decode(b64_text) - .map_err(|e| ABIError::ValidationError { message: format!("Failed to decode base64: {}", e) })?; + let decoded = + general_purpose::STANDARD + .decode(b64_text) + .map_err(|e| ABIError::ValidationError { + message: format!("Failed to decode base64: {}", e), + })?; Ok(String::from_utf8_lossy(&decoded).to_string()) } } @@ -514,8 +517,9 @@ pub struct Arc56Contract { impl Arc56Contract { /// Create Arc56Contract from JSON string pub fn from_json(json_str: &str) -> Result { - serde_json::from_str(json_str) - .map_err(|e| ABIError::ValidationError { message: format!("Failed to parse ARC-56 JSON: {}", e) }) + serde_json::from_str(json_str).map_err(|e| ABIError::ValidationError { + message: format!("Failed to parse ARC-56 JSON: {}", e), + }) } /// Convert Arc56Contract to JSON string with optional indentation @@ -526,17 +530,14 @@ impl Arc56Contract { match indent { None => { // Compact JSON - serde_json::to_string(self).map_err(|e| { - ABIError::EncodingError { message: format!("Failed to serialize ARC-56 to JSON: {}", e) } + serde_json::to_string(self).map_err(|e| ABIError::EncodingError { + message: format!("Failed to serialize ARC-56 to JSON: {}", e), }) } Some(0) => { // Pretty JSON with default formatting - serde_json::to_string_pretty(self).map_err(|e| { - ABIError::EncodingError { message: format!( - "Failed to serialize ARC-56 to pretty JSON: {}", - e - ) } + serde_json::to_string_pretty(self).map_err(|e| ABIError::EncodingError { + message: format!("Failed to serialize ARC-56 to pretty JSON: {}", e), }) } Some(indent_size) => { @@ -545,17 +546,12 @@ impl Arc56Contract { let formatter = serde_json::ser::PrettyFormatter::with_indent(&indent_bytes); let mut buf = Vec::new(); let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter); - self.serialize(&mut ser).map_err(|e| { - ABIError::EncodingError { message: format!( - "Failed to serialize ARC-56 with indent: {}", - e - ) } - })?; - String::from_utf8(buf).map_err(|e| { - ABIError::EncodingError { message: format!( - "Failed to convert serialized JSON to string: {}", - e - ) } + self.serialize(&mut ser) + .map_err(|e| ABIError::EncodingError { + message: format!("Failed to serialize ARC-56 with indent: {}", e), + })?; + String::from_utf8(buf).map_err(|e| ABIError::EncodingError { + message: format!("Failed to convert serialized JSON to string: {}", e), }) } } @@ -572,23 +568,27 @@ impl Arc56Contract { .collect(); if methods.is_empty() { - return Err(ABIError::ValidationError { message: format!( - "Unable to find method {} in {} app", - method_name_or_signature, self.name - )}); + return Err(ABIError::ValidationError { + message: format!( + "Unable to find method {} in {} app", + method_name_or_signature, self.name + ), + }); } if methods.len() > 1 { let signatures: Result, ABIError> = methods.iter().map(|m| m.get_signature()).collect(); let signatures = signatures?; - return Err(ABIError::ValidationError { message: format!( - "Received a call to method {} in contract {}, but this resolved to multiple methods; \ + return Err(ABIError::ValidationError { + message: format!( + "Received a call to method {} in contract {}, but this resolved to multiple methods; \ please pass in an ABI signature instead: {}", - method_name_or_signature, - self.name, - signatures.join(", ") - )}); + method_name_or_signature, + self.name, + signatures.join(", ") + ), + }); } Ok(methods[0]) @@ -600,11 +600,11 @@ impl Arc56Contract { m.get_signature() .is_ok_and(|sig| sig == method_name_or_signature) }) - .ok_or_else(|| { - ABIError::ValidationError { message: format!( + .ok_or_else(|| ABIError::ValidationError { + message: format!( "Unable to find method {} in {} app", method_name_or_signature, self.name - ) } + ), }) } } diff --git a/crates/algokit_abi/src/method.rs b/crates/algokit_abi/src/method.rs index d2d939f36..210c10054 100644 --- a/crates/algokit_abi/src/method.rs +++ b/crates/algokit_abi/src/method.rs @@ -39,10 +39,9 @@ impl FromStr for ABITransactionType { "axfer" => Ok(ABITransactionType::AssetTransfer), "afrz" => Ok(ABITransactionType::AssetFreeze), "appl" => Ok(ABITransactionType::ApplicationCall), - _ => Err(ABIError::ValidationError { message: format!( - "Invalid transaction type: {}", - s - )}), + _ => Err(ABIError::ValidationError { + message: format!("Invalid transaction type: {}", s), + }), } } } @@ -92,10 +91,9 @@ impl FromStr for ABIReferenceType { "account" => Ok(ABIReferenceType::Account), "application" => Ok(ABIReferenceType::Application), "asset" => Ok(ABIReferenceType::Asset), - _ => Err(ABIError::ValidationError { message: format!( - "Invalid reference type: {}", - s - )}), + _ => Err(ABIError::ValidationError { + message: format!("Invalid reference type: {}", s), + }), } } } @@ -227,7 +225,9 @@ impl ABIMethod { pub fn selector(&self) -> Result, ABIError> { let signature = self.signature()?; if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError { message: "Method signature cannot contain whitespace".to_string() }); + return Err(ABIError::ValidationError { + message: "Method signature cannot contain whitespace".to_string(), + }); } let mut hasher = Sha512_256::new(); @@ -240,7 +240,9 @@ impl ABIMethod { /// Returns the method signature as a string. pub fn signature(&self) -> Result { if self.name.is_empty() { - return Err(ABIError::ValidationError { message: "Method name cannot be empty".to_string() }); + return Err(ABIError::ValidationError { + message: "Method name cannot be empty".to_string(), + }); } let arg_types: Vec = self @@ -268,7 +270,9 @@ impl ABIMethod { let signature = format!("{}({}){}", self.name, args_str, return_type); if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError { message: "Generated signature contains whitespace".to_string() }); + return Err(ABIError::ValidationError { + message: "Generated signature contains whitespace".to_string(), + }); } Ok(signature) @@ -280,15 +284,21 @@ impl FromStr for ABIMethod { fn from_str(signature: &str) -> Result { if signature.chars().any(|c| c.is_whitespace()) { - return Err(ABIError::ValidationError { message: "Method signature cannot contain whitespace".to_string() }); + return Err(ABIError::ValidationError { + message: "Method signature cannot contain whitespace".to_string(), + }); } - let open_paren_pos = signature.find('(').ok_or_else(|| { - ABIError::ValidationError { message: "Method signature must contain opening parenthesis".to_string() } - })?; + let open_paren_pos = signature + .find('(') + .ok_or_else(|| ABIError::ValidationError { + message: "Method signature must contain opening parenthesis".to_string(), + })?; if open_paren_pos == 0 { - return Err(ABIError::ValidationError { message: "Method name cannot be empty".to_string() }); + return Err(ABIError::ValidationError { + message: "Method name cannot be empty".to_string(), + }); } let method_name = signature[..open_paren_pos].to_string(); @@ -375,7 +385,9 @@ fn find_matching_closing_paren(s: &str, open_pos: usize) -> Result Result, ABIError> { // Additional validation for method arguments: no empty arguments for arg in &arguments { if arg.trim().is_empty() { - return Err(ABIError::ValidationError { message: "Empty argument in method signature".to_string() }); + return Err(ABIError::ValidationError { + message: "Empty argument in method signature".to_string(), + }); } } diff --git a/crates/algokit_abi/src/types/collections/array_dynamic.rs b/crates/algokit_abi/src/types/collections/array_dynamic.rs index 34000b185..2db15136f 100644 --- a/crates/algokit_abi/src/types/collections/array_dynamic.rs +++ b/crates/algokit_abi/src/types/collections/array_dynamic.rs @@ -9,14 +9,18 @@ impl ABIType { let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected an array of values".to_string(), + }); } }; let child_type = match self { ABIType::DynamicArray(child_type) => child_type, _ => { - return Err(ABIError::EncodingError { message: "ABI type mismatch, expected dynamic array".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI type mismatch, expected dynamic array".to_string(), + }); } }; @@ -32,7 +36,9 @@ impl ABIType { pub(crate) fn decode_dynamic_array(&self, value: &[u8]) -> Result { if value.len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError { message: "Byte array is too short to be decoded as dynamic array".to_string() }); + return Err(ABIError::DecodingError { + message: "Byte array is too short to be decoded as dynamic array".to_string(), + }); } // The first 2 bytes in the value determines how many values in the array @@ -42,7 +48,9 @@ impl ABIType { let child_type = match self { ABIType::DynamicArray(child_type) => child_type, _ => { - return Err(ABIError::EncodingError { message: "ABI type mismatch, expected dynamic array".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI type mismatch, expected dynamic array".to_string(), + }); } }; diff --git a/crates/algokit_abi/src/types/collections/array_static.rs b/crates/algokit_abi/src/types/collections/array_static.rs index 3041fa211..b6b6b7d16 100644 --- a/crates/algokit_abi/src/types/collections/array_static.rs +++ b/crates/algokit_abi/src/types/collections/array_static.rs @@ -8,14 +8,18 @@ impl ABIType { let child_types = match self { ABIType::StaticArray(child_type, size) => vec![child_type.as_ref(); *size], _ => { - return Err(ABIError::EncodingError { message: "ABI type mismatch, expected static array".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI type mismatch, expected static array".to_string(), + }); } }; let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected an array of values".to_string(), + }); } }; @@ -26,7 +30,9 @@ impl ABIType { let child_types = match self { ABIType::StaticArray(child_type, size) => vec![child_type.as_ref(); *size], _ => { - return Err(ABIError::EncodingError { message: "ABI type mismatch, expected static array".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI type mismatch, expected static array".to_string(), + }); } }; diff --git a/crates/algokit_abi/src/types/collections/tuple.rs b/crates/algokit_abi/src/types/collections/tuple.rs index 4c4da6c58..33f6ebb88 100644 --- a/crates/algokit_abi/src/types/collections/tuple.rs +++ b/crates/algokit_abi/src/types/collections/tuple.rs @@ -15,14 +15,18 @@ impl ABIType { let child_types = match self { ABIType::Tuple(child_types) => child_types.iter().collect::>(), _ => { - return Err(ABIError::EncodingError { message: "ABI type mismatch, expected tuple".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI type mismatch, expected tuple".to_string(), + }); } }; let values = match value { ABIValue::Array(n) => n, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected an array of values".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected an array of values".to_string(), + }); } }; @@ -33,7 +37,9 @@ impl ABIType { let child_types = match self { ABIType::Tuple(child_types) => child_types.iter().collect::>(), _ => { - return Err(ABIError::DecodingError { message: "ABI type mismatch, expected tuple".to_string() }); + return Err(ABIError::DecodingError { + message: "ABI type mismatch, expected tuple".to_string(), + }); } }; @@ -43,7 +49,9 @@ impl ABIType { pub fn encode_abi_types(abi_types: &[&ABIType], values: &[ABIValue]) -> Result, ABIError> { if abi_types.len() != values.len() { - return Err(ABIError::EncodingError { message: "Mismatch lengths between the values and types".to_string() }); + return Err(ABIError::EncodingError { + message: "Mismatch lengths between the values and types".to_string(), + }); } let mut heads: Vec> = Vec::new(); @@ -86,9 +94,10 @@ pub fn encode_abi_types(abi_types: &[&ABIType], values: &[ABIValue]) -> Result Result Result { if values.len() > 8 { - return Err(ABIError::EncodingError { message: format!( - "Expected no more than 8 bool values, received {}", - values.len() - ) }); + return Err(ABIError::EncodingError { + message: format!( + "Expected no more than 8 bool values, received {}", + values.len() + ), + }); } let mut result: u8 = 0; @@ -130,7 +141,9 @@ fn compress_bools(values: &[ABIValue]) -> Result { } } _ => { - return Err(ABIError::EncodingError { message: "Expected all values to be ABIValue::Bool".to_string() }); + return Err(ABIError::EncodingError { + message: "Expected all values to be ABIValue::Bool".to_string(), + }); } } } @@ -148,13 +161,19 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, if child_type.is_dynamic() { if bytes[bytes_cursor..].len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError { message: "Byte array is too short to be decoded".to_string() }); + return Err(ABIError::DecodingError { + message: "Byte array is too short to be decoded".to_string(), + }); } let dynamic_index = u16::from_be_bytes([bytes[bytes_cursor], bytes[bytes_cursor + 1]]); if let Some(last_segment) = dynamic_segments.last_mut() { if dynamic_index < last_segment.left { - return Err(ABIError::DecodingError { message: "Dynamic index segment miscalculation: left is greater than right index".to_string() }); + return Err(ABIError::DecodingError { + message: + "Dynamic index segment miscalculation: left is greater than right index" + .to_string(), + }); } last_segment.right = dynamic_index; } @@ -202,27 +221,35 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, } } if abi_types_cursor != abi_types.len() - 1 && bytes_cursor >= bytes.len() { - return Err(ABIError::DecodingError { message: "Input bytes not enough to decode".to_string() }); + return Err(ABIError::DecodingError { + message: "Input bytes not enough to decode".to_string(), + }); } abi_types_cursor += 1; } if let Some(last_segment) = dynamic_segments.last_mut() { let bytes_length = bytes.len(); - last_segment.right = u16::try_from(bytes_length).map_err(|_| { - ABIError::EncodingError { message: format!("Value {} cannot fit in u16", bytes_length) } + last_segment.right = u16::try_from(bytes_length).map_err(|_| ABIError::EncodingError { + message: format!("Value {} cannot fit in u16", bytes_length), })?; } else if bytes_cursor < bytes.len() { - return Err(ABIError::DecodingError { message: "Input bytes not fully consumed".to_string() }); + return Err(ABIError::DecodingError { + message: "Input bytes not fully consumed".to_string(), + }); } for i in 0..dynamic_segments.len() { let segment = &dynamic_segments[i]; if segment.left > segment.right { - return Err(ABIError::DecodingError { message: "Dynamic segment should display a [l, r] space with l <= r".to_string() }); + return Err(ABIError::DecodingError { + message: "Dynamic segment should display a [l, r] space with l <= r".to_string(), + }); } if i != dynamic_segments.len() - 1 && segment.right != dynamic_segments[i + 1].left { - return Err(ABIError::DecodingError { message: "Dynamic segments should be consecutive".to_string() }); + return Err(ABIError::DecodingError { + message: "Dynamic segments should be consecutive".to_string(), + }); } } @@ -244,8 +271,8 @@ fn extract_values(abi_types: &[&ABIType], bytes: &[u8]) -> Result>, .into_iter() .enumerate() .map(|(i, partition)| { - partition.ok_or_else(|| { - ABIError::DecodingError { message: format!("Value partition at index {} is None", i) } + partition.ok_or_else(|| ABIError::DecodingError { + message: format!("Value partition at index {} is None", i), }) }) .collect::>, ABIError>>()?; diff --git a/crates/algokit_abi/src/types/primitives/address.rs b/crates/algokit_abi/src/types/primitives/address.rs index 8de6813c2..b959f884f 100644 --- a/crates/algokit_abi/src/types/primitives/address.rs +++ b/crates/algokit_abi/src/types/primitives/address.rs @@ -15,26 +15,32 @@ impl ABIType { let address_str = match value { ABIValue::Address(a) => a, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected address string".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected address string".to_string(), + }); } }; if address_str.len() != ALGORAND_ADDRESS_LENGTH { - return Err(ABIError::ValidationError { message: format!( - "Algorand address must be exactly {} characters", - ALGORAND_ADDRESS_LENGTH - ) }); + return Err(ABIError::ValidationError { + message: format!( + "Algorand address must be exactly {} characters", + ALGORAND_ADDRESS_LENGTH + ), + }); } let decoded_address = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, address_str) - .ok_or_else(|| { - ABIError::ValidationError { message: "Invalid base32 encoding for Algorand address".to_string() } + .ok_or_else(|| ABIError::ValidationError { + message: "Invalid base32 encoding for Algorand address".to_string(), })?[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH] .to_vec(); Ok(decoded_address) } - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected address".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected address".to_string(), + }), } } @@ -42,17 +48,19 @@ impl ABIType { match self { ABIType::Address => { if bytes.len() != ALGORAND_PUBLIC_KEY_BYTE_LENGTH { - return Err(ABIError::DecodingError { message: format!( - "Address byte string must be {} bytes long", - ALGORAND_PUBLIC_KEY_BYTE_LENGTH - ) }); + return Err(ABIError::DecodingError { + message: format!( + "Address byte string must be {} bytes long", + ALGORAND_PUBLIC_KEY_BYTE_LENGTH + ), + }); } let bytes: &[u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = - bytes.try_into().map_err(|_| { - ABIError::DecodingError { message: format!( + bytes.try_into().map_err(|_| ABIError::DecodingError { + message: format!( "Failed to convert bytes to [u8; {}] for checksum", ALGORAND_PUBLIC_KEY_BYTE_LENGTH - ) } + ), })?; let mut buffer = @@ -67,7 +75,9 @@ impl ABIType { Ok(ABIValue::Address(address_str)) } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected address".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected address".to_string(), + }), } } } diff --git a/crates/algokit_abi/src/types/primitives/bool.rs b/crates/algokit_abi/src/types/primitives/bool.rs index 74a10d6ab..c4f095317 100644 --- a/crates/algokit_abi/src/types/primitives/bool.rs +++ b/crates/algokit_abi/src/types/primitives/bool.rs @@ -10,7 +10,9 @@ impl ABIType { let bool_value = match value { ABIValue::Bool(b) => b, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected boolean".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected boolean".to_string(), + }); } }; @@ -19,7 +21,9 @@ impl ABIType { false => Ok(vec![BOOL_FALSE_BYTE]), // false -> 0 } } - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected bool".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected bool".to_string(), + }), } } @@ -27,16 +31,22 @@ impl ABIType { match self { ABIType::Bool => { if bytes.len() != 1 { - return Err(ABIError::DecodingError { message: "Bool string must be 1 byte long".to_string() }); + return Err(ABIError::DecodingError { + message: "Bool string must be 1 byte long".to_string(), + }); } match bytes[0] { BOOL_TRUE_BYTE => Ok(ABIValue::Bool(true)), BOOL_FALSE_BYTE => Ok(ABIValue::Bool(false)), - _ => Err(ABIError::DecodingError { message: "Boolean could not be decoded from the byte string".to_string() }), + _ => Err(ABIError::DecodingError { + message: "Boolean could not be decoded from the byte string".to_string(), + }), } } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected bool".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected bool".to_string(), + }), } } } diff --git a/crates/algokit_abi/src/types/primitives/byte.rs b/crates/algokit_abi/src/types/primitives/byte.rs index 9c57f4484..d70790cc9 100644 --- a/crates/algokit_abi/src/types/primitives/byte.rs +++ b/crates/algokit_abi/src/types/primitives/byte.rs @@ -5,9 +5,13 @@ impl ABIType { match self { ABIType::Byte => match value { ABIValue::Byte(n) => Ok(vec![*n]), - _ => Err(ABIError::EncodingError { message: "ABI value mismatch, expected byte".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI value mismatch, expected byte".to_string(), + }), }, - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected byte".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected byte".to_string(), + }), } } @@ -15,12 +19,16 @@ impl ABIType { match self { ABIType::Byte => { if bytes.len() != 1 { - return Err(ABIError::DecodingError { message: "Byte array must be 1 byte long".to_string() }); + return Err(ABIError::DecodingError { + message: "Byte array must be 1 byte long".to_string(), + }); } Ok(ABIValue::Byte(bytes[0])) } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected byte".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected byte".to_string(), + }), } } } diff --git a/crates/algokit_abi/src/types/primitives/string.rs b/crates/algokit_abi/src/types/primitives/string.rs index 9b5732972..85dd5b999 100644 --- a/crates/algokit_abi/src/types/primitives/string.rs +++ b/crates/algokit_abi/src/types/primitives/string.rs @@ -7,7 +7,9 @@ impl ABIType { let value = match value { ABIValue::String(s) => s, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected string".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected string".to_string(), + }); } }; @@ -19,7 +21,9 @@ impl ABIType { Ok(result) } - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected string".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected string".to_string(), + }), } } @@ -27,24 +31,33 @@ impl ABIType { match self { ABIType::String => { if value.len() < LENGTH_ENCODE_BYTE_SIZE { - return Err(ABIError::DecodingError { message: "Byte array is too short for string".to_string() }); + return Err(ABIError::DecodingError { + message: "Byte array is too short for string".to_string(), + }); } let length = u16::from_be_bytes([value[0], value[1]]) as usize; let content_bytes = &value[LENGTH_ENCODE_BYTE_SIZE..]; if content_bytes.len() != length { - return Err(ABIError::DecodingError { message: format!( - "Invalid byte array length for string, expected {} value, got {}", - length, - content_bytes.len() - ) }); + return Err(ABIError::DecodingError { + message: format!( + "Invalid byte array length for string, expected {} value, got {}", + length, + content_bytes.len() + ), + }); } - let string_value = String::from_utf8(content_bytes.to_vec()) - .map_err(|_| ABIError::DecodingError { message: "Invalid UTF-8 encoding".to_string() })?; + let string_value = String::from_utf8(content_bytes.to_vec()).map_err(|_| { + ABIError::DecodingError { + message: "Invalid UTF-8 encoding".to_string(), + } + })?; Ok(ABIValue::String(string_value)) } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected string".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected string".to_string(), + }), } } } diff --git a/crates/algokit_abi/src/types/primitives/ufixed.rs b/crates/algokit_abi/src/types/primitives/ufixed.rs index 43d086f48..17880ea0d 100644 --- a/crates/algokit_abi/src/types/primitives/ufixed.rs +++ b/crates/algokit_abi/src/types/primitives/ufixed.rs @@ -12,20 +12,26 @@ impl ABIType { let value = match value { ABIValue::Uint(n) => n, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected uint".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected uint".to_string(), + }); } }; if value >= &BigUint::from(2u64).pow(bit_size as u32) { - return Err(ABIError::EncodingError { message: format!( - "{} is too big to fit in ufixed{}x{}", - value, bit_size, precision - ) }); + return Err(ABIError::EncodingError { + message: format!( + "{} is too big to fit in ufixed{}x{}", + value, bit_size, precision + ), + }); } Ok(utils::big_uint_to_bytes(value, (bit_size / 8) as usize)) } - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected ufixed".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected ufixed".to_string(), + }), } } @@ -35,16 +41,20 @@ impl ABIType { let bit_size = bit_size.value(); let expected_len = (bit_size / 8) as usize; if bytes.len() != expected_len { - return Err(ABIError::DecodingError { message: format!( - "Invalid byte array length, expected {} bytes, got {}", - expected_len, - bytes.len() - ) }); + return Err(ABIError::DecodingError { + message: format!( + "Invalid byte array length, expected {} bytes, got {}", + expected_len, + bytes.len() + ), + }); } Ok(ABIValue::Uint(BigUint::from_bytes_be(bytes))) } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected ufixed".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected ufixed".to_string(), + }), } } } diff --git a/crates/algokit_abi/src/types/primitives/uint.rs b/crates/algokit_abi/src/types/primitives/uint.rs index ae0a4a9c1..ad33e93cf 100644 --- a/crates/algokit_abi/src/types/primitives/uint.rs +++ b/crates/algokit_abi/src/types/primitives/uint.rs @@ -10,20 +10,23 @@ impl ABIType { let value = match value { ABIValue::Uint(n) => n, _ => { - return Err(ABIError::EncodingError { message: "ABI value mismatch, expected uint".to_string() }); + return Err(ABIError::EncodingError { + message: "ABI value mismatch, expected uint".to_string(), + }); } }; if value >= &BigUint::from(2u64).pow(bit_size as u32) { - return Err(ABIError::EncodingError { message: format!( - "{} is too big to fit in uint{}", - value, bit_size - ) }); + return Err(ABIError::EncodingError { + message: format!("{} is too big to fit in uint{}", value, bit_size), + }); } Ok(utils::big_uint_to_bytes(value, (bit_size / 8) as usize)) } - _ => Err(ABIError::EncodingError { message: "ABI type mismatch, expected uint".to_string() }), + _ => Err(ABIError::EncodingError { + message: "ABI type mismatch, expected uint".to_string(), + }), } } @@ -33,16 +36,20 @@ impl ABIType { let bit_size = bit_size.value(); let expected_len = (bit_size / 8) as usize; if bytes.len() != expected_len { - return Err(ABIError::DecodingError { message: format!( - "Invalid byte array length, expected {} bytes, got {}", - expected_len, - bytes.len() - ) }); + return Err(ABIError::DecodingError { + message: format!( + "Invalid byte array length, expected {} bytes, got {}", + expected_len, + bytes.len() + ), + }); } Ok(ABIValue::Uint(BigUint::from_bytes_be(bytes))) } - _ => Err(ABIError::DecodingError { message: "ABI type mismatch, expected uint".to_string() }), + _ => Err(ABIError::DecodingError { + message: "ABI type mismatch, expected uint".to_string(), + }), } } } diff --git a/crates/algokit_http_client/src/lib.rs b/crates/algokit_http_client/src/lib.rs index 534e3f843..14d4ae0da 100644 --- a/crates/algokit_http_client/src/lib.rs +++ b/crates/algokit_http_client/src/lib.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; -use std::collections::HashMap; use snafu::Snafu; +use std::collections::HashMap; #[cfg(feature = "ffi_uniffi")] uniffi::setup_scaffolding!(); @@ -93,16 +93,22 @@ impl DefaultHttpClient { let mut headers = reqwest::header::HeaderMap::new(); headers.insert( reqwest::header::HeaderName::from_bytes(header_name.as_bytes()).map_err(|e| { - HttpError::RequestError { message: format!("Invalid header name '{}': {}", header_name, e) } + HttpError::RequestError { + message: format!("Invalid header name '{}': {}", header_name, e), + } })?, reqwest::header::HeaderValue::from_str(header_value).map_err(|e| { - HttpError::RequestError { message: format!("Invalid header value '{}': {}", header_value, e) } + HttpError::RequestError { + message: format!("Invalid header value '{}': {}", header_value, e), + } })?, ); let client = reqwest::Client::builder() .default_headers(headers) .build() - .map_err(|e| HttpError::RequestError { message: format!("Failed to build HTTP client: {}", e) })?; + .map_err(|e| HttpError::RequestError { + message: format!("Failed to build HTTP client: {}", e), + })?; Ok(DefaultHttpClient { client, base_url: base_url.to_string(), @@ -123,8 +129,11 @@ impl HttpClient for DefaultHttpClient { headers: Option>, ) -> Result { let url = format!("{}{}", self.base_url, path); - let method = reqwest::Method::from_bytes(method.as_str().as_bytes()) - .map_err(|e| HttpError::RequestError { message: e.to_string() })?; + let method = reqwest::Method::from_bytes(method.as_str().as_bytes()).map_err(|e| { + HttpError::RequestError { + message: e.to_string(), + } + })?; let mut request_builder = self.client.request(method, &url); @@ -145,7 +154,9 @@ impl HttpClient for DefaultHttpClient { let response = request_builder .send() .await - .map_err(|e| HttpError::RequestError { message: e.to_string() })?; + .map_err(|e| HttpError::RequestError { + message: e.to_string(), + })?; if !response.status().is_success() { let status = response.status(); @@ -153,10 +164,9 @@ impl HttpClient for DefaultHttpClient { .text() .await .unwrap_or_else(|_| "Failed to read error response text".to_string()); - return Err(HttpError::RequestError { message: format!( - "Request failed with status {}: {}", - status, text - )}); + return Err(HttpError::RequestError { + message: format!("Request failed with status {}: {}", status, text), + }); } let response_headers = response @@ -168,7 +178,9 @@ impl HttpClient for DefaultHttpClient { let body = response .bytes() .await - .map_err(|e| HttpError::RequestError { message: e.to_string() })? + .map_err(|e| HttpError::RequestError { + message: e.to_string(), + })? .to_vec(); Ok(HttpResponse { diff --git a/crates/algokit_transact/src/transactions/mod.rs b/crates/algokit_transact/src/transactions/mod.rs index 293ae31be..7d6cde418 100644 --- a/crates/algokit_transact/src/transactions/mod.rs +++ b/crates/algokit_transact/src/transactions/mod.rs @@ -115,10 +115,12 @@ impl Transaction { if let Some(max_fee) = request.max_fee { if calculated_fee > max_fee { - return Err(AlgoKitTransactError::InputError { message: format!( - "Transaction fee {} µALGO is greater than max fee {} µALGO", - calculated_fee, max_fee - )}); + return Err(AlgoKitTransactError::InputError { + message: format!( + "Transaction fee {} µALGO is greater than max fee {} µALGO", + calculated_fee, max_fee + ), + }); } } @@ -205,10 +207,12 @@ impl AlgorandMsgpack for SignedTransaction { Ok(stxn) } - _ => Err(AlgoKitTransactError::InputError { message: format!( - "expected signed transaction to be a map, but got a: {:#?}", - value.type_id() - )}), + _ => Err(AlgoKitTransactError::InputError { + message: format!( + "expected signed transaction to be a map, but got a: {:#?}", + value.type_id() + ), + }), } } } @@ -235,10 +239,12 @@ impl Transactions for &[Transaction] { /// A result containing the transactions with group assign or an error if grouping fails. fn assign_group(self) -> Result, AlgoKitTransactError> { if self.len() > MAX_TX_GROUP_SIZE { - return Err(AlgoKitTransactError::InputError { message: format!( - "Transaction group size exceeds the max limit of {}", - MAX_TX_GROUP_SIZE - )}); + return Err(AlgoKitTransactError::InputError { + message: format!( + "Transaction group size exceeds the max limit of {}", + MAX_TX_GROUP_SIZE + ), + }); } if self.is_empty() { diff --git a/crates/algokit_transact_ffi/src/lib.rs b/crates/algokit_transact_ffi/src/lib.rs index 5860fd727..d3db944e1 100644 --- a/crates/algokit_transact_ffi/src/lib.rs +++ b/crates/algokit_transact_ffi/src/lib.rs @@ -47,28 +47,42 @@ impl From for AlgoKitTransactError { fn from(e: algokit_transact::AlgoKitTransactError) -> Self { match e { algokit_transact::AlgoKitTransactError::DecodingError { .. } => { - AlgoKitTransactError::DecodingError { message: e.to_string() } + AlgoKitTransactError::DecodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::EncodingError { .. } => { - AlgoKitTransactError::EncodingError { message: e.to_string() } + AlgoKitTransactError::EncodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::MsgpackDecodingError { .. } => { - AlgoKitTransactError::DecodingError { message: e.to_string() } + AlgoKitTransactError::DecodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::MsgpackEncodingError { .. } => { - AlgoKitTransactError::EncodingError { message: e.to_string() } + AlgoKitTransactError::EncodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::UnknownTransactionType { .. } => { - AlgoKitTransactError::DecodingError { message: e.to_string() } + AlgoKitTransactError::DecodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::InputError { message } => { AlgoKitTransactError::InputError { message } } algokit_transact::AlgoKitTransactError::InvalidAddress { .. } => { - AlgoKitTransactError::DecodingError { message: e.to_string() } + AlgoKitTransactError::DecodingError { + message: e.to_string(), + } } algokit_transact::AlgoKitTransactError::InvalidMultisigSignature { .. } => { - AlgoKitTransactError::DecodingError { message: e.to_string() } + AlgoKitTransactError::DecodingError { + message: e.to_string(), + } } } } @@ -138,10 +152,9 @@ impl TryFrom for algokit_transact::KeyPairAccount { fn try_from(value: KeyPairAccount) -> Result { let pub_key: [u8; ALGORAND_PUBLIC_KEY_BYTE_LENGTH] = bytebuf_to_bytes(&value.pub_key) - .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( - "Error while decoding a public key: {}", - e - )})?; + .map_err(|e| AlgoKitTransactError::DecodingError { + message: format!("Error while decoding a public key: {}", e), + })?; Ok(algokit_transact::KeyPairAccount::from_pubkey(&pub_key)) } @@ -254,7 +267,9 @@ impl TryFrom for algokit_transact::Transaction { .count() > 1 { - return Err(Self::Error::DecodingError { message: "Multiple transaction type specific fields set".to_string() }); + return Err(Self::Error::DecodingError { + message: "Multiple transaction type specific fields set".to_string(), + }); } match tx.transaction_type { @@ -323,7 +338,9 @@ impl TryFrom for algokit_transact::PaymentTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::Payment || tx.payment.is_none() { - return Err(Self::Error::DecodingError { message: "Payment data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Payment data missing".to_string(), + }); } let data = tx.clone().payment.unwrap(); @@ -358,7 +375,9 @@ impl TryFrom for algokit_transact::AssetTransferTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetTransfer || tx.asset_transfer.is_none() { - return Err(Self::Error::DecodingError { message: "Asset Transfer data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Asset Transfer data missing".to_string(), + }); } let data = tx.clone().asset_transfer.unwrap(); @@ -501,10 +520,12 @@ impl TryFrom for algokit_transact::SignedTransaction { .signature .map(|sig| bytebuf_to_bytes(&sig)) .transpose() - .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( - "Error while decoding the signature in a signed transaction: {}", - e - )})?, + .map_err(|e| AlgoKitTransactError::DecodingError { + message: format!( + "Error while decoding the signature in a signed transaction: {}", + e + ), + })?, auth_address: signed_tx .auth_address .map(|addr| addr.parse()) @@ -518,10 +539,11 @@ impl TryFrom for algokit_transact::SignedTransaction { } fn bytebuf_to_bytes(buf: &ByteBuf) -> Result<[u8; N], AlgoKitTransactError> { - buf.to_vec().try_into().map_err(|_| AlgoKitTransactError::DecodingError { message: format!( - "Expected {} bytes but got a different length", - N - )}) + buf.to_vec() + .try_into() + .map_err(|_| AlgoKitTransactError::DecodingError { + message: format!("Expected {} bytes but got a different length", N), + }) } fn byte32_to_bytebuf(b32: Byte32) -> ByteBuf { @@ -677,7 +699,12 @@ pub fn decode_transactions( #[ffi_func] pub fn estimate_transaction_size(transaction: Transaction) -> Result { let core_tx: algokit_transact::Transaction = transaction.try_into()?; - core_tx.estimate_size()?.try_into().map_err(|_| AlgoKitTransactError::EncodingError { message: "Failed to convert size to u64".to_string() }) + core_tx + .estimate_size()? + .try_into() + .map_err(|_| AlgoKitTransactError::EncodingError { + message: "Failed to convert size to u64".to_string(), + }) } #[ffi_func] @@ -686,10 +713,12 @@ pub fn keypair_account_from_pub_key( ) -> Result { Ok( algokit_transact::KeyPairAccount::from_pubkey(pub_key.try_into().map_err(|_| { - AlgoKitTransactError::EncodingError { message: format!( - "public key should be {} bytes", - ALGORAND_PUBLIC_KEY_BYTE_LENGTH - ) } + AlgoKitTransactError::EncodingError { + message: format!( + "public key should be {} bytes", + ALGORAND_PUBLIC_KEY_BYTE_LENGTH + ), + } })?) .into(), ) diff --git a/crates/algokit_transact_ffi/src/multisig.rs b/crates/algokit_transact_ffi/src/multisig.rs index 181720d30..87162c56b 100644 --- a/crates/algokit_transact_ffi/src/multisig.rs +++ b/crates/algokit_transact_ffi/src/multisig.rs @@ -69,10 +69,9 @@ impl TryFrom for algokit_transact::MultisigSubsignature { .signature .map(|sig| bytebuf_to_bytes(&sig)) .transpose() - .map_err(|e| AlgoKitTransactError::DecodingError { message: format!( - "Error while decoding a subsignature: {}", - e - )})?, + .map_err(|e| AlgoKitTransactError::DecodingError { + message: format!("Error while decoding a subsignature: {}", e), + })?, }) } } @@ -141,10 +140,14 @@ pub fn apply_multisig_subsignature( let multisignature: algokit_transact::MultisigSignature = multisig_signature.try_into()?; let partially_signed_multisignature = multisignature.apply_subsignature( participant.parse()?, - subsignature.try_into().map_err(|_| AlgoKitTransactError::EncodingError { message: format!( - "signature should be {} bytes", - ALGORAND_SIGNATURE_BYTE_LENGTH - )})?, + subsignature + .try_into() + .map_err(|_| AlgoKitTransactError::EncodingError { + message: format!( + "signature should be {} bytes", + ALGORAND_SIGNATURE_BYTE_LENGTH + ), + })?, )?; Ok(partially_signed_multisignature.into()) } diff --git a/crates/algokit_transact_ffi/src/transactions/application_call.rs b/crates/algokit_transact_ffi/src/transactions/application_call.rs index 1f316409b..af89bb649 100644 --- a/crates/algokit_transact_ffi/src/transactions/application_call.rs +++ b/crates/algokit_transact_ffi/src/transactions/application_call.rs @@ -101,7 +101,9 @@ impl TryFrom for algokit_transact::ApplicationCallTransactionFields fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::ApplicationCall || tx.application_call.is_none() { - return Err(Self::Error::DecodingError { message: "Application call data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Application call data missing".to_string(), + }); } let data = tx.clone().application_call.unwrap(); @@ -135,10 +137,11 @@ impl TryFrom for algokit_transact::ApplicationCallTransactionFields .map(|boxes| boxes.into_iter().map(Into::into).collect()), }; - transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( - "Application call validation failed: {}", - errors.join("\n") - )})?; + transaction_fields + .validate() + .map_err(|errors| AlgoKitTransactError::DecodingError { + message: format!("Application call validation failed: {}", errors.join("\n")), + })?; Ok(transaction_fields) } diff --git a/crates/algokit_transact_ffi/src/transactions/asset_config.rs b/crates/algokit_transact_ffi/src/transactions/asset_config.rs index 282e6e47c..93052cdd4 100644 --- a/crates/algokit_transact_ffi/src/transactions/asset_config.rs +++ b/crates/algokit_transact_ffi/src/transactions/asset_config.rs @@ -138,7 +138,9 @@ impl TryFrom for algokit_transact::AssetConfigTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetConfig || tx.asset_config.is_none() { - return Err(Self::Error::DecodingError { message: "Asset configuration data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Asset configuration data missing".to_string(), + }); } let data = tx.clone().asset_config.unwrap(); @@ -165,10 +167,11 @@ impl TryFrom for algokit_transact::AssetConfigTransactionFields { clawback: data.clawback.map(|addr| addr.parse()).transpose()?, }; - transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( - "Asset config validation failed: {}", - errors.join("\n") - )})?; + transaction_fields + .validate() + .map_err(|errors| AlgoKitTransactError::DecodingError { + message: format!("Asset config validation failed: {}", errors.join("\n")), + })?; Ok(transaction_fields) } diff --git a/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs b/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs index 77c5a9bbf..b1cd10cce 100644 --- a/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs +++ b/crates/algokit_transact_ffi/src/transactions/asset_freeze.rs @@ -34,7 +34,9 @@ impl TryFrom for algokit_transact::AssetFreezeTransactionFields { fn try_from(tx: Transaction) -> Result { if tx.transaction_type != TransactionType::AssetFreeze || tx.asset_freeze.is_none() { - return Err(Self::Error::DecodingError { message: "Asset Freeze data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Asset Freeze data missing".to_string(), + }); } let data = tx.clone().asset_freeze.unwrap(); diff --git a/crates/algokit_transact_ffi/src/transactions/keyreg.rs b/crates/algokit_transact_ffi/src/transactions/keyreg.rs index 67be468b6..96ff5a372 100644 --- a/crates/algokit_transact_ffi/src/transactions/keyreg.rs +++ b/crates/algokit_transact_ffi/src/transactions/keyreg.rs @@ -54,7 +54,9 @@ impl TryFrom for algokit_transact::KeyRegistrationTransactio if tx.transaction_type != crate::TransactionType::KeyRegistration || tx.key_registration.is_none() { - return Err(Self::Error::DecodingError { message: "Key Registration data missing".to_string() }); + return Err(Self::Error::DecodingError { + message: "Key Registration data missing".to_string(), + }); } let data = tx.clone().key_registration.unwrap(); @@ -80,10 +82,11 @@ impl TryFrom for algokit_transact::KeyRegistrationTransactio non_participation: data.non_participation, }; - transaction_fields.validate().map_err(|errors| AlgoKitTransactError::DecodingError { message: format!( - "Key registration validation failed: {}", - errors.join("\n") - )})?; + transaction_fields + .validate() + .map_err(|errors| AlgoKitTransactError::DecodingError { + message: format!("Key registration validation failed: {}", errors.join("\n")), + })?; Ok(transaction_fields) } diff --git a/crates/algokit_utils/src/clients/app_manager.rs b/crates/algokit_utils/src/clients/app_manager.rs index f199c3d25..a4bbafe43 100644 --- a/crates/algokit_utils/src/clients/app_manager.rs +++ b/crates/algokit_utils/src/clients/app_manager.rs @@ -6,9 +6,9 @@ use algokit_abi::{ABIMethod, ABIReturn, ABIType, ABIValue}; use algokit_transact::Address; use base64::{Engine, engine::general_purpose::STANDARD as Base64}; use sha2::{Digest, Sha256}; +use snafu::Snafu; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use snafu::Snafu; #[derive(Debug, Clone)] pub enum TealTemplateValue { @@ -188,12 +188,16 @@ impl AppManager { Ok(AppInformation { app_id, app_address: Address::from_app_id(&app_id), - approval_program: Base64 - .decode(&app.params.approval_program) - .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?, + approval_program: Base64.decode(&app.params.approval_program).map_err(|e| { + AppManagerError::DecodingError { + message: e.to_string(), + } + })?, clear_state_program: Base64 .decode(&app.params.clear_state_program) - .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?, + .map_err(|e| AppManagerError::DecodingError { + message: e.to_string(), + })?, creator: app.params.creator, local_ints: app .params @@ -296,7 +300,9 @@ impl AppManager { Base64 .decode(&box_result.value) - .map_err(|e| AppManagerError::DecodingError { message: e.to_string() }) + .map_err(|e| AppManagerError::DecodingError { + message: e.to_string(), + }) } /// Get values for multiple boxes. @@ -332,9 +338,12 @@ impl AppManager { abi_type: &ABIType, ) -> Result { let raw_value = self.get_box_value(app_id, box_name).await?; - let decoded_value = abi_type - .decode(&raw_value) - .map_err(|e| AppManagerError::ABIDecodeError { message: e.to_string() })?; + let decoded_value = + abi_type + .decode(&raw_value) + .map_err(|e| AppManagerError::ABIDecodeError { + message: e.to_string(), + })?; Ok(decoded_value) } @@ -373,9 +382,11 @@ impl AppManager { method: &ABIMethod, ) -> Result, AppManagerError> { if let Some(return_type) = &method.returns { - let return_value = return_type - .decode(confirmation_data) - .map_err(|e| AppManagerError::ABIDecodeError { message: e.to_string() })?; + let return_value = return_type.decode(confirmation_data).map_err(|e| { + AppManagerError::ABIDecodeError { + message: e.to_string(), + } + })?; Ok(Some(ABIReturn { method: method.clone(), @@ -400,9 +411,12 @@ impl AppManager { let mut state_values = HashMap::new(); for state_val in state { - let key_raw = Base64 - .decode(&state_val.key) - .map_err(|e| AppManagerError::DecodingError { message: e.to_string() })?; + let key_raw = + Base64 + .decode(&state_val.key) + .map_err(|e| AppManagerError::DecodingError { + message: e.to_string(), + })?; // TODO(stabilization): Consider r#type pattern consistency across API vs ABI types (PR #229 comment) let (value_raw, value_base64, value) = match state_val.value.r#type { @@ -420,10 +434,9 @@ impl AppManager { } 2 => (None, None, AppStateValue::Uint(state_val.value.uint)), _ => { - return Err(AppManagerError::DecodingError { message: format!( - "Unknown state data type: {}", - state_val.value.r#type - ) }); + return Err(AppManagerError::DecodingError { + message: format!("Unknown state data type: {}", state_val.value.r#type), + }); } }; @@ -550,20 +563,24 @@ impl AppManager { if let Some(updatable) = params.updatable { if !teal_template_code.contains(UPDATABLE_TEMPLATE_NAME) { - return Err(AppManagerError::TemplateVariableNotFound { message: format!( - "Deploy-time updatability control requested, but {} not present in TEAL code", - UPDATABLE_TEMPLATE_NAME - ) }); + return Err(AppManagerError::TemplateVariableNotFound { + message: format!( + "Deploy-time updatability control requested, but {} not present in TEAL code", + UPDATABLE_TEMPLATE_NAME + ), + }); } result = result.replace(UPDATABLE_TEMPLATE_NAME, &(updatable as u8).to_string()); } if let Some(deletable) = params.deletable { if !teal_template_code.contains(DELETABLE_TEMPLATE_NAME) { - return Err(AppManagerError::TemplateVariableNotFound { message: format!( - "Deploy-time deletability control requested, but {} not present in TEAL code", - DELETABLE_TEMPLATE_NAME - ) }); + return Err(AppManagerError::TemplateVariableNotFound { + message: format!( + "Deploy-time deletability control requested, but {} not present in TEAL code", + DELETABLE_TEMPLATE_NAME + ), + }); } result = result.replace(DELETABLE_TEMPLATE_NAME, &(deletable as u8).to_string()); } diff --git a/crates/algokit_utils/src/clients/asset_manager.rs b/crates/algokit_utils/src/clients/asset_manager.rs index 04753a27a..4faec0b80 100644 --- a/crates/algokit_utils/src/clients/asset_manager.rs +++ b/crates/algokit_utils/src/clients/asset_manager.rs @@ -1,8 +1,8 @@ use algod_client::apis::{AlgodClient, Error as AlgodError}; use algod_client::models::{AccountAssetInformation as AlgodAccountAssetInformation, Asset}; use algokit_transact::Address; -use std::{str::FromStr, sync::Arc}; use snafu::Snafu; +use std::{str::FromStr, sync::Arc}; use crate::transactions::{ AssetOptInParams, AssetOptOutParams, CommonParams, Composer, ComposerError, diff --git a/crates/algokit_utils/src/testing/indexer_helpers.rs b/crates/algokit_utils/src/testing/indexer_helpers.rs index 040d4df8a..8f87e418c 100644 --- a/crates/algokit_utils/src/testing/indexer_helpers.rs +++ b/crates/algokit_utils/src/testing/indexer_helpers.rs @@ -1,8 +1,8 @@ use indexer_client::{IndexerClient, apis::Error as IndexerError}; +use snafu::Snafu; use std::future::Future; use std::time::Duration; use tokio::time::sleep; -use snafu::Snafu; /// Configuration for indexer wait operations #[derive(Debug, Clone)] @@ -59,7 +59,9 @@ where // If it's not a 404-like error, fail immediately if !is_not_found { - return Err(IndexerWaitError::ClientError { message: last_error }); + return Err(IndexerWaitError::ClientError { + message: last_error, + }); } // If we've reached max attempts, break out of the loop @@ -121,7 +123,9 @@ pub async fn wait_for_indexer_transaction( .and_then(|response| { if response.transactions.is_empty() { // Return a string error that will be treated as "not found" - Err(IndexerError::Serde { message: "Transaction not found".to_string() }) + Err(IndexerError::Serde { + message: "Transaction not found".to_string(), + }) } else { Ok(()) } diff --git a/crates/algokit_utils/src/transactions/application_call.rs b/crates/algokit_utils/src/transactions/application_call.rs index f68719829..bfa2d9397 100644 --- a/crates/algokit_utils/src/transactions/application_call.rs +++ b/crates/algokit_utils/src/transactions/application_call.rs @@ -570,7 +570,9 @@ fn populate_method_args_into_reference_arrays( match value { ABIReferenceValue::Account(addr_str) => { let address = Address::from_str(addr_str).map_err(|_e| { - ComposerError::TransactionError { message: format!("Invalid address {}", addr_str) } + ComposerError::TransactionError { + message: format!("Invalid address {}", addr_str), + } })?; if address != *sender && !account_references.contains(&address) { @@ -604,9 +606,10 @@ fn calculate_method_arg_reference_array_index( ) -> Result { match ref_value { ABIReferenceValue::Account(addr_str) => { - let address = Address::from_str(addr_str).map_err(|_e| { - ComposerError::TransactionError { message: format!("Invalid address {}", addr_str) } - })?; + let address = + Address::from_str(addr_str).map_err(|_e| ComposerError::TransactionError { + message: format!("Invalid address {}", addr_str), + })?; if address == *sender { // If address is the same as sender, use index 0 @@ -618,10 +621,9 @@ fn calculate_method_arg_reference_array_index( // If address already exists in account_references, use existing index + 1 Ok((existing_index + 1) as u8) } else { - Err(ComposerError::ABIEncodingError { message: format!( - "Account {} not found in reference array", - addr_str - ) }) + Err(ComposerError::ABIEncodingError { + message: format!("Account {} not found in reference array", addr_str), + }) } } ABIReferenceValue::Asset(asset_id) => { @@ -632,10 +634,9 @@ fn calculate_method_arg_reference_array_index( // If asset already exists in asset_references, use existing index Ok(existing_index as u8) } else { - Err(ComposerError::ABIEncodingError { message: format!( - "Asset {} not found in reference array", - asset_id - ) }) + Err(ComposerError::ABIEncodingError { + message: format!("Asset {} not found in reference array", asset_id), + }) } } ABIReferenceValue::Application(app_id_ref) => { @@ -649,10 +650,9 @@ fn calculate_method_arg_reference_array_index( // If application already exists in app_references, use existing index + 1 Ok((existing_index + 1) as u8) } else { - Err(ComposerError::ABIEncodingError { message: format!( - "Application {} not found in reference array", - app_id_ref - ) }) + Err(ComposerError::ABIEncodingError { + message: format!("Application {} not found in reference array", app_id_ref), + }) } } } @@ -703,7 +703,9 @@ fn encode_arguments( .collect::, _>>()?; if abi_values.len() != abi_types.len() { - return Err(ComposerError::ABIEncodingError { message: "Mismatch in length of non-transaction arguments".to_string() }); + return Err(ComposerError::ABIEncodingError { + message: "Mismatch in length of non-transaction arguments".to_string(), + }); } // Apply ARC-4 tuple packing for methods with more than 14 arguments @@ -730,9 +732,12 @@ fn encode_args_with_tuple_packing( let remaining_abi_values = &abi_values[ARGS_TUPLE_PACKING_THRESHOLD..]; let tuple_type = ABIType::Tuple(remaining_abi_types.to_vec()); let tuple_value = ABIValue::Array(remaining_abi_values.to_vec()); - let tuple_encoded = tuple_type.encode(&tuple_value).map_err(|e| { - ComposerError::ABIEncodingError { message: format!("Failed to encode ABI value: {}", e) } - })?; + let tuple_encoded = + tuple_type + .encode(&tuple_value) + .map_err(|e| ComposerError::ABIEncodingError { + message: format!("Failed to encode ABI value: {}", e), + })?; encoded_args.push(tuple_encoded); @@ -746,9 +751,11 @@ fn encode_args_individually( let encoded_args: &mut Vec> = &mut Vec::new(); for (abi_value, abi_type) in abi_values.iter().zip(abi_types.iter()) { - let encoded = abi_type.encode(abi_value).map_err(|e| { - ComposerError::ABIEncodingError { message: format!("Failed to encode ABI value: {}", e) } - })?; + let encoded = abi_type + .encode(abi_value) + .map_err(|e| ComposerError::ABIEncodingError { + message: format!("Failed to encode ABI value: {}", e), + })?; encoded_args.push(encoded); } @@ -875,9 +882,13 @@ where )?; // Insert method selector at the front - let method_selector = params.method().selector().map_err(|e| { - ComposerError::ABIEncodingError { message: format!("Failed to get method selector: {}", e) } - })?; + let method_selector = + params + .method() + .selector() + .map_err(|e| ComposerError::ABIEncodingError { + message: format!("Failed to get method selector: {}", e), + })?; encoded_args.insert(0, method_selector); Ok(transaction_builder( diff --git a/crates/algokit_utils/src/transactions/composer.rs b/crates/algokit_utils/src/transactions/composer.rs index c26f4dae5..10618bf77 100644 --- a/crates/algokit_utils/src/transactions/composer.rs +++ b/crates/algokit_utils/src/transactions/composer.rs @@ -14,8 +14,8 @@ use algokit_transact::{ Transactions, }; use derive_more::Debug; -use std::{collections::HashMap, sync::Arc}; use snafu::Snafu; +use std::{collections::HashMap, sync::Arc}; use crate::{ AppMethodCallArg, @@ -83,11 +83,15 @@ pub enum ComposerError { } impl From for ComposerError { - fn from(e: AlgodError) -> Self { Self::AlgodClientError { source: e } } + fn from(e: AlgodError) -> Self { + Self::AlgodClientError { source: e } + } } impl From for ComposerError { - fn from(e: AlgoKitTransactError) -> Self { Self::TransactError { source: e } } + fn from(e: AlgoKitTransactError) -> Self { + Self::TransactError { source: e } + } } #[derive(Debug)] @@ -691,19 +695,23 @@ impl Composer { let last_log = match confirmation.logs.as_ref().and_then(|logs| logs.last()) { Some(log) => log, None => { - return Err(ComposerError::ABIDecodingError { message: format!( - "No logs found for method {} which requires a return type", - method.name - ) }); + return Err(ComposerError::ABIDecodingError { + message: format!( + "No logs found for method {} which requires a return type", + method.name + ), + }); } }; // Check if the last log entry has the ABI return prefix if !last_log.starts_with(ABI_RETURN_PREFIX) { - return Err(ComposerError::ABIDecodingError { message: format!( - "Transaction log for method {} doesn't match with ABI return value format", - method.name - ) }); + return Err(ComposerError::ABIDecodingError { + message: format!( + "Transaction log for method {} doesn't match with ABI return value format", + method.name + ), + }); } // Extract the return value bytes (skip the prefix) @@ -716,10 +724,12 @@ impl Composer { raw_return_value: return_bytes.to_vec(), return_value, })), - Err(e) => Err(ComposerError::ABIDecodingError { message: format!( - "Failed to decode ABI return value for method {}: {}", - method.name, e - ) }), + Err(e) => Err(ComposerError::ABIDecodingError { + message: format!( + "Failed to decode ABI return value for method {}: {}", + method.name, e + ), + }), } } @@ -805,9 +815,12 @@ impl Composer { // Regroup the transactions, as the transactions have likely been adjusted if transactions.len() > 1 { - transactions = transactions.assign_group().map_err(|e| { - ComposerError::TransactionError { message: format!("Failed to assign group: {}", e) } - })?; + transactions = + transactions + .assign_group() + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to assign group: {}", e), + })?; } let signed_transactions = transactions @@ -821,14 +834,16 @@ impl Composer { .collect(); if cover_inner_fees && !app_call_indexes_without_max_fees.is_empty() { - return Err(ComposerError::StateError { message: format!( - "Please provide a max fee for each application call transaction when inner transaction fee coverage is enabled. Required for transaction {}", - app_call_indexes_without_max_fees - .iter() - .map(|i| i.to_string()) - .collect::>() - .join(", ") - ) }); + return Err(ComposerError::StateError { + message: format!( + "Please provide a max fee for each application call transaction when inner transaction fee coverage is enabled. Required for transaction {}", + app_call_indexes_without_max_fees + .iter() + .map(|i| i.to_string()) + .collect::>() + .join(", ") + ), + }); } let txn_group = SimulateRequestTransactionGroup { @@ -868,10 +883,12 @@ impl Composer { }) .unwrap_or_else(|| "unknown".to_string()); - return Err(ComposerError::StateError { message: format!( - "Error analyzing group requirements via simulate in transaction {}: {}", - failed_at, failure_message - ) }); + return Err(ComposerError::StateError { + message: format!( + "Error analyzing group requirements via simulate in transaction {}: {}", + failed_at, failure_message + ), + }); } let txn_analysis_results: Result, ComposerError> = group_response @@ -888,11 +905,8 @@ impl Composer { min_fee: suggested_params.min_fee, ..Default::default() }) - .map_err(|e| { - ComposerError::TransactionError { message: format!( - "Failed to calculate min transaction fee: {}", - e - ) } + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to calculate min transaction fee: {}", e), })?; let txn_fee = btxn.header().fee.unwrap_or(0); @@ -982,7 +996,9 @@ impl Composer { .genesis_hash .clone() .try_into() - .map_err(|_e| ComposerError::DecodeError { message: "Invalid genesis hash".to_string() })?, + .map_err(|_e| ComposerError::DecodeError { + message: "Invalid genesis hash".to_string(), + })?, ), first_valid, last_valid: common_params.last_valid_round.unwrap_or_else(|| { @@ -1039,7 +1055,9 @@ impl Composer { build_asset_clawback(params, header) } ComposerTransaction::AssetCreate(params) => build_asset_create(params, header) - .map_err(|e| ComposerError::TransactionError { message: e.to_string() })?, + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?, ComposerTransaction::AssetReconfigure(params) => { build_asset_reconfigure(params, header) } @@ -1050,9 +1068,10 @@ impl Composer { ComposerTransaction::AssetUnfreeze(params) => { build_asset_unfreeze(params, header) } - ComposerTransaction::AppCall(params) => { - build_app_call(params, header).map_err(|e| ComposerError::TransactionError { message: e.to_string() })? - } + ComposerTransaction::AppCall(params) => build_app_call(params, header) + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?, ComposerTransaction::AppCreateCall(params) => { build_app_create_call(params, header) } @@ -1093,7 +1112,9 @@ impl Composer { extra_fee: common_params.extra_fee, max_fee: common_params.max_fee, }) - .map_err(|e| ComposerError::TransactionError { message: e.to_string() })?; + .map_err(|e| ComposerError::TransactionError { + message: e.to_string(), + })?; } Ok(transaction) @@ -1180,21 +1201,25 @@ impl Composer { if logical_max_fee.is_none() || transaction_fee > logical_max_fee.unwrap() { - return Err(ComposerError::TransactionError { message: format!( - "Calculated transaction fee {} µALGO is greater than max of {} for transaction {}", - transaction_fee, - logical_max_fee.unwrap_or(0), - group_index - ) }); + return Err(ComposerError::TransactionError { + message: format!( + "Calculated transaction fee {} µALGO is greater than max of {} for transaction {}", + transaction_fee, + logical_max_fee.unwrap_or(0), + group_index + ), + }); } txn_header.fee = Some(transaction_fee); } _ => { - return Err(ComposerError::TransactionError { message: format!( - "An additional fee of {} µALGO is required for non application call transaction {}", - deficit_amount, group_index - ) }); + return Err(ComposerError::TransactionError { + message: format!( + "An additional fee of {} µALGO is required for non application call transaction {}", + deficit_amount, group_index + ), + }); } } } @@ -1203,9 +1228,12 @@ impl Composer { } if transactions.len() > 1 { - transactions = transactions.assign_group().map_err(|e| { - ComposerError::TransactionError { message: format!("Failed to assign group: {}", e) } - })?; + transactions = + transactions + .assign_group() + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to assign group: {}", e), + })?; } Ok(transactions) @@ -1269,10 +1297,9 @@ impl Composer { } else { let sender_address = txn.header().sender.clone(); self.get_signer(sender_address.clone()) - .ok_or(ComposerError::SigningError { message: format!( - "No signer found for address: {}", - sender_address - ) })? + .ok_or(ComposerError::SigningError { + message: format!("No signer found for address: {}", sender_address), + })? }; Ok(TransactionWithSigner { transaction: txn, @@ -1288,7 +1315,10 @@ impl Composer { } let transactions_with_signers = - self.built_group.as_ref().ok_or(ComposerError::StateError { message: "Cannot gather signatures before building the transaction group".to_string() })?; + self.built_group.as_ref().ok_or(ComposerError::StateError { + message: "Cannot gather signatures before building the transaction group" + .to_string(), + })?; // Group transactions by signer let mut transactions = Vec::new(); @@ -1309,7 +1339,9 @@ impl Composer { let signed_txns = signer .sign_transactions(&transactions, &indices) .await - .map_err(|e| ComposerError::SigningError { message: e.to_string() })?; + .map_err(|e| ComposerError::SigningError { + message: e.to_string(), + })?; for (i, &index) in indices.iter().enumerate() { signed_transactions[index] = Some(signed_txns[i].to_owned()); @@ -1320,11 +1352,8 @@ impl Composer { .into_iter() .enumerate() .map(|(i, signed_transaction)| { - signed_transaction.ok_or_else(|| { - ComposerError::SigningError { message: format!( - "Transaction at index {} was not signed", - i - ) } + signed_transaction.ok_or_else(|| ComposerError::SigningError { + message: format!("Transaction at index {} was not signed", i), }) }) .collect(); @@ -1339,9 +1368,13 @@ impl Composer { tx_id: &str, max_rounds: u64, ) -> Result { - let status = self.algod_client.get_status().await.map_err(|e| { - ComposerError::TransactionError { message: format!("Failed to get status: {:?}", e) } - })?; + let status = + self.algod_client + .get_status() + .await + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to get status: {:?}", e), + })?; let start_round = status.last_round + 1; let mut current_round = start_round; @@ -1355,7 +1388,9 @@ impl Composer { Ok(response) => { // Check for pool errors first - transaction was kicked out of pool if !response.pool_error.is_empty() { - return Err(ComposerError::PoolError { message: response.pool_error.clone() }); + return Err(ComposerError::PoolError { + message: response.pool_error.clone(), + }); } // Check if transaction is confirmed @@ -1388,10 +1423,12 @@ impl Composer { current_round += 1; } - Err(ComposerError::MaxWaitRoundExpired { message: format!( - "Transaction {} unconfirmed after {} rounds", - tx_id, max_rounds - ) }) + Err(ComposerError::MaxWaitRoundExpired { + message: format!( + "Transaction {} unconfirmed after {} rounds", + tx_id, max_rounds + ), + }) } pub async fn send( @@ -1403,19 +1440,27 @@ impl Composer { self.build(build_params).await?; let group_id = { - let transactions_with_signers = self.built_group.as_ref().ok_or( - ComposerError::StateError { message: "No transactions built".to_string() }, - )?; + let transactions_with_signers = + self.built_group.as_ref().ok_or(ComposerError::StateError { + message: "No transactions built".to_string(), + })?; if transactions_with_signers.is_empty() { - return Err(ComposerError::StateError { message: "No transactions to send".to_string() }); + return Err(ComposerError::StateError { + message: "No transactions to send".to_string(), + }); } transactions_with_signers[0].transaction.header().group }; self.gather_signatures().await?; - let signed_transactions = self.signed_group.as_ref().ok_or(ComposerError::StateError { message: "No signed transactions".to_string() })?; + let signed_transactions = self + .signed_group + .as_ref() + .ok_or(ComposerError::StateError { + message: "No signed transactions".to_string(), + })?; let wait_rounds = if let Some(max_rounds_to_wait_for_confirmation) = params.and_then(|p| p.max_rounds_to_wait_for_confirmation) @@ -1426,13 +1471,17 @@ impl Composer { .iter() .map(|signed_transaction| signed_transaction.transaction.header().first_valid) .min() - .ok_or(ComposerError::StateError { message: "Failed to calculate first valid round".to_string() })?; + .ok_or(ComposerError::StateError { + message: "Failed to calculate first valid round".to_string(), + })?; let last_round: u64 = signed_transactions .iter() .map(|signed_transaction| signed_transaction.transaction.header().last_valid) .max() - .ok_or(ComposerError::StateError { message: "Failed to calculate last valid round".to_string() })?; + .ok_or(ComposerError::StateError { + message: "Failed to calculate last valid round".to_string(), + })?; last_round - first_round }; @@ -1441,12 +1490,11 @@ impl Composer { let mut encoded_bytes = Vec::new(); for signed_txn in signed_transactions { - let encoded_txn = signed_txn.encode().map_err(|e| { - ComposerError::TransactionError { message: format!( - "Failed to encode signed transaction: {}", - e - ) } - })?; + let encoded_txn = signed_txn + .encode() + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to encode signed transaction: {}", e), + })?; encoded_bytes.extend_from_slice(&encoded_txn); } @@ -1454,8 +1502,8 @@ impl Composer { .algod_client .raw_transaction(encoded_bytes) .await - .map_err(|e| { - ComposerError::TransactionError { message: format!("Failed to submit transaction(s): {:?}", e) } + .map_err(|e| ComposerError::TransactionError { + message: format!("Failed to submit transaction(s): {:?}", e), })?; let transaction_ids: Vec = signed_transactions diff --git a/crates/algokit_utils/src/transactions/creator.rs b/crates/algokit_utils/src/transactions/creator.rs index 363353cbc..f512d302f 100644 --- a/crates/algokit_utils/src/transactions/creator.rs +++ b/crates/algokit_utils/src/transactions/creator.rs @@ -48,7 +48,9 @@ impl TransactionCreator { built_transactions .last() .map(|tx_with_signer| tx_with_signer.transaction.clone()) - .ok_or(ComposerError::StateError { message: "No transactions were built by the composer".to_string() }) + .ok_or(ComposerError::StateError { + message: "No transactions were built by the composer".to_string(), + }) } pub async fn payment(&self, params: PaymentParams) -> Result { @@ -70,7 +72,9 @@ impl TransactionCreator { ) -> Result { // Enhanced parameter validation if params.asset_id == 0 { - return Err(ComposerError::TransactionError { message: "Asset ID must be greater than 0".to_string() }); + return Err(ComposerError::TransactionError { + message: "Asset ID must be greater than 0".to_string(), + }); } // Note: amount can be 0 for opt-in transactions, so we don't validate it here diff --git a/crates/algokit_utils/src/transactions/sender.rs b/crates/algokit_utils/src/transactions/sender.rs index ac590eb9f..b393f20e6 100644 --- a/crates/algokit_utils/src/transactions/sender.rs +++ b/crates/algokit_utils/src/transactions/sender.rs @@ -42,23 +42,33 @@ pub enum TransactionSenderError { } impl From for TransactionSenderError { - fn from(e: AlgodApiError) -> Self { Self::AlgodClientError { source: e } } + fn from(e: AlgodApiError) -> Self { + Self::AlgodClientError { source: e } + } } impl From for TransactionSenderError { - fn from(e: ComposerError) -> Self { Self::ComposerError { source: e } } + fn from(e: ComposerError) -> Self { + Self::ComposerError { source: e } + } } impl From for TransactionSenderError { - fn from(e: AssetManagerError) -> Self { Self::AssetManagerError { source: e } } + fn from(e: AssetManagerError) -> Self { + Self::AssetManagerError { source: e } + } } impl From for TransactionSenderError { - fn from(e: AppManagerError) -> Self { Self::AppManagerError { source: e } } + fn from(e: AppManagerError) -> Self { + Self::AppManagerError { source: e } + } } impl From for TransactionSenderError { - fn from(e: TransactionResultError) -> Self { Self::TransactionResultError { source: e } } + fn from(e: TransactionResultError) -> Self { + Self::TransactionResultError { source: e } + } } /// Sends transactions and groups with validation and result processing. @@ -203,7 +213,9 @@ impl TransactionSender { let returns: Result, _> = composer_results .abi_returns .into_iter() - .map(|result| result.map_err(|e| TransactionSenderError::ComposerError { source: e })) + .map(|result| { + result.map_err(|e| TransactionSenderError::ComposerError { source: e }) + }) .collect(); match returns { Ok(returns) => { @@ -362,7 +374,9 @@ impl TransactionSender { ) -> Result { // Enhanced parameter validation if params.asset_id == 0 { - return Err(TransactionSenderError::InvalidParameters { message: "Asset ID must be greater than 0".to_string() }); + return Err(TransactionSenderError::InvalidParameters { + message: "Asset ID must be greater than 0".to_string(), + }); } // Note: amount can be 0 for opt-in transactions, so we don't validate it here @@ -393,18 +407,17 @@ impl TransactionSender { .asset_manager .get_by_id(params.asset_id) .await - .map_err(|e| { - TransactionSenderError::ValidationError { message: format!( - "Failed to get asset {} information: {}", - params.asset_id, e - ) } + .map_err(|e| TransactionSenderError::ValidationError { + message: format!("Failed to get asset {} information: {}", params.asset_id, e), })?; let creator = Address::from_str(&asset_info.creator).map_err(|e| { - TransactionSenderError::ValidationError { message: format!( - "Invalid creator address for asset {}: {}", - params.asset_id, e - ) } + TransactionSenderError::ValidationError { + message: format!( + "Invalid creator address for asset {}: {}", + params.asset_id, e + ), + } })?; AssetOptOutParams { @@ -421,11 +434,11 @@ impl TransactionSender { .asset_manager .get_account_information(¶ms.common_params.sender, params.asset_id) .await - .map_err(|e| { - TransactionSenderError::ValidationError { message: format!( + .map_err(|e| TransactionSenderError::ValidationError { + message: format!( "Account {} validation failed for Asset {}: {}", params.common_params.sender, params.asset_id, e - ) } + ), })?; let balance = account_info @@ -434,10 +447,12 @@ impl TransactionSender { .map(|h| h.amount) .unwrap_or(0); if balance != 0 { - return Err(TransactionSenderError::ValidationError { message: format!( - "Account {} does not have a zero balance for Asset {}; can't opt-out.", - params.common_params.sender, params.asset_id - ) }); + return Err(TransactionSenderError::ValidationError { + message: format!( + "Account {} does not have a zero balance for Asset {}; can't opt-out.", + params.common_params.sender, params.asset_id + ), + }); } } From e142cf5e9b9057cd07ce70f9aa6fa66a28c65c27 Mon Sep 17 00:00:00 2001 From: Joe Polny Date: Mon, 18 Aug 2025 11:16:03 -0400 Subject: [PATCH 5/6] fix: fix remaining error-related errors --- crates/algokit_http_client/src/lib.rs | 17 ++++++++--------- .../src/testing/indexer_helpers.rs | 5 ++++- .../tests/clients/asset_manager.rs | 4 ++-- .../tests/indexer/search_applications.rs | 4 +++- .../algokit_utils/tests/transactions/sender.rs | 5 ++++- scripts/sanity.sh | 4 ++++ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/crates/algokit_http_client/src/lib.rs b/crates/algokit_http_client/src/lib.rs index 14d4ae0da..085249934 100644 --- a/crates/algokit_http_client/src/lib.rs +++ b/crates/algokit_http_client/src/lib.rs @@ -265,18 +265,17 @@ impl HttpClient for WasmHttpClient { let result = self .request(method.as_str(), &path, &query_js, &body_js, &headers_js) .await - .map_err(|e| { - HttpError::RequestError( - e.as_string().unwrap_or( - "A HTTP error occurred in JavaScript, but it cannot be converted to a string" - .to_string(), - ), - ) + .map_err(|e| HttpError::RequestError { + message: e.as_string().unwrap_or( + "A HTTP error occurred in JavaScript, but it cannot be converted to a string" + .to_string(), + ), })?; // Parse the response from JavaScript - let response = HttpResponse::from_js(result) - .map_err(|e| HttpError::RequestError(format!("Failed to parse response: {:?}", e)))?; + let response = HttpResponse::from_js(result).map_err(|e| HttpError::RequestError { + message: format!("Failed to parse response: {:?}", e), + })?; Ok(response) } diff --git a/crates/algokit_utils/src/testing/indexer_helpers.rs b/crates/algokit_utils/src/testing/indexer_helpers.rs index 8f87e418c..f52fae2c5 100644 --- a/crates/algokit_utils/src/testing/indexer_helpers.rs +++ b/crates/algokit_utils/src/testing/indexer_helpers.rs @@ -198,6 +198,9 @@ mod tests { async fn fails_immediately_on_non_retriable_error() { let result = wait_for_indexer(|| async { Err::<(), &str>("server error") }, None).await; - assert!(matches!(result, Err(IndexerWaitError::ClientError(_)))); + assert!(matches!( + result, + Err(IndexerWaitError::ClientError { message: _ }) + )); } } diff --git a/crates/algokit_utils/tests/clients/asset_manager.rs b/crates/algokit_utils/tests/clients/asset_manager.rs index 1f405eb70..2839bbe25 100644 --- a/crates/algokit_utils/tests/clients/asset_manager.rs +++ b/crates/algokit_utils/tests/clients/asset_manager.rs @@ -49,7 +49,7 @@ async fn test_get_asset_by_id_nonexistent() -> Result<(), Box Date: Mon, 18 Aug 2025 11:21:19 -0400 Subject: [PATCH 6/6] chore: fix extra space in error message Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- crates/algokit_transact/src/multisig.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/algokit_transact/src/multisig.rs b/crates/algokit_transact/src/multisig.rs index 057049811..21becb637 100644 --- a/crates/algokit_transact/src/multisig.rs +++ b/crates/algokit_transact/src/multisig.rs @@ -87,7 +87,7 @@ impl MultisigSignature { }); } if threshold == 0 || threshold as usize > subsignatures.len() { - return Err(AlgoKitTransactError::InvalidMultisigSignature { message: "Threshold must be greater than zero and less than or equal to the number of sub-signers".to_string() }); + return Err(AlgoKitTransactError::InvalidMultisigSignature { message: "Threshold must be greater than zero and less than or equal to the number of sub-signers".to_string() }); } Ok(Self { version,