devol_accounts_kit/
dvl_error.rs1use std::fmt::Formatter;
2use crate::errors::{AccountTag, ContractError};
3
4#[derive(Clone, Copy)]
5pub struct DvlError {
6 error: ContractError,
7 account: Option<AccountTag>,
8}
9
10impl DvlError {
11 pub fn new_with_account(account: AccountTag, error: ContractError) -> Self {
13 Self {
14 error,
15 account: Some(account),
16 }
17 }
18
19 pub fn new(error: ContractError) -> Self {
21 Self {
22 error,
23 account: None,
24 }
25 }
26
27 pub fn encode(&self) -> u32 {
57 let error_code = self.error as u32;
58 let account_code = self.account.map_or(0, |a| a as u32) << 16;
59 let sign_bit = 1 << 31;
60 let account_related_bit = if self.account.is_some() { 1 << 30 } else { 0 };
61 sign_bit | account_related_bit | account_code | error_code
62 }
63
64 pub fn from_code(code: u32) -> Self {
65 let error_code = (code & 0xFFFF) as u16;
66 let account_code = ((code >> 16) & 0xFF) as u8;
67 let has_account = (code >> 30) & 1 == 1;
68
69 let error = ContractError::from_u16(error_code);
70
71 let account = if has_account {
72 Some(AccountTag::from_u8(account_code))
73 } else {
74 None
75 };
76 Self {
77 error,
78 account,
79 }
80 }
81}
82
83impl std::fmt::Display for DvlError {
84 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85 match self.account {
86 Some(account) => write!(f, "Error: {}, Account: {:?}", self.error, account),
87 None => write!(f, "Error: {}", self.error),
88 }
89 }
90}
91
92impl std::fmt::Debug for DvlError {
93 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
94 write!(f, "DvlError {{ error: {}, account: ", self.error)?;
95 match self.account {
96 Some(account) => write!(f, "{:?}", account),
97 None => write!(f, "None"),
98 }?;
99 write!(f, " }}")
100 }
101}
102
103impl std::error::Error for DvlError {}
104
105#[cfg(test)]
106mod tests {
107 use crate::dvl_error::DvlError;
108 use super::*;
109
110 #[test]
111 fn test_sets_bits_correctly_1() {
112 for error in [
113 ContractError::NoError,
114 ].iter() {
115 for account in [
116 AccountTag::Root,
117 AccountTag::Mints,
118 ].iter() {
120 let error_code = DvlError::new_with_account(*account, *error).encode();
121
122 assert_eq!(error_code >> 31, 1, "The most significant bit should always be set.");
123 assert_eq!((error_code >> 30) & 1, 1, "The second most significant bit should be set for account-specific errors.");
124 assert_eq!(error_code & (0xFF << 16), (*account as u32) << 16, "The account code bits 24-16 should correctly represent the account.");
125 assert_eq!(error_code & 0xFFFF, *error as u16 as u32, "The least significant 16 bits should correctly represent the error code.");
126 }
127 }
128 }
129
130 #[test]
131 fn test_sets_bits_correctly_2() {
132 for error in [
133 ContractError::NoError,
134 ].iter() {
135 let error_code = DvlError::new(*error).encode();
136
137 assert_eq!(error_code >> 31, 1, "The most significant bit should always be set.");
138 assert_eq!((error_code >> 30) & 1, 0, "The second most significant bit should not be set for non-account-specific errors.");
139 assert_eq!(error_code & (0xFF << 16), 0, "The account code bits 24-16 should be unset for non-account-specific errors.");
140 assert_eq!(error_code & 0xFFFF, *error as u16 as u32, "The least significant 16 bits should correctly represent the error code.");
141 }
142 }
143
144 #[test]
145 fn test_decode_error() {
146 let error_code = DvlError::new_with_account(AccountTag::AllWorkers, ContractError::AccountOwner).encode();
147 let dvl_error = DvlError::from_code(error_code);
148 assert_eq!(dvl_error.error, ContractError::AccountOwner);
149 assert_eq!(dvl_error.account, Some(AccountTag::AllWorkers));
150 }
151}