1use alloc::{
18 boxed::Box,
19 collections::{
20 btree_map::{self, BTreeMap},
21 btree_set::BTreeSet,
22 },
23 vec::Vec,
24};
25use core::{fmt::Formatter, mem};
26use frame_support::traits::tokens::imbalance::ImbalanceAccounting;
27use xcm::latest::{
28 Asset, AssetFilter, AssetId, AssetInstance, Assets,
29 Fungibility::{Fungible, NonFungible},
30 InteriorLocation, Location, Reanchorable,
31 WildAsset::{All, AllCounted, AllOf, AllOfCounted},
32 WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible},
33};
34
35#[derive(Debug)]
37pub enum TakeError {
38 AssetUnderflow(Asset),
40}
41
42pub struct BackupAssetsInHolding {
47 inner: AssetsInHolding,
49}
50
51impl BackupAssetsInHolding {
52 pub fn safe_backup(other: &AssetsInHolding) -> Self {
55 Self {
56 inner: AssetsInHolding {
57 fungible: other
58 .fungible
59 .iter()
60 .map(|(id, accounting)| (id.clone(), accounting.unsafe_clone()))
61 .collect(),
62 non_fungible: other.non_fungible.clone(),
63 },
64 }
65 }
66
67 pub fn restore_into(&mut self, target: &mut AssetsInHolding) {
70 core::mem::swap(target, &mut self.inner);
71 }
72
73 pub fn safe_drop(&mut self) {
76 self.inner.fungible.iter_mut().for_each(|(_, accounting)| {
78 accounting.forget_imbalance();
79 });
80 }
81}
82
83impl Drop for BackupAssetsInHolding {
84 fn drop(&mut self) {
85 self.safe_drop();
86 }
87}
88
89pub struct AssetsInHolding {
91 pub fungible: BTreeMap<AssetId, Box<dyn ImbalanceAccounting<u128>>>,
93 pub non_fungible: BTreeSet<(AssetId, AssetInstance)>,
97}
98
99impl PartialEq for AssetsInHolding {
100 fn eq(&self, other: &Self) -> bool {
101 if self.non_fungible != other.non_fungible {
102 return false;
103 }
104 if self.fungible.len() != other.fungible.len() {
105 return false;
106 }
107 if !self
108 .fungible
109 .iter()
110 .zip(other.fungible.iter())
111 .all(|(left, right)| left.0 == right.0 && left.1.amount() == right.1.amount())
112 {
113 return false;
114 }
115 true
116 }
117}
118
119impl core::fmt::Debug for AssetsInHolding {
120 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
121 let fungibles: BTreeMap<&AssetId, u128> =
122 self.fungible.iter().map(|(id, accounting)| (id, accounting.amount())).collect();
123 f.debug_struct("AssetsInHolding")
124 .field("fungible", &fungibles)
125 .field("non_fungible", &self.non_fungible)
126 .finish()
127 }
128}
129
130impl AssetsInHolding {
131 pub fn new() -> Self {
133 AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() }
134 }
135
136 pub fn new_from_fungible_credit(
138 asset: AssetId,
139 credit: Box<dyn ImbalanceAccounting<u128>>,
140 ) -> Self {
141 let mut new = AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() };
142 new.fungible.insert(asset, credit);
143 new
144 }
145
146 pub fn new_from_non_fungible(class: AssetId, instance: AssetInstance) -> Self {
148 let mut new = AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() };
149 new.non_fungible.insert((class, instance));
150 new
151 }
152
153 pub fn len(&self) -> usize {
155 self.fungible.len() + self.non_fungible.len()
156 }
157
158 pub fn is_empty(&self) -> bool {
160 self.fungible.is_empty() && self.non_fungible.is_empty()
161 }
162
163 pub fn fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
165 self.fungible
166 .iter()
167 .map(|(id, accounting)| Asset { fun: Fungible(accounting.amount()), id: id.clone() })
168 }
169
170 pub fn non_fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
172 self.non_fungible
173 .iter()
174 .map(|(id, instance)| Asset { fun: NonFungible(*instance), id: id.clone() })
175 }
176
177 pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> {
179 self.fungible
180 .into_iter()
181 .map(|(id, accounting)| Asset { fun: Fungible(accounting.amount()), id })
182 .chain(
183 self.non_fungible
184 .into_iter()
185 .map(|(id, instance)| Asset { fun: NonFungible(instance), id }),
186 )
187 }
188
189 pub fn into_per_asset_holdings(self) -> impl Iterator<Item = AssetsInHolding> {
197 let fungibles = self.fungible.into_iter().map(|(asset_id, accounting)| {
198 AssetsInHolding::new_from_fungible_credit(asset_id, accounting)
199 });
200 let non_fungibles = self
201 .non_fungible
202 .into_iter()
203 .map(|(class, instance)| AssetsInHolding::new_from_non_fungible(class, instance));
204 fungibles.chain(non_fungibles)
205 }
206
207 pub fn assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
209 self.fungible_assets_iter().chain(self.non_fungible_assets_iter())
210 }
211
212 pub fn subsume_assets(&mut self, assets: AssetsInHolding) {
216 for (asset_id, accounting) in assets.fungible.into_iter() {
219 match self.fungible.entry(asset_id) {
220 btree_map::Entry::Occupied(mut e) => {
221 e.get_mut().saturating_subsume(accounting);
222 },
223 btree_map::Entry::Vacant(e) => {
224 e.insert(accounting);
225 },
226 }
227 }
228 let mut non_fungible = assets.non_fungible;
232 self.non_fungible.append(&mut non_fungible);
233 }
234
235 pub fn swapped(&mut self, mut with: AssetsInHolding) -> Self {
237 mem::swap(&mut *self, &mut with);
238 with
239 }
240
241 pub fn reanchor_and_burn_local(
250 self,
251 target: &Location,
252 context: &InteriorLocation,
253 failed_bin: &mut Self,
254 ) -> Assets {
255 let mut assets: Vec<Asset> = self
256 .fungible
257 .into_iter()
258 .filter_map(|(mut id, accounting)| match id.reanchor(target, context) {
259 Ok(()) => Some(Asset::from((id, Fungible(accounting.amount())))),
260 Err(()) => {
261 failed_bin.fungible.insert(id, accounting);
262 None
263 },
264 })
265 .chain(self.non_fungible.into_iter().filter_map(|(mut class, inst)| {
266 match class.reanchor(target, context) {
267 Ok(()) => Some(Asset::from((class, inst))),
268 Err(()) => {
269 failed_bin.non_fungible.insert((class, inst));
270 None
271 },
272 }
273 }))
274 .collect();
275 assets.sort();
276 assets.into()
277 }
278
279 pub fn reanchored_assets(&self, target: &Location, context: &InteriorLocation) -> Assets {
287 let mut assets: Vec<Asset> = self
288 .fungible
289 .iter()
290 .filter_map(|(id, accounting)| match id.clone().reanchored(target, context) {
291 Ok(new_id) => Some(Asset::from((new_id, Fungible(accounting.amount())))),
292 Err(()) => None,
293 })
294 .chain(self.non_fungible.iter().filter_map(|(class, inst)| {
295 match class.clone().reanchored(target, context) {
296 Ok(new_class) => Some(Asset::from((new_class, *inst))),
297 Err(()) => None,
298 }
299 }))
300 .collect();
301 assets.sort();
302 assets.into()
303 }
304
305 pub fn contains_asset(&self, asset: &Asset) -> bool {
307 match asset {
308 Asset { fun: Fungible(amount), id } => {
309 self.fungible.get(id).map_or(false, |a| a.amount() >= *amount)
310 },
311 Asset { fun: NonFungible(instance), id } => {
312 self.non_fungible.contains(&(id.clone(), *instance))
313 },
314 }
315 }
316
317 pub fn contains_assets(&self, assets: &Assets) -> bool {
319 assets.inner().iter().all(|a| self.contains_asset(a))
320 }
321
322 pub fn ensure_contains(&self, assets: &Assets) -> Result<(), TakeError> {
324 for asset in assets.inner().iter() {
325 match asset {
326 Asset { fun: Fungible(amount), id } => {
327 if self.fungible.get(id).map_or(true, |a| a.amount() < *amount) {
328 return Err(TakeError::AssetUnderflow((id.clone(), *amount).into()));
329 }
330 },
331 Asset { fun: NonFungible(instance), id } => {
332 let id_instance = (id.clone(), *instance);
333 if !self.non_fungible.contains(&id_instance) {
334 return Err(TakeError::AssetUnderflow(id_instance.into()));
335 }
336 },
337 }
338 }
339 return Ok(());
340 }
341
342 fn general_take(
356 &mut self,
357 mask: AssetFilter,
358 saturate: bool,
359 ) -> Result<AssetsInHolding, TakeError> {
360 let mut taken = AssetsInHolding::new();
361 let maybe_limit = mask.limit().map(|x| x as usize);
362 match mask {
363 AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => match maybe_limit {
364 None => return Ok(self.swapped(AssetsInHolding::new())),
365 Some(limit) if self.len() <= limit => {
366 return Ok(self.swapped(AssetsInHolding::new()))
367 },
368 Some(0) => return Ok(AssetsInHolding::new()),
369 Some(limit) => {
370 let fungible = mem::replace(&mut self.fungible, Default::default());
371 fungible.into_iter().for_each(|(c, amount)| {
372 if taken.len() < limit {
373 taken.fungible.insert(c, amount);
374 } else {
375 self.fungible.insert(c, amount);
376 }
377 });
378 let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
379 non_fungible.into_iter().for_each(|(c, instance)| {
380 if taken.len() < limit {
381 taken.non_fungible.insert((c, instance));
382 } else {
383 self.non_fungible.insert((c, instance));
384 }
385 });
386 },
387 },
388 AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
389 AssetFilter::Wild(AllOf { fun: WildFungible, id }) => {
390 if maybe_limit.map_or(true, |l| l >= 1) {
391 if let Some((id, amount)) = self.fungible.remove_entry(&id) {
392 taken.fungible.insert(id, amount);
393 }
394 }
395 },
396 AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
397 AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => {
398 let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
399 non_fungible.into_iter().for_each(|(c, instance)| {
400 if c == id && maybe_limit.map_or(true, |l| taken.len() < l) {
401 taken.non_fungible.insert((c, instance));
402 } else {
403 self.non_fungible.insert((c, instance));
404 }
405 });
406 },
407 AssetFilter::Definite(assets) => {
408 if !saturate {
409 self.ensure_contains(&assets)?;
410 }
411 for asset in assets.into_inner().into_iter() {
412 match asset {
413 Asset { fun: Fungible(amount), id } => {
414 let (remove, balance) = match self.fungible.get_mut(&id) {
415 Some(self_amount) => {
416 let balance = self_amount.saturating_take(amount);
419 (self_amount.amount() == 0, Some(balance))
420 },
421 None => (false, None),
422 };
423 if remove {
424 self.fungible.remove(&id);
425 }
426 if let Some(balance) = balance {
427 let other = Self::new_from_fungible_credit(id, balance);
428 taken.subsume_assets(other);
429 }
430 },
431 Asset { fun: NonFungible(instance), id } => {
432 let id_instance = (id, instance);
433 if self.non_fungible.remove(&id_instance) {
434 taken.non_fungible.insert((id_instance.0, id_instance.1));
435 }
436 },
437 }
438 }
439 },
440 }
441 Ok(taken)
442 }
443
444 pub fn saturating_take(&mut self, asset: AssetFilter) -> Self {
450 self.general_take(asset, true)
451 .expect("general_take never results in error when saturating")
452 }
453
454 pub fn try_take(&mut self, mask: AssetFilter) -> Result<Self, TakeError> {
460 self.general_take(mask, false)
461 }
462
463 pub fn min(&self, mask: &AssetFilter) -> Assets {
485 let mut masked = Assets::new();
486 let maybe_limit = mask.limit().map(|x| x as usize);
487 if maybe_limit.map_or(false, |l| l == 0) {
488 return masked;
489 }
490 match mask {
491 AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => {
492 if maybe_limit.map_or(true, |l| self.len() <= l) {
493 return self.assets_iter().collect::<Vec<Asset>>().into();
494 } else {
495 for (c, accounting) in self.fungible.iter() {
496 masked.push((c.clone(), accounting.amount()).into());
497 if maybe_limit.map_or(false, |l| masked.len() >= l) {
498 return masked;
499 }
500 }
501 for (c, instance) in self.non_fungible.iter() {
502 masked.push((c.clone(), *instance).into());
503 if maybe_limit.map_or(false, |l| masked.len() >= l) {
504 return masked;
505 }
506 }
507 }
508 },
509 AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
510 AssetFilter::Wild(AllOf { fun: WildFungible, id }) => {
511 if let Some(accounting) = self.fungible.get(&id) {
512 masked.push((id.clone(), accounting.amount()).into());
513 }
514 },
515 AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
516 AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => {
517 for (c, instance) in self.non_fungible.iter() {
518 if c == id {
519 masked.push((c.clone(), *instance).into());
520 if maybe_limit.map_or(false, |l| masked.len() >= l) {
521 return masked;
522 }
523 }
524 }
525 },
526 AssetFilter::Definite(assets) => {
527 for asset in assets.inner().iter() {
528 match asset {
529 Asset { fun: Fungible(amount), id } => {
530 if let Some(m) = self.fungible.get(id) {
531 masked
532 .push((id.clone(), Fungible(*amount.min(&m.amount()))).into());
533 }
534 },
535 Asset { fun: NonFungible(instance), id } => {
536 let id_instance = (id.clone(), *instance);
537 if self.non_fungible.contains(&id_instance) {
538 masked.push(id_instance.into());
539 }
540 },
541 }
542 }
543 },
544 }
545 masked
546 }
547
548 #[cfg(feature = "std")]
553 pub fn unsafe_clone_for_tests(&self) -> Self {
554 Self {
555 fungible: self
556 .fungible
557 .iter()
558 .map(|(id, accounting)| (id.clone(), accounting.unsafe_clone()))
559 .collect(),
560 non_fungible: self.non_fungible.clone(),
561 }
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use crate::tests::mock::*;
569 use alloc::vec;
570 use xcm::latest::prelude::*;
571
572 #[allow(non_snake_case)]
573 fn CF(amount: u128) -> Asset {
575 (Here, amount).into()
576 }
577 #[allow(non_snake_case)]
578 fn CFG(index: u128, amount: u128) -> Asset {
580 (GeneralIndex(index), amount).into()
581 }
582 #[allow(non_snake_case)]
583 fn CFP(amount: u128) -> Asset {
585 (Parent, amount).into()
586 }
587 #[allow(non_snake_case)]
588 fn CFPP(amount: u128) -> Asset {
590 ((Parent, Parent), amount).into()
591 }
592 #[allow(non_snake_case)]
593 fn CNF(instance_id: u8) -> Asset {
595 (Here, [instance_id; 4]).into()
596 }
597
598 fn asset_to_holding(asset: Asset) -> AssetsInHolding {
600 let mut holding = AssetsInHolding::new();
603 match asset.fun {
604 Fungible(amount) => {
605 holding.fungible.insert(asset.id, Box::new(MockCredit(amount)));
606 },
607 NonFungible(instance) => {
608 holding.non_fungible.insert((asset.id, instance));
609 },
610 }
611 holding
612 }
613
614 fn test_assets() -> AssetsInHolding {
615 let mut assets = AssetsInHolding::new();
616 assets.subsume_assets(asset_to_holding(CF(300)));
617 assets.subsume_assets(asset_to_holding(CNF(40)));
618 assets
619 }
620
621 #[test]
622 fn assets_in_holding_order_works() {
623 let mut assets = AssetsInHolding::new();
625 assets.subsume_assets(asset_to_holding(CFPP(300)));
626 assets.subsume_assets(asset_to_holding(CFP(200)));
627 assets.subsume_assets(asset_to_holding(CNF(2)));
628 assets.subsume_assets(asset_to_holding(CF(100)));
629 assets.subsume_assets(asset_to_holding(CNF(1)));
630 assets.subsume_assets(asset_to_holding(CFG(10, 400)));
631 assets.subsume_assets(asset_to_holding(CFG(15, 500)));
632
633 let mut iter = assets.unsafe_clone_for_tests().into_assets_iter();
639 assert_eq!(Some(CF(100)), iter.next());
641 assert_eq!(Some(CFG(10, 400)), iter.next());
643 assert_eq!(Some(CFG(15, 500)), iter.next());
645 assert_eq!(Some(CFP(200)), iter.next());
647 assert_eq!(Some(CFPP(300)), iter.next());
649 assert_eq!(Some(CNF(1)), iter.next());
651 assert_eq!(Some(CNF(2)), iter.next());
653 assert_eq!(None, iter.next());
655
656 let assets_same = assets.unsafe_clone_for_tests();
659 assets.subsume_assets(assets_same);
660
661 let mut iter = assets.into_assets_iter();
662 assert_eq!(Some(CF(200)), iter.next());
663 assert_eq!(Some(CFG(10, 800)), iter.next());
664 assert_eq!(Some(CFG(15, 1000)), iter.next());
665 assert_eq!(Some(CFP(400)), iter.next());
666 assert_eq!(Some(CFPP(600)), iter.next());
667 assert_eq!(Some(CNF(1)), iter.next());
668 assert_eq!(Some(CNF(2)), iter.next());
669 assert_eq!(None, iter.next());
670 }
671
672 #[test]
673 fn subsume_assets_equal_length_holdings() {
674 let mut t1 = test_assets();
675 let mut t2 = AssetsInHolding::new();
676 t2.subsume_assets(asset_to_holding(CF(300)));
677 t2.subsume_assets(asset_to_holding(CNF(50)));
678
679 let t1_clone = t1.unsafe_clone_for_tests();
680 let mut t2_clone = t2.unsafe_clone_for_tests();
681
682 t1.subsume_assets(t2.unsafe_clone_for_tests());
685 let mut iter = t1.into_assets_iter();
686 assert_eq!(Some(CF(600)), iter.next());
687 assert_eq!(Some(CNF(40)), iter.next());
688 assert_eq!(Some(CNF(50)), iter.next());
689 assert_eq!(None, iter.next());
690
691 t2_clone.subsume_assets(t1_clone.unsafe_clone_for_tests());
694 let mut iter = t2_clone.into_assets_iter();
695 assert_eq!(Some(CF(600)), iter.next());
696 assert_eq!(Some(CNF(40)), iter.next());
697 assert_eq!(Some(CNF(50)), iter.next());
698 assert_eq!(None, iter.next());
699 }
700
701 #[test]
702 fn subsume_assets_different_length_holdings() {
703 let mut t1 = AssetsInHolding::new();
704 t1.subsume_assets(asset_to_holding(CFP(400)));
705 t1.subsume_assets(asset_to_holding(CFPP(100)));
706
707 let mut t2 = AssetsInHolding::new();
708 t2.subsume_assets(asset_to_holding(CF(100)));
709 t2.subsume_assets(asset_to_holding(CNF(50)));
710 t2.subsume_assets(asset_to_holding(CNF(40)));
711 t2.subsume_assets(asset_to_holding(CFP(100)));
712 t2.subsume_assets(asset_to_holding(CFPP(100)));
713
714 let t1_clone = t1.unsafe_clone_for_tests();
715 let mut t2_clone = t2.unsafe_clone_for_tests();
716
717 t1.subsume_assets(t2);
720 let mut iter = t1.into_assets_iter();
721 assert_eq!(Some(CF(100)), iter.next());
722 assert_eq!(Some(CFP(500)), iter.next());
723 assert_eq!(Some(CFPP(200)), iter.next());
724 assert_eq!(Some(CNF(40)), iter.next());
725 assert_eq!(Some(CNF(50)), iter.next());
726 assert_eq!(None, iter.next());
727
728 t2_clone.subsume_assets(t1_clone);
731 let mut iter = t2_clone.into_assets_iter();
732 assert_eq!(Some(CF(100)), iter.next());
733 assert_eq!(Some(CFP(500)), iter.next());
734 assert_eq!(Some(CFPP(200)), iter.next());
735 assert_eq!(Some(CNF(40)), iter.next());
736 assert_eq!(Some(CNF(50)), iter.next());
737 assert_eq!(None, iter.next());
738 }
739
740 #[test]
741 fn subsume_assets_empty_holding() {
742 let mut t1 = AssetsInHolding::new();
743 let t2 = AssetsInHolding::new();
744 t1.subsume_assets(t2.unsafe_clone_for_tests());
745 let mut iter = t1.unsafe_clone_for_tests().into_assets_iter();
746 assert_eq!(None, iter.next());
747
748 t1.subsume_assets(asset_to_holding(CFP(400)));
749 t1.subsume_assets(asset_to_holding(CNF(40)));
750 t1.subsume_assets(asset_to_holding(CFPP(100)));
751
752 let t1_clone = t1.unsafe_clone_for_tests();
753 let mut t2_clone = t2.unsafe_clone_for_tests();
754
755 t1.subsume_assets(t2.unsafe_clone_for_tests());
758 let mut iter = t1.into_assets_iter();
759 assert_eq!(Some(CFP(400)), iter.next());
760 assert_eq!(Some(CFPP(100)), iter.next());
761 assert_eq!(Some(CNF(40)), iter.next());
762 assert_eq!(None, iter.next());
763
764 t2_clone.subsume_assets(t1_clone.unsafe_clone_for_tests());
767 let mut iter = t2_clone.into_assets_iter();
768 assert_eq!(Some(CFP(400)), iter.next());
769 assert_eq!(Some(CFPP(100)), iter.next());
770 assert_eq!(Some(CNF(40)), iter.next());
771 assert_eq!(None, iter.next());
772 }
773
774 #[test]
775 fn into_assets_iter_works() {
776 let assets = test_assets();
777 let mut iter = assets.into_assets_iter();
778 assert_eq!(Some(CF(300)), iter.next());
780 assert_eq!(Some(CNF(40)), iter.next());
781 assert_eq!(None, iter.next());
782 }
783
784 #[test]
785 fn assets_into_works() {
786 let mut assets_vec: Vec<Asset> = Vec::new();
787 assets_vec.push(CF(300));
788 assets_vec.push(CNF(40));
789 assets_vec.push(CF(300));
791 assets_vec.push(CNF(40));
792
793 let mut assets = AssetsInHolding::new();
794 for asset in assets_vec {
795 assets.subsume_assets(asset_to_holding(asset));
796 }
797 let mut iter = assets.into_assets_iter();
798 assert_eq!(Some(CF(600)), iter.next());
800 assert_eq!(Some(CNF(40)), iter.next());
802 assert_eq!(None, iter.next());
803 }
804
805 #[test]
806 fn min_all_and_none_works() {
807 let assets = test_assets();
808 let none = Assets::new().into();
809 let all = All.into();
810
811 let none_min = assets.min(&none);
812 assert_eq!(None, none_min.inner().iter().next());
813 let all_min = assets.min(&all);
814 let all_min_vec: Vec<_> = all_min.inner().iter().cloned().collect();
815 let assets_vec: Vec<_> = assets.assets_iter().collect();
816 assert_eq!(all_min_vec, assets_vec);
817 }
818
819 #[test]
820 fn min_counted_works() {
821 let mut assets = AssetsInHolding::new();
822 assets.subsume_assets(asset_to_holding(CNF(40)));
823 assets.subsume_assets(asset_to_holding(CF(3000)));
824 assets.subsume_assets(asset_to_holding(CNF(80)));
825 let all = WildAsset::AllCounted(6).into();
826
827 let all = assets.min(&all);
828 assert_eq!(all.inner(), &vec![CF(3000), CNF(40), CNF(80)]);
829 }
830
831 #[test]
832 fn min_all_concrete_works() {
833 let assets = test_assets();
834 let fungible = Wild((Here, WildFungible).into());
835 let non_fungible = Wild((Here, WildNonFungible).into());
836
837 let fungible = assets.min(&fungible);
838 assert_eq!(fungible.inner(), &vec![CF(300)]);
839 let non_fungible = assets.min(&non_fungible);
840 assert_eq!(non_fungible.inner(), &vec![CNF(40)]);
841 }
842
843 #[test]
844 fn min_basic_works() {
845 let assets1 = test_assets();
846
847 let assets2: Assets = vec![
849 CF(600),
851 CNF(40),
853 ]
854 .into();
855
856 let assets_min = assets1.min(&assets2.into());
857 assert_eq!(assets_min.inner(), &vec![CF(300), CNF(40)]);
858 }
859
860 #[test]
861 fn saturating_take_all_and_none_works() {
862 let mut assets = test_assets();
863
864 let taken_none = assets.saturating_take(vec![].into());
865 assert_eq!(None, taken_none.assets_iter().next());
866 let taken_all = assets.saturating_take(All.into());
867 assert_eq!(None, assets.assets_iter().next());
869 let all_iter = taken_all.assets_iter();
870 assert!(all_iter.eq(test_assets().assets_iter()));
871 }
872
873 #[test]
874 fn saturating_take_all_concrete_works() {
875 let mut assets = test_assets();
876 let fungible = Wild((Here, WildFungible).into());
877 let non_fungible = Wild((Here, WildNonFungible).into());
878
879 let fungible = assets.saturating_take(fungible);
880 let fungible = fungible.assets_iter().collect::<Vec<_>>();
881 assert_eq!(fungible, vec![CF(300)]);
882 let non_fungible = assets.saturating_take(non_fungible);
883 let non_fungible = non_fungible.assets_iter().collect::<Vec<_>>();
884 assert_eq!(non_fungible, vec![CNF(40)]);
885 }
886
887 #[test]
888 fn saturating_take_basic_works() {
889 let mut assets1 = test_assets();
890
891 let assets2: Assets = vec![
893 CF(600),
895 CNF(40),
897 ]
898 .into();
899
900 let taken = assets1.saturating_take(assets2.into());
901 let taken_vec: Vec<_> = taken.assets_iter().collect();
902 assert_eq!(taken_vec, vec![CF(300), CNF(40)]);
903 }
904
905 #[test]
906 fn try_take_all_counted_works() {
907 let mut assets = AssetsInHolding::new();
908 assets.subsume_assets(asset_to_holding(CNF(40)));
909 assets.subsume_assets(asset_to_holding(CF(3000)));
910 assets.subsume_assets(asset_to_holding(CNF(80)));
911 let all = assets.try_take(WildAsset::AllCounted(6).into()).unwrap();
912 let all_vec: Vec<_> = all.assets_iter().collect();
913 assert_eq!(all_vec, vec![CF(3000), CNF(40), CNF(80)]);
914 }
915
916 #[test]
917 fn try_take_fungibles_counted_works() {
918 let mut assets = AssetsInHolding::new();
919 assets.subsume_assets(asset_to_holding(CNF(40)));
920 assets.subsume_assets(asset_to_holding(CF(3000)));
921 assets.subsume_assets(asset_to_holding(CNF(80)));
922 let assets_vec: Vec<_> = assets.assets_iter().collect();
923 assert_eq!(assets_vec, vec![CF(3000), CNF(40), CNF(80)]);
924 }
925
926 #[test]
927 fn try_take_non_fungibles_counted_works() {
928 let mut assets = AssetsInHolding::new();
929 assets.subsume_assets(asset_to_holding(CNF(40)));
930 assets.subsume_assets(asset_to_holding(CF(3000)));
931 assets.subsume_assets(asset_to_holding(CNF(80)));
932 let assets_vec: Vec<_> = assets.assets_iter().collect();
933 assert_eq!(assets_vec, vec![CF(3000), CNF(40), CNF(80)]);
934 }
935}