b3_users/data/
chain.rs

1// chain.rs
2use super::transaction::UserTransactionData;
3use crate::error::UserStateError;
4use ic_cdk::export::{candid::CandidType, serde::Deserialize};
5
6#[derive(Debug, CandidType, Deserialize, Clone, PartialEq)]
7pub struct UserChainData {
8    pub nonce: u64,
9    pub transactions: Vec<UserTransactionData>,
10}
11
12impl UserChainData {
13    /// Creates a default ChainData with nonce 0 and an empty transactions vector.
14    pub fn default() -> Self {
15        UserChainData {
16            nonce: 0,
17            transactions: vec![],
18        }
19    }
20
21    pub fn new(transaction: UserTransactionData) -> Self {
22        UserChainData {
23            nonce: 0,
24            transactions: vec![transaction],
25        }
26    }
27
28    /// Adds a new transaction to the chain and updates the nonce.
29    pub fn add(&mut self, nonce: u64, transaction: UserTransactionData) {
30        self.nonce = nonce;
31        self.transactions.push(transaction);
32    }
33
34    /// Removes a transaction from the specified chain and updates the ChainData.
35    /// Returns an error if the chain_id is not found.
36    /// Returns an error if the transaction index is not found.
37    pub fn remove(&mut self, index: usize) -> Result<&UserTransactionData, UserStateError> {
38        if index >= self.transactions.len() {
39            return Err(UserStateError::TransactionNotFound);
40        }
41
42        self.transactions.remove(index);
43
44        self.get_transaction(index)
45    }
46
47    /// Returns a reference to the transaction at the specified index.
48    pub fn get_transaction(&self, index: usize) -> Result<&UserTransactionData, UserStateError> {
49        self.transactions
50            .get(index)
51            .ok_or(UserStateError::TransactionNotFound)
52    }
53
54    pub fn get_transactions(&self) -> &Vec<UserTransactionData> {
55        &self.transactions
56    }
57
58    /// Adds a transaction to the specified chain and updates the ChainData.
59    /// Returns an error if the chain_id is not found.
60    pub fn add_transaction(
61        &mut self,
62        nonce: u64,
63        transaction: UserTransactionData,
64    ) -> Result<&UserTransactionData, UserStateError> {
65        self.add(nonce, transaction);
66
67        self.get_transaction(self.transactions.len() - 1)
68    }
69
70    /// Clears the transactions vector for the specified chain.
71    /// Returns an error if the chain_id is not found.
72    pub fn clear_transactions(&mut self) {
73        self.transactions.clear();
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use proptest::prelude::*;
81
82    proptest! {
83        #[test]
84        fn test_add_transaction(nonce: u64, transaction: UserTransactionData) {
85            let mut chain_data = UserChainData::default();
86
87            chain_data.add(nonce, transaction.clone());
88
89            assert_eq!(chain_data.nonce, nonce);
90            assert_eq!(chain_data.transactions, vec![transaction]);
91        }
92
93        #[test]
94        fn test_clear_transactions(transaction: Vec<UserTransactionData>) {
95            let mut chain_data = UserChainData::default();
96
97            chain_data.transactions = transaction.clone();
98
99            chain_data.clear_transactions();
100
101            assert_eq!(chain_data.nonce, 0);
102
103            assert_eq!(chain_data.transactions, vec![]);
104        }
105
106        #[test]
107        fn test_get_transaction(transaction: Vec<UserTransactionData>) {
108            let mut chain_data = UserChainData::default();
109
110            chain_data.transactions = transaction.clone();
111
112            for i in 0..chain_data.transactions.len() {
113                assert_eq!(chain_data.get_transaction(i).unwrap(), &transaction[i]);
114            }
115        }
116
117        #[test]
118        fn test_get_transactions(transaction: Vec<UserTransactionData>) {
119            let mut chain_data = UserChainData::default();
120
121            chain_data.transactions = transaction.clone();
122
123            assert_eq!(chain_data.get_transactions(), &transaction);
124        }
125
126        #[test]
127        fn test_add_transaction_error(transaction: Vec<UserTransactionData>) {
128            let mut chain_data = UserChainData::default();
129
130            chain_data.transactions = transaction.clone();
131
132            let result = chain_data.get_transaction(chain_data.transactions.len());
133
134            match result {
135                Err(UserStateError::TransactionNotFound) => (),
136                _ => panic!("Expected TransactionNotFound error"),
137            }
138        }
139
140        #[test]
141        fn test_add_transaction_to_chain(nonce: u64, transaction: UserTransactionData) {
142            let mut chain_data = UserChainData::default();
143
144            chain_data.add_transaction(nonce, transaction.clone()).unwrap();
145
146            assert_eq!(chain_data.nonce, nonce);
147            assert_eq!(chain_data.transactions, vec![transaction]);
148        }
149
150        #[test]
151        fn test_add_transaction_to_chain_error(nonce: u64, transaction: UserTransactionData) {
152            let mut chain_data = UserChainData::default();
153
154            chain_data.add_transaction(nonce, transaction.clone()).unwrap();
155
156            let result = chain_data.get_transaction(chain_data.transactions.len());
157
158            match result {
159                Err(UserStateError::TransactionNotFound) => (),
160                _ => panic!("Expected TransactionNotFound error"),
161            }
162        }
163
164        #[test]
165        fn test_clear_transactions_error(transaction: Vec<UserTransactionData>) {
166            let mut chain_data = UserChainData::default();
167
168            chain_data.transactions = transaction.clone();
169
170            chain_data.clear_transactions();
171
172            let result = chain_data.get_transaction(chain_data.transactions.len());
173
174            match result {
175                Err(UserStateError::TransactionNotFound) => (),
176                _ => panic!("Expected TransactionNotFound error"),
177            }
178        }
179    }
180}