1use bpstd::psbt::Utxo;
26use bpstd::{Idx, Keychain, NormalIndex, Outpoint, Sats, Terminal};
27use indexmap::IndexMap;
28
29#[allow(clippy::len_without_is_empty)]
30pub trait UtxoSet {
31 fn len(&self) -> usize;
32 fn has(&self, outpoint: Outpoint) -> bool;
33 fn get(&self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
34
35 #[cfg(not(feature = "async"))]
36 fn insert(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal);
37 #[cfg(feature = "async")]
38 async fn insert_async(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal);
39
40 #[cfg(not(feature = "async"))]
41 fn insert_all(&mut self, utxos: impl IntoIterator<Item = Utxo>);
42 #[cfg(feature = "async")]
43 async fn insert_all_async(&mut self, utxos: impl IntoIterator<Item = Utxo>);
44
45 #[cfg(not(feature = "async"))]
46 fn clear(&mut self);
47 #[cfg(feature = "async")]
48 async fn clear_async(&mut self);
49
50 #[cfg(not(feature = "async"))]
51 fn remove(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
52 #[cfg(feature = "async")]
53 async fn remove_async(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)>;
54
55 #[cfg(not(feature = "async"))]
56 fn remove_all(&mut self, outpoints: impl IntoIterator<Item = Outpoint>);
57 #[cfg(feature = "async")]
58 async fn remove_all_async(&mut self, outpoints: impl IntoIterator<Item = Outpoint>);
59
60 fn outpoints(&self) -> impl Iterator<Item = Outpoint>;
61
62 fn next_index_noshift(&self, keychain: impl Into<Keychain>) -> NormalIndex;
63
64 #[cfg(not(feature = "async"))]
65 fn next_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex;
66 #[cfg(feature = "async")]
67 async fn next_index_async(&mut self, keychain: impl Into<Keychain>, shift: bool)
68 -> NormalIndex;
69}
70
71#[derive(Clone, PartialEq, Eq, Debug, Default)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
73pub struct MemUtxos {
74 set: IndexMap<Outpoint, (Sats, Terminal)>,
75 next_index: IndexMap<Keychain, NormalIndex>,
76}
77
78impl UtxoSet for MemUtxos {
79 #[inline]
80 fn len(&self) -> usize { self.set.len() }
81 #[inline]
82 fn has(&self, outpoint: Outpoint) -> bool { self.set.contains_key(&outpoint) }
83 #[inline]
84 fn get(&self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
85 self.set.get(&outpoint).copied()
86 }
87
88 #[inline]
89 #[cfg(not(feature = "async"))]
90 fn insert(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal) {
91 self.set.insert(outpoint, (value, terminal));
92 }
93 #[inline]
94 #[cfg(feature = "async")]
95 async fn insert_async(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal) {
96 self.set.insert(outpoint, (value, terminal));
97 }
98
99 #[inline]
100 #[cfg(not(feature = "async"))]
101 fn insert_all(&mut self, utxos: impl IntoIterator<Item = Utxo>) {
102 for utxo in utxos {
103 self.set.insert(utxo.outpoint, (utxo.value, utxo.terminal));
104 }
105 }
106 #[inline]
107 #[cfg(feature = "async")]
108 async fn insert_all_async(&mut self, utxos: impl IntoIterator<Item = Utxo>) {
109 for utxo in utxos {
110 self.set.insert(utxo.outpoint, (utxo.value, utxo.terminal));
111 }
112 }
113
114 #[inline]
115 #[cfg(not(feature = "async"))]
116 fn clear(&mut self) { self.set.clear() }
117 #[inline]
118 #[cfg(feature = "async")]
119 async fn clear_async(&mut self) { self.set.clear() }
120
121 #[inline]
122 #[cfg(not(feature = "async"))]
123 fn remove(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
124 self.set.shift_remove(&outpoint)
125 }
126 #[inline]
127 #[cfg(feature = "async")]
128 async fn remove_async(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)> {
129 self.set.shift_remove(&outpoint)
130 }
131
132 #[inline]
133 #[cfg(not(feature = "async"))]
134 fn remove_all(&mut self, outpoints: impl IntoIterator<Item = Outpoint>) {
135 for outpoint in outpoints {
136 self.set.shift_remove(&outpoint);
137 }
138 }
139 #[inline]
140 #[cfg(feature = "async")]
141 async fn remove_all_async(&mut self, outpoints: impl IntoIterator<Item = Outpoint>) {
142 for outpoint in outpoints {
143 self.set.shift_remove(&outpoint);
144 }
145 }
146
147 #[inline]
148 fn outpoints(&self) -> impl Iterator<Item = Outpoint> { self.set.keys().copied() }
149
150 fn next_index_noshift(&self, keychain: impl Into<Keychain>) -> NormalIndex {
151 self.next_index
152 .get(&keychain.into())
153 .copied()
154 .unwrap_or_default()
155 }
156
157 #[cfg(not(feature = "async"))]
158 fn next_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex {
159 let index = self.next_index.entry(keychain.into()).or_default();
160 let next = *index;
161 if shift {
162 index.saturating_inc_assign();
163 }
164 next
165 }
166 #[inline]
167 #[cfg(feature = "async")]
168 async fn next_index_async(
169 &mut self,
170 keychain: impl Into<Keychain>,
171 shift: bool,
172 ) -> NormalIndex {
173 let index = self.next_index.entry(keychain.into()).or_default();
174 let next = *index;
175 if shift {
176 index.saturating_inc_assign();
177 }
178 next
179 }
180}