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