1use rand::distributions::Alphanumeric;
9use rand::Rng;
10
11#[derive(Debug, Clone)]
12pub struct Amount(pub u64);
13
14impl Amount {
15 pub fn split(&self) -> SplitAmount {
16 split_amount(self.0).into()
17 }
18}
19
20#[derive(Debug, Clone)]
21pub struct SplitAmount(Vec<u64>);
22
23impl From<Vec<u64>> for SplitAmount {
24 fn from(from: Vec<u64>) -> Self {
25 Self(from)
26 }
27}
28
29impl SplitAmount {
30 pub fn create_secrets(&self) -> Vec<String> {
31 (0..self.0.len())
32 .map(|_| generate_random_string())
33 .collect::<Vec<String>>()
34 }
35
36 pub fn len(&self) -> usize {
37 self.0.len()
38 }
39
40 pub fn is_empty(&self) -> bool {
41 self.0.is_empty()
42 }
43}
44
45impl From<u64> for Amount {
46 fn from(amount: u64) -> Self {
47 Self(amount)
48 }
49}
50
51impl IntoIterator for SplitAmount {
52 type Item = u64;
53 type IntoIter = std::vec::IntoIter<Self::Item>;
54
55 fn into_iter(self) -> Self::IntoIter {
56 self.0.into_iter()
57 }
58}
59
60fn split_amount(amount: u64) -> Vec<u64> {
62 format!("{amount:b}")
63 .chars()
64 .rev()
65 .enumerate()
66 .filter_map(|(i, c)| {
67 if c == '1' {
68 return Some(2_u64.pow(i as u32));
69 }
70 None
71 })
72 .collect::<Vec<u64>>()
73}
74
75pub fn generate_random_string() -> String {
76 rand::thread_rng()
77 .sample_iter(&Alphanumeric)
78 .take(24)
79 .map(char::from)
80 .collect()
81}
82
83#[cfg(test)]
84mod tests {
85 use crate::amount::SplitAmount;
86 use pretty_assertions::assert_eq;
87
88 #[test]
89 fn test_split_amount() -> anyhow::Result<()> {
90 let bits = super::split_amount(13);
91 assert_eq!(bits, vec![1, 4, 8]);
92
93 let bits = super::split_amount(63);
94 assert_eq!(bits, vec![1, 2, 4, 8, 16, 32]);
95
96 let bits = super::split_amount(64);
97 assert_eq!(bits, vec![64]);
98 Ok(())
99 }
100
101 #[test]
102 fn test_create_secrets() {
103 let amounts = vec![1, 2, 3, 4, 5, 6, 7];
104 let secrets = SplitAmount::from(amounts.clone()).create_secrets();
105 assert!(secrets.len() == amounts.len());
106 assert_eq!(secrets.first().unwrap().len(), 24);
107 }
108}