1use crate::drive::credit_pools::epochs;
7use crate::drive::identity::IdentityRootStructure;
8use crate::drive::{credit_pools, tokens, RootTree};
9use crate::util::batch::grovedb_op_batch::KnownPath::{
10 TokenBalancesRoot, TokenContractInfoRoot, TokenDirectSellPriceRoot, TokenDistributionRoot,
11 TokenIdentityInfoRoot, TokenOncePerIdentityDistributionRoot, TokenPerpetualDistributionRoot,
12 TokenPreProgrammedDistributionRoot, TokenStatusRoot, TokenTimedDistributionRoot,
13};
14use crate::util::storage_flags::StorageFlags;
15use dpp::block::epoch::Epoch;
16use dpp::identity::{Purpose, SecurityLevel};
17use dpp::prelude::Identifier;
18use grovedb::batch::key_info::KeyInfo;
19use grovedb::batch::{
20 GroveDbOpConsistencyResults, GroveOp, KeyInfoPath, QualifiedGroveDbOp,
21 SubelementsDeletionBehavior,
22};
23use grovedb::operations::proof::util::hex_to_ascii;
24use grovedb::{Element, TreeType};
25use std::borrow::Cow;
26use std::fmt;
27
28#[derive(Debug, Default, Clone)]
31pub struct GroveDbOpBatch {
32 pub(crate) operations: Vec<QualifiedGroveDbOp>,
34}
35
36#[derive(Debug, PartialEq, Eq, Copy, Clone)]
37enum KnownPath {
38 Root, DataContractAndDocumentsRoot, DataContractStorage, DocumentsRoot, IdentitiesRoot, IdentityTreeRevisionRoot, IdentityTreeNonceRoot, IdentityTreeKeysRoot, IdentityTreeKeyReferencesRoot, IdentityTreeKeyReferencesInPurpose(Purpose), IdentityTreeKeyReferencesInSecurityLevel(Purpose, SecurityLevel), IdentityTreeNegativeCreditRoot, IdentityContractInfoRoot, IdentityTreeKeyBudgetsRoot, UniquePublicKeyHashesToIdentitiesRoot, NonUniquePublicKeyKeyHashesToIdentitiesRoot, PoolsRoot, PoolsInsideEpoch(Epoch), PreFundedSpecializedBalancesRoot, SavedBlockTransactionsRoot, SpentAssetLockTransactionsRoot, MiscRoot, WithdrawalTransactionsRoot, BalancesRoot, TokenRoot, TokenBalancesRoot, TokenDistributionRoot, TokenDirectSellPriceRoot, TokenTimedDistributionRoot, TokenPreProgrammedDistributionRoot, TokenPerpetualDistributionRoot, TokenOncePerIdentityDistributionRoot, TokenIdentityInfoRoot, TokenContractInfoRoot, TokenStatusRoot, VersionsRoot, VotesRoot, GroupActionsRoot, SingleUseKeyBalancesRoot, ShieldedBalancesRoot, ContractGroupsRoot, }
80
81impl From<RootTree> for KnownPath {
82 fn from(value: RootTree) -> Self {
83 match value {
84 RootTree::DataContractDocuments => KnownPath::DataContractAndDocumentsRoot,
85 RootTree::Identities => KnownPath::IdentitiesRoot,
86 RootTree::UniquePublicKeyHashesToIdentities => {
87 KnownPath::UniquePublicKeyHashesToIdentitiesRoot
88 }
89 RootTree::NonUniquePublicKeyKeyHashesToIdentities => {
90 KnownPath::NonUniquePublicKeyKeyHashesToIdentitiesRoot
91 }
92 RootTree::Pools => KnownPath::PoolsRoot,
93 RootTree::PreFundedSpecializedBalances => KnownPath::PreFundedSpecializedBalancesRoot,
94 RootTree::SavedBlockTransactions => KnownPath::SavedBlockTransactionsRoot,
95 RootTree::SpentAssetLockTransactions => KnownPath::SpentAssetLockTransactionsRoot,
96 RootTree::Misc => KnownPath::MiscRoot,
97 RootTree::WithdrawalTransactions => KnownPath::WithdrawalTransactionsRoot,
98 RootTree::Balances => KnownPath::BalancesRoot,
99 RootTree::Tokens => KnownPath::TokenRoot,
100 RootTree::Versions => KnownPath::VersionsRoot,
101 RootTree::Votes => KnownPath::VotesRoot,
102 RootTree::GroupActions => KnownPath::GroupActionsRoot,
103 RootTree::AddressBalances => KnownPath::SingleUseKeyBalancesRoot,
104 RootTree::ShieldedBalances => KnownPath::ShieldedBalancesRoot,
105 RootTree::ContractGroups => KnownPath::ContractGroupsRoot,
106 }
107 }
108}
109
110impl From<IdentityRootStructure> for KnownPath {
111 fn from(value: IdentityRootStructure) -> Self {
112 match value {
113 IdentityRootStructure::IdentityTreeRevision => KnownPath::IdentityTreeRevisionRoot,
114 IdentityRootStructure::IdentityTreeNonce => KnownPath::IdentityTreeNonceRoot,
115 IdentityRootStructure::IdentityTreeKeys => KnownPath::IdentityTreeKeysRoot,
116 IdentityRootStructure::IdentityTreeKeyReferences => {
117 KnownPath::IdentityTreeKeyReferencesRoot
118 }
119 IdentityRootStructure::IdentityTreeNegativeCredit => {
120 KnownPath::IdentityTreeNegativeCreditRoot
121 }
122 IdentityRootStructure::IdentityContractInfo => KnownPath::IdentityContractInfoRoot,
123 IdentityRootStructure::IdentityTreeKeyBudgets => KnownPath::IdentityTreeKeyBudgetsRoot,
124 }
125 }
126}
127
128fn readable_key_info(known_path: KnownPath, key_info: &KeyInfo) -> (String, Option<KnownPath>) {
129 match key_info {
130 KeyInfo::KnownKey(key) => {
131 match known_path {
132 KnownPath::Root => {
133 if let Ok(root_tree) = RootTree::try_from(key[0]) {
134 (
135 format!("{}({})", root_tree, key[0]),
136 Some(root_tree.into()),
137 )
138 } else {
139 (hex_to_ascii(key), None)
140 }
141 }
142 KnownPath::BalancesRoot | KnownPath::IdentitiesRoot if key.len() == 32 => (
143 format!(
144 "IdentityId(bs58::{})",
145 Identifier::from_vec(key.clone()).unwrap()
146 ),
147 None,
148 ),
149 KnownPath::DataContractAndDocumentsRoot if key.len() == 32 => (
150 format!(
151 "ContractId(bs58::{})",
152 Identifier::from_vec(key.clone()).unwrap()
153 ),
154 None,
155 ),
156 KnownPath::DataContractAndDocumentsRoot if key.len() == 1 => match key[0] {
157 0 => (
158 "DataContractStorage(0)".to_string(),
159 Some(KnownPath::DataContractStorage),
160 ),
161 1 => (
162 "DataContractDocuments(1)".to_string(),
163 Some(KnownPath::DocumentsRoot),
164 ),
165 _ => (hex_to_ascii(key), None),
166 },
167 KnownPath::IdentitiesRoot if key.len() == 1 => {
168 if let Ok(root_tree) = IdentityRootStructure::try_from(key[0]) {
169 (
170 format!("{}({})", root_tree, key[0]),
171 Some(root_tree.into()),
172 )
173 } else {
174 (hex_to_ascii(key), None)
175 }
176 }
177 KnownPath::IdentityTreeKeyReferencesRoot if key.len() == 1 => {
178 if let Ok(purpose) = Purpose::try_from(key[0]) {
179 (
180 format!("Purpose::{}({})", purpose, key[0]),
181 Some(KnownPath::IdentityTreeKeyReferencesInPurpose(purpose)),
182 )
183 } else {
184 (hex_to_ascii(key), None)
185 }
186 }
187 KnownPath::IdentityTreeKeyReferencesInPurpose(purpose) if key.len() == 1 => {
188 if let Ok(security_level) = SecurityLevel::try_from(key[0]) {
189 (
190 format!("SecurityLevel::{}({})", security_level, key[0]),
191 Some(KnownPath::IdentityTreeKeyReferencesInSecurityLevel(
192 purpose,
193 security_level,
194 )),
195 )
196 } else {
197 (hex_to_ascii(key), None)
198 }
199 }
200
201 KnownPath::PoolsRoot if key.len() == 1 => match key[0] {
202 epochs::epochs_root_tree_key_constants::KEY_STORAGE_FEE_POOL_U8 => {
203 ("StorageFeePool(ascii:'s')".to_string(), None)
204 }
205 epochs::epochs_root_tree_key_constants::KEY_UNPAID_EPOCH_INDEX_U8 => {
206 ("UnpaidEpochIndex(ascii:'u')".to_string(), None)
207 }
208 epochs::epochs_root_tree_key_constants::KEY_PENDING_EPOCH_REFUNDS_U8 => {
209 ("PendingEpochRefunds(ascii:'p')".to_string(), None)
210 }
211 _ => (hex_to_ascii(key), None),
212 },
213 KnownPath::PoolsRoot if key.len() == 2 => {
214 if let Ok(epoch) = Epoch::try_from(key) {
216 (
217 format!("Epoch::{}({})", epoch.index, hex::encode(key)),
218 Some(KnownPath::PoolsInsideEpoch(epoch)),
219 )
220 } else {
221 (hex_to_ascii(key), None)
222 }
223 }
224 KnownPath::PoolsInsideEpoch(_) if key.len() == 1 => {
225 match key[0] {
227 credit_pools::epochs::epoch_key_constants::KEY_POOL_PROCESSING_FEES_U8 => {
228 ("PoolProcessingFees(ascii:'p')".to_string(), None)
229 }
230 credit_pools::epochs::epoch_key_constants::KEY_POOL_STORAGE_FEES_U8 => {
231 ("PoolStorageFees(ascii:'s')".to_string(), None)
232 }
233 credit_pools::epochs::epoch_key_constants::KEY_START_TIME_U8 => {
234 ("StartTime(ascii:'t')".to_string(), None)
235 }
236 credit_pools::epochs::epoch_key_constants::KEY_PROTOCOL_VERSION_U8 => {
237 ("ProtocolVersion(ascii:'v')".to_string(), None)
238 }
239 credit_pools::epochs::epoch_key_constants::KEY_START_BLOCK_HEIGHT_U8 => {
240 ("StartBlockHeight(ascii:'h')".to_string(), None)
241 }
242 credit_pools::epochs::epoch_key_constants::KEY_START_BLOCK_CORE_HEIGHT_U8 => {
243 ("StartBlockCoreHeight(ascii:'c')".to_string(), None)
244 }
245 credit_pools::epochs::epoch_key_constants::KEY_PROPOSERS_U8 => {
246 ("Proposers(ascii:'m')".to_string(), None)
247 }
248 credit_pools::epochs::epoch_key_constants::KEY_FEE_MULTIPLIER_U8 => {
249 ("FeeMultiplier(ascii:'x')".to_string(), None)
250 }
251 _ => (hex_to_ascii(key), None),
252 }
253 }
254 KnownPath::TokenRoot if key.len() == 1 => match key[0] {
255 tokens::paths::TOKEN_DISTRIBUTIONS_KEY => {
256 (format!("Distribution({})", tokens::paths::TOKEN_DISTRIBUTIONS_KEY), Some(TokenDistributionRoot))
257 }
258 tokens::paths::TOKEN_DIRECT_SELL_PRICE_KEY => {
259 (format!("SellPrice({})", tokens::paths::TOKEN_DIRECT_SELL_PRICE_KEY), Some(TokenDirectSellPriceRoot))
260 }
261 tokens::paths::TOKEN_BALANCES_KEY => {
262 (format!("Balances({})", tokens::paths::TOKEN_BALANCES_KEY), Some(TokenBalancesRoot))
263 }
264 tokens::paths::TOKEN_IDENTITY_INFO_KEY => {
265 (format!("IdentityInfo({})", tokens::paths::TOKEN_IDENTITY_INFO_KEY), Some(TokenIdentityInfoRoot))
266 }
267 tokens::paths::TOKEN_CONTRACT_INFO_KEY => {
268 (format!("ContractInfo({})", tokens::paths::TOKEN_CONTRACT_INFO_KEY), Some(TokenContractInfoRoot))
269 }
270 tokens::paths::TOKEN_STATUS_INFO_KEY => {
271 (format!("Status({})", tokens::paths::TOKEN_STATUS_INFO_KEY), Some(TokenStatusRoot))
272 }
273 _ => (hex_to_ascii(key), None),
274 },
275 KnownPath::TokenDistributionRoot if key.len() == 1 => match key[0] {
276 tokens::paths::TOKEN_TIMED_DISTRIBUTIONS_KEY => {
277 (format!("TimedDistribution({})", tokens::paths::TOKEN_TIMED_DISTRIBUTIONS_KEY), Some(TokenTimedDistributionRoot))
278 }
279 tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_KEY => {
280 (format!("PerpetualDistribution({})", tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_KEY), Some(TokenPerpetualDistributionRoot))
281 }
282 tokens::paths::TOKEN_PRE_PROGRAMMED_DISTRIBUTIONS_KEY => {
283 (format!("PreProgrammedDistribution({})", tokens::paths::TOKEN_PRE_PROGRAMMED_DISTRIBUTIONS_KEY), Some(TokenPreProgrammedDistributionRoot))
284 }
285 tokens::paths::TOKEN_ONCE_PER_IDENTITY_DISTRIBUTIONS_KEY => {
286 (format!("OncePerIdentityDistribution({})", tokens::paths::TOKEN_ONCE_PER_IDENTITY_DISTRIBUTIONS_KEY), Some(TokenOncePerIdentityDistributionRoot))
287 }
288 _ => (hex_to_ascii(key), None),
289 },
290 KnownPath::TokenTimedDistributionRoot if key.len() == 1 => match key[0] {
291 tokens::paths::TOKEN_MS_TIMED_DISTRIBUTIONS_KEY => {
292 (format!("MillisecondTimedDistribution({})", tokens::paths::TOKEN_MS_TIMED_DISTRIBUTIONS_KEY), None)
293 }
294 tokens::paths::TOKEN_BLOCK_TIMED_DISTRIBUTIONS_KEY => {
295 (format!("BlockTimedDistribution({})", tokens::paths::TOKEN_BLOCK_TIMED_DISTRIBUTIONS_KEY), None)
296 }
297 tokens::paths::TOKEN_EPOCH_TIMED_DISTRIBUTIONS_KEY => {
298 (format!("EpochTimedDistribution({})", tokens::paths::TOKEN_EPOCH_TIMED_DISTRIBUTIONS_KEY), None)
299 }
300 _ => (hex_to_ascii(key), None),
301 },
302 KnownPath::TokenPerpetualDistributionRoot if key.len() == 1 => match key[0] {
303 tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_INFO_KEY => {
304 (format!("PerpetualDistributionInfo({})", tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_INFO_KEY), None)
305 }
306 tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_FOR_IDENTITIES_LAST_CLAIM_KEY => {
307 (format!("PerpetualDistributionLastClaim({})", tokens::paths::TOKEN_PERPETUAL_DISTRIBUTIONS_FOR_IDENTITIES_LAST_CLAIM_KEY), None)
308 }
309 _ => (hex_to_ascii(key), None),
310 },
311 _ => (hex_to_ascii(key), None),
312 }
313 }
314 KeyInfo::MaxKeySize {
315 unique_id,
316 max_size,
317 } => (
318 format!(
319 "MaxKeySize(unique_id: {:?}, max_size: {})",
320 unique_id, max_size
321 ),
322 None,
323 ),
324 }
325}
326
327fn readable_path(path: &KeyInfoPath) -> (String, KnownPath) {
328 let mut known_path = KnownPath::Root;
329 let string = path
330 .0
331 .iter()
332 .map(|key_info| {
333 let (string, new_known_path) = readable_key_info(known_path, key_info);
334 if let Some(new_known_path) = new_known_path {
335 known_path = new_known_path;
336 }
337 string
338 })
339 .collect::<Vec<_>>()
340 .join("/");
341 (string, known_path)
342}
343
344impl fmt::Display for GroveDbOpBatch {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 for op in &self.operations {
347 let (path_string, known_path) = readable_path(&op.path);
348 let (key_string, _) = if let Some(ref key) = op.key {
349 readable_key_info(known_path, key)
350 } else {
351 ("(none)".to_string(), None)
352 };
353 writeln!(f, "{{")?;
354 writeln!(f, " Path: {}", path_string)?;
355 writeln!(f, " Key: {}", key_string)?;
356 match &op.op {
357 GroveOp::InsertOrReplace { element }
358 | GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { element }
359 | GroveOp::InsertWithKnownToNotAlreadyExist { element }
360 | GroveOp::InsertIfNotExists { element, .. } => {
361 let flags = element.get_flags();
362 let flag_info = match flags {
363 None => "No Flags".to_string(),
364 Some(flags) => format!("Flags are 0x{}", hex::encode(flags)),
365 };
366 match element {
367 Element::Item(data, _) => {
368 let num = match data.len() {
369 8 => format!(
370 " u64({})",
371 u64::from_be_bytes(data.clone().try_into().unwrap())
372 ),
373 4 => format!(
374 " u32({})",
375 u32::from_be_bytes(data.clone().try_into().unwrap())
376 ),
377 _ => String::new(),
378 };
379 writeln!(
380 f,
381 " Operation: Insert Item with length: {}{} {}",
382 data.len(),
383 num,
384 flag_info
385 )?
386 }
387 Element::Tree(None, _) => {
388 writeln!(f, " Operation: Insert Empty Tree {}", flag_info)?
389 }
390 Element::SumTree(None, _, _) => {
391 writeln!(f, " Operation: Insert Empty Sum Tree {}", flag_info)?
392 }
393 _ => writeln!(f, " Operation: Insert {}", element)?,
394 }
395 }
396 _ => {
397 writeln!(f, " Operation: {:?}", op.op)?;
398 }
399 }
400 writeln!(f, "}}")?;
401 }
402 Ok(())
403 }
404}
405
406pub trait GroveDbOpBatchV0Methods {
408 fn new() -> Self;
410
411 fn len(&self) -> usize;
413
414 fn is_empty(&self) -> bool;
416
417 fn push(&mut self, op: QualifiedGroveDbOp);
419
420 fn append(&mut self, other: &mut Self);
422
423 fn extend<I: IntoIterator<Item = QualifiedGroveDbOp>>(&mut self, other_ops: I);
425
426 fn from_operations(operations: Vec<QualifiedGroveDbOp>) -> Self;
428
429 fn add_insert_empty_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>);
431
432 fn add_insert_empty_tree_with_flags(
434 &mut self,
435 path: Vec<Vec<u8>>,
436 key: Vec<u8>,
437 storage_flags: &Option<Cow<StorageFlags>>,
438 );
439
440 fn add_insert_empty_sum_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>);
442
443 fn add_insert_empty_sum_tree_with_flags(
445 &mut self,
446 path: Vec<Vec<u8>>,
447 key: Vec<u8>,
448 storage_flags: &Option<Cow<StorageFlags>>,
449 );
450
451 fn add_delete(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>);
453
454 fn add_delete_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>, tree_type: TreeType);
456
457 fn add_insert(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>, element: Element);
459
460 fn verify_consistency_of_operations(&self) -> GroveDbOpConsistencyResults;
462
463 fn contains<'c, P>(&self, path: P, key: &[u8]) -> Option<&GroveOp>
474 where
475 P: IntoIterator<Item = &'c [u8]>,
476 <P as IntoIterator>::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone;
477
478 fn remove<'c, P>(&mut self, path: P, key: &[u8]) -> Option<GroveOp>
489 where
490 P: IntoIterator<Item = &'c [u8]>,
491 <P as IntoIterator>::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone;
492
493 fn remove_if_insert(&mut self, path: Vec<Vec<u8>>, key: &[u8]) -> Option<GroveOp>;
506}
507
508impl GroveDbOpBatchV0Methods for GroveDbOpBatch {
509 fn new() -> Self {
511 GroveDbOpBatch {
512 operations: Vec::new(),
513 }
514 }
515
516 fn len(&self) -> usize {
518 self.operations.len()
519 }
520
521 fn is_empty(&self) -> bool {
523 self.operations.is_empty()
524 }
525
526 fn push(&mut self, op: QualifiedGroveDbOp) {
528 self.operations.push(op);
529 }
530
531 fn append(&mut self, other: &mut Self) {
533 self.operations.append(&mut other.operations);
534 }
535
536 fn extend<I: IntoIterator<Item = QualifiedGroveDbOp>>(&mut self, other_ops: I) {
538 self.operations.extend(other_ops);
539 }
540
541 fn from_operations(operations: Vec<QualifiedGroveDbOp>) -> Self {
543 GroveDbOpBatch { operations }
544 }
545
546 fn add_insert_empty_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>) {
548 self.operations.push(
549 QualifiedGroveDbOp::insert_or_replace_op(path, key, Element::empty_tree())
550 .dont_check_for_backwards_references(),
551 )
552 }
553
554 fn add_insert_empty_tree_with_flags(
556 &mut self,
557 path: Vec<Vec<u8>>,
558 key: Vec<u8>,
559 storage_flags: &Option<Cow<StorageFlags>>,
560 ) {
561 self.operations.push(
562 QualifiedGroveDbOp::insert_or_replace_op(
563 path,
564 key,
565 Element::empty_tree_with_flags(
566 StorageFlags::map_borrowed_cow_to_some_element_flags(storage_flags),
567 ),
568 )
569 .dont_check_for_backwards_references(),
570 )
571 }
572
573 fn add_insert_empty_sum_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>) {
575 self.operations.push(
576 QualifiedGroveDbOp::insert_or_replace_op(path, key, Element::empty_sum_tree())
577 .dont_check_for_backwards_references(),
578 )
579 }
580
581 fn add_insert_empty_sum_tree_with_flags(
583 &mut self,
584 path: Vec<Vec<u8>>,
585 key: Vec<u8>,
586 storage_flags: &Option<Cow<StorageFlags>>,
587 ) {
588 self.operations.push(
589 QualifiedGroveDbOp::insert_or_replace_op(
590 path,
591 key,
592 Element::empty_sum_tree_with_flags(
593 StorageFlags::map_borrowed_cow_to_some_element_flags(storage_flags),
594 ),
595 )
596 .dont_check_for_backwards_references(),
597 )
598 }
599
600 fn add_delete(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>) {
602 self.operations
603 .push(QualifiedGroveDbOp::delete_op(path, key).dont_check_for_backwards_references())
604 }
605
606 fn add_delete_tree(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>, tree_type: TreeType) {
610 self.operations.push(
611 QualifiedGroveDbOp::delete_tree_op(
612 path,
613 key,
614 tree_type,
615 SubelementsDeletionBehavior::DontCheckWithNoCleanup,
616 )
617 .dont_check_for_backwards_references(),
618 )
619 }
620
621 fn add_insert(&mut self, path: Vec<Vec<u8>>, key: Vec<u8>, element: Element) {
623 self.operations.push(
624 QualifiedGroveDbOp::insert_or_replace_op(path, key, element)
625 .dont_check_for_backwards_references(),
626 )
627 }
628
629 fn verify_consistency_of_operations(&self) -> GroveDbOpConsistencyResults {
631 QualifiedGroveDbOp::verify_consistency_of_operations(&self.operations)
632 }
633
634 fn contains<'c, P>(&self, path: P, key: &[u8]) -> Option<&GroveOp>
645 where
646 P: IntoIterator<Item = &'c [u8]>,
647 <P as IntoIterator>::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone,
648 {
649 let path = KeyInfoPath(
650 path.into_iter()
651 .map(|item| KeyInfo::KnownKey(item.to_vec()))
652 .collect(),
653 );
654
655 self.operations.iter().find_map(|op| {
656 if op.path == path && op.key == Some(KeyInfo::KnownKey(key.to_vec())) {
657 Some(&op.op)
658 } else {
659 None
660 }
661 })
662 }
663
664 fn remove<'c, P>(&mut self, path: P, key: &[u8]) -> Option<GroveOp>
675 where
676 P: IntoIterator<Item = &'c [u8]>,
677 <P as IntoIterator>::IntoIter: ExactSizeIterator + DoubleEndedIterator + Clone,
678 {
679 let path = KeyInfoPath(
680 path.into_iter()
681 .map(|item| KeyInfo::KnownKey(item.to_vec()))
682 .collect(),
683 );
684
685 if let Some(index) = self
686 .operations
687 .iter()
688 .position(|op| op.path == path && op.key == Some(KeyInfo::KnownKey(key.to_vec())))
689 {
690 Some(self.operations.remove(index).op)
691 } else {
692 None
693 }
694 }
695
696 fn remove_if_insert(&mut self, path: Vec<Vec<u8>>, key: &[u8]) -> Option<GroveOp> {
709 let path = KeyInfoPath(
710 path.into_iter()
711 .map(|item| KeyInfo::KnownKey(item.to_vec()))
712 .collect(),
713 );
714
715 if let Some(index) = self
716 .operations
717 .iter()
718 .position(|op| op.path == path && op.key == Some(KeyInfo::KnownKey(key.to_vec())))
719 {
720 let op = &self.operations[index].op;
721 let op = if matches!(
722 op,
723 &GroveOp::InsertOrReplace { .. }
724 | &GroveOp::InsertOrReplaceDontCheckForBackwardsReferences { .. }
725 | &GroveOp::InsertWithKnownToNotAlreadyExist { .. }
726 | &GroveOp::InsertIfNotExists { .. }
727 | &GroveOp::Replace { .. }
728 | &GroveOp::ReplaceDontCheckForBackwardsReferences { .. }
729 | &GroveOp::Patch { .. }
730 | &GroveOp::PatchDontCheckForBackwardsReferences { .. }
731 ) {
732 self.operations.remove(index).op
733 } else {
734 op.clone()
735 };
736 Some(op)
737 } else {
738 None
739 }
740 }
741}
742
743impl IntoIterator for GroveDbOpBatch {
744 type Item = QualifiedGroveDbOp;
745 type IntoIter = std::vec::IntoIter<QualifiedGroveDbOp>;
746
747 fn into_iter(self) -> Self::IntoIter {
748 self.operations.into_iter()
749 }
750}