1use std::collections::{HashMap, HashSet};
2use std::fmt::Display;
3use std::hash::Hash;
4
5use candid::{CandidType, Deserialize, Encode, Principal};
6use hex::ToHex;
7use num_bigint::{BigInt, BigUint};
8use num_traits::cast::ToPrimitive;
9use num_traits::Signed;
10use serde::Serialize;
11
12use crate::types::PropertyShared;
13
14#[derive(CandidType, Debug, Serialize, Deserialize, Clone)]
20pub enum CandyShared {
21 Int(candid::Int),
22 Int8(i8),
23 Int16(i16),
24 Int32(i32),
25 Int64(i64),
26 Ints(Vec<candid::Int>),
27 Nat(candid::Nat),
28 Nat8(u8),
29 Nat16(u16),
30 Nat32(u32),
31 Nat64(u64),
32 Float(f64),
33 Text(String),
34 Bool(bool),
35 Blob(Vec<u8>),
36 Bytes(Vec<u8>),
37 Class(Vec<PropertyShared>),
38 Principal(Principal),
39 Option(Option<Box<CandyShared>>),
40 Array(Vec<CandyShared>),
41 Nats(Vec<candid::Nat>),
42 Floats(Vec<f64>),
43 Map(HashMap<CandyShared, CandyShared>),
44 Set(HashSet<CandyShared>),
45}
46
47macro_rules! to_nat_of_size {
48 ($x:tt, $method: ident) => {
49 match $x {
50 Self::Nat(val) => val.0.$method(),
51 Self::Nat8(val) => val.$method(),
52 Self::Nat16(val) => val.$method(),
53 Self::Nat32(val) => val.$method(),
54 Self::Nat64(val) => val.$method(),
55 Self::Float(val) => match val {
56 val if val < 0.0 => None,
57 _ => val.round().$method(),
58 },
59 Self::Int(val) => val.0.$method(),
60 Self::Int8(val) => val.$method(),
61 Self::Int16(val) => val.$method(),
62 Self::Int32(val) => val.$method(),
63 Self::Int64(val) => val.$method(),
64 _ => None,
65 }
66 };
67}
68
69macro_rules! to_int_of_size {
70 ($x:tt, $method: ident) => {
71 match $x {
72 Self::Nat(val) => val.0.$method(),
73 Self::Nat8(val) => val.$method(),
74 Self::Nat16(val) => val.$method(),
75 Self::Nat32(val) => val.$method(),
76 Self::Nat64(val) => val.$method(),
77 Self::Float(val) => val.round().$method(),
78 Self::Int(val) => val.0.$method(),
79 Self::Int8(val) => val.$method(),
80 Self::Int16(val) => val.$method(),
81 Self::Int32(val) => val.$method(),
82 Self::Int64(val) => val.$method(),
83 _ => None,
84 }
85 };
86}
87
88impl CandyShared {
89 pub fn to_nat(self) -> Option<u128> {
104 to_nat_of_size!(self, to_u128)
105 }
106
107 pub fn to_nat8(self) -> Option<u8> {
126 to_nat_of_size!(self, to_u8)
127 }
128
129 pub fn to_nat16(self) -> Option<u16> {
144 to_nat_of_size!(self, to_u16)
145 }
146
147 pub fn to_nat32(self) -> Option<u32> {
162 to_nat_of_size!(self, to_u32)
163 }
164
165 pub fn to_nat64(self) -> Option<u64> {
180 to_nat_of_size!(self, to_u64)
181 }
182
183 pub fn to_int(self) -> Option<i128> {
198 to_int_of_size!(self, to_i128)
199 }
200
201 pub fn to_int8(self) -> Option<i8> {
216 to_int_of_size!(self, to_i8)
217 }
218
219 pub fn to_int16(self) -> Option<i16> {
238 to_int_of_size!(self, to_i16)
239 }
240
241 pub fn to_int32(self) -> Option<i32> {
256 to_int_of_size!(self, to_i32)
257 }
258
259 pub fn to_int64(self) -> Option<i64> {
274 to_int_of_size!(self, to_i64)
275 }
276
277 pub fn to_float(self) -> Option<f64> {
292 match self {
293 Self::Nat(val) => val.0.to_f64(),
294 Self::Nat8(val) => val.to_f64(),
295 Self::Nat16(val) => val.to_f64(),
296 Self::Nat32(val) => val.to_f64(),
297 Self::Nat64(val) => val.to_f64(),
298 Self::Float(val) => Some(val),
299 Self::Int(val) => val.0.to_f64(),
300 Self::Int8(val) => val.to_f64(),
301 Self::Int16(val) => val.to_f64(),
302 Self::Int32(val) => val.to_f64(),
303 Self::Int64(val) => val.to_f64(),
304 _ => None,
305 }
306 }
307
308 pub fn to_bool(self) -> Option<bool> {
323 match self {
324 Self::Bool(val) => Some(val),
325 _ => None,
326 }
327 }
328
329 pub fn to_principal(self) -> Option<Principal> {
346 match self {
347 Self::Principal(val) => Some(val),
348 _ => None,
349 }
350 }
351
352 pub fn to_blob(self) -> Vec<u8> {
367 match self {
368 Self::Blob(val) => val,
369 Self::Bytes(val) => val,
370 Self::Text(val) => val.chars().flat_map(|c| (c as u32).to_be_bytes()).collect(),
371 Self::Int(val) => val.0.to_blob(),
372 Self::Nat(val) => val.0.to_blob(),
373 Self::Nat8(val) => [val].to_vec(),
374 Self::Nat16(val) => val.to_be_bytes().to_vec(),
375 Self::Nat32(val) => val.to_be_bytes().to_vec(),
376 Self::Nat64(val) => val.to_be_bytes().to_vec(),
377 Self::Principal(val) => val.as_slice().into(),
378 _ => ic_cdk::trap(format!("Cannot convert to blob {}", self).as_str()),
379 }
380 }
381
382 pub fn to_json(self) -> String {
397 match self {
398 Self::Nat(val) => val.to_string(),
399 Self::Nat8(val) => val.to_string(),
400 Self::Nat16(val) => val.to_string(),
401 Self::Nat32(val) => val.to_string(),
402 Self::Nat64(val) => val.to_string(),
403 Self::Int(val) => val.to_string(),
404 Self::Int8(val) => val.to_string(),
405 Self::Int16(val) => val.to_string(),
406 Self::Int32(val) => val.to_string(),
407 Self::Int64(val) => val.to_string(),
408 Self::Float(val) => val.to_string(),
409 Self::Text(val) => serde_json::to_string(&val).unwrap(),
410 Self::Class(val) => PropertyShared::props_to_json(&val),
411 Self::Array(val) => format!(
412 "[{}]",
413 val.iter()
414 .map(|i| i.clone().to_json())
415 .collect::<Vec<String>>()
416 .join(",")
417 ),
418 Self::Option(val) => match val {
419 Some(val) => val.to_json(),
420 None => "null".to_string(),
421 },
422 Self::Nats(val) => format!(
423 "[{}]",
424 val.iter()
425 .map(|i| i.to_string())
426 .collect::<Vec<String>>()
427 .join(",")
428 ),
429 Self::Ints(val) => format!(
430 "[{}]",
431 val.iter()
432 .map(|i| i.to_string())
433 .collect::<Vec<String>>()
434 .join(",")
435 ),
436 Self::Floats(val) => format!(
437 "[{}]",
438 val.iter()
439 .map(|i| i.to_string())
440 .collect::<Vec<String>>()
441 .join(",")
442 ),
443 Self::Bytes(val) => format!("\"{}\"", val.encode_hex::<String>()),
444 Self::Blob(val) => format!("\"{}\"", val.encode_hex::<String>()),
445 Self::Principal(val) => format!("\"{}\"", val),
446 Self::Bool(val) => format!("\"{}\"", val),
447 _ => "".to_string(),
448 }
449 }
450
451 pub fn to_value_array(self) -> Option<Vec<CandyShared>> {
467 match self {
468 Self::Array(val) => Some(val),
469 _ => None,
470 }
471 }
472
473 pub fn get_value_size(&self) -> u128 {
487 Encode!(self).unwrap().len() as u128
488 }
489
490 pub fn stringify_array_of_values(vals: &[CandyShared]) -> String {
491 let mut result = String::new();
492 result.push('[');
493 for value in vals {
494 let converted = format!("{{{}}} ", value.clone());
495 result.push_str(&converted);
496 }
497 let mut trimmed = result.trim_end().to_string();
498 trimmed.push(']');
499 trimmed
500 }
501}
502
503macro_rules! impl_from {
504 ($($t:ty => $v:ident),*) => {
505 $(impl From<$t> for CandyShared {
506 fn from(value: $t) -> Self {
507 CandyShared::$v(value)
508 }
509 })*
510 };
511}
512
513impl_from!(
514 i8 => Int8,
515 i16 => Int16,
516 i32 => Int32,
517 i64 => Int64,
518 u8 => Nat8,
519 u16 => Nat16,
520 u32 => Nat32,
521 u64 => Nat64,
522 f64 => Float
523);
524
525impl_from!(
526 String => Text,
527 bool => Bool,
528 Vec<PropertyShared> => Class,
529 Principal => Principal ,
530 Option<Box<CandyShared >> => Option,
531 Vec<u8> => Blob,
532 HashMap<CandyShared,CandyShared> => Map,
533 HashSet<CandyShared> => Set
534);
535
536impl From<u128> for CandyShared {
537 fn from(value: u128) -> Self {
538 CandyShared::Nat(candid::Nat::from(value))
539 }
540}
541
542impl From<BigInt> for CandyShared {
543 fn from(value: BigInt) -> Self {
544 CandyShared::Int(candid::Int::from(value))
545 }
546}
547
548impl From<BigUint> for CandyShared {
549 fn from(value: BigUint) -> Self {
550 CandyShared::Nat(candid::Nat::from(value))
551 }
552}
553
554impl From<i128> for CandyShared {
555 fn from(value: i128) -> Self {
556 CandyShared::Int(candid::Int::from(value))
557 }
558}
559
560impl From<&str> for CandyShared {
561 fn from(value: &str) -> Self {
562 CandyShared::Text(value.to_string())
563 }
564}
565
566impl From<Vec<CandyShared>> for CandyShared {
567 fn from(value: Vec<CandyShared>) -> Self {
568 CandyShared::Array(value)
569 }
570}
571
572impl From<Vec<candid::Nat>> for CandyShared {
573 fn from(value: Vec<candid::Nat>) -> Self {
574 CandyShared::Nats(value)
575 }
576}
577
578impl From<Vec<candid::Int>> for CandyShared {
579 fn from(value: Vec<candid::Int>) -> Self {
580 CandyShared::Ints(value)
581 }
582}
583
584impl From<Vec<u128>> for CandyShared {
585 fn from(value: Vec<u128>) -> Self {
586 CandyShared::Nats(value.into_iter().map(candid::Nat::from).collect())
587 }
588}
589
590impl From<Vec<i128>> for CandyShared {
591 fn from(value: Vec<i128>) -> Self {
592 CandyShared::Ints(value.into_iter().map(candid::Int::from).collect())
593 }
594}
595
596impl From<Vec<f64>> for CandyShared {
597 fn from(value: Vec<f64>) -> Self {
598 CandyShared::Floats(value)
599 }
600}
601
602pub trait ToCandyValue {
617 fn to_candy(self) -> CandyShared;
618}
619
620macro_rules! to_candy {
621 ($($t:ty),*) => {
622 $(impl ToCandyValue for $t {
623 #[inline]
624 fn to_candy(self) -> CandyShared {
625 CandyShared::from(self)
626 }
627 })*
628 };
629}
630
631to_candy!(
633 BigInt,
634 BigUint,
635 i128,
636 i8,
637 i16,
638 i32,
639 i64,
640 u128,
641 u8,
642 u16,
643 u32,
644 u64,
645 f64,
646 Vec<u128>,
647 Vec<i128>,
648 Vec<f64>,
649 String,
650 bool,
651 Vec<PropertyShared>,
652 Principal,
653 Option<Box<CandyShared>>,
654 Vec<u8>,
655 Vec<CandyShared>,
656 &str,
657 HashMap<CandyShared, CandyShared>,
658 HashSet<CandyShared>
659);
660
661pub trait ToBlob {
677 fn to_blob(self) -> Vec<u8>;
678}
679
680impl ToBlob for BigUint {
681 #[inline]
682 fn to_blob(self) -> Vec<u8> {
683 let mut b = self;
684 let mut bytes: Vec<u8> = Vec::new();
685 loop {
686 let a = (b.clone() % BigUint::from(256_u32))
687 .to_u8()
688 .unwrap_or_else(|| ic_cdk::trap("Can not convert BigUint to u8"));
689 b /= BigUint::from(256_u32);
690 bytes.push(a);
691 if b == BigUint::from(0_u32) {
692 break;
693 }
694 }
695 bytes.reverse();
696 bytes
697 }
698}
699
700impl ToBlob for BigInt {
701 #[inline]
702 fn to_blob(self) -> Vec<u8> {
703 let c = u8::from(self < BigInt::from(0_i32));
704 let mut b = self.abs();
705 let mut bytes: Vec<u8> = vec![];
706 loop {
707 let a = (b.clone() % BigInt::from(128_i32))
708 .to_u8()
709 .unwrap_or_else(|| ic_cdk::trap("Can not convert BigInt to u8"));
710 b /= BigInt::from(128_i32);
711 bytes.push(a);
712 if b == BigInt::from(0_i32) {
713 break;
714 }
715 }
716 bytes.reverse();
717 bytes.insert(0, c);
718 bytes
719 }
720}
721
722impl ToBlob for char {
723 #[inline]
724 fn to_blob(self) -> Vec<u8> {
725 (self as u32).to_be_bytes().to_vec()
726 }
727}
728
729impl Hash for CandyShared {
730 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
731 match self {
732 CandyShared::Int(i) => i.hash(state),
733 CandyShared::Int8(i) => i.hash(state),
734 CandyShared::Int16(i) => i.hash(state),
735 CandyShared::Int32(i) => i.hash(state),
736 CandyShared::Int64(i) => i.hash(state),
737 CandyShared::Nat(i) => i.hash(state),
738 CandyShared::Nat8(i) => i.hash(state),
739 CandyShared::Nat16(i) => i.hash(state),
740 CandyShared::Nat32(i) => i.hash(state),
741 CandyShared::Nat64(i) => i.hash(state),
742 CandyShared::Float(i) => i.to_string().hash(state),
743 CandyShared::Text(i) => i.hash(state),
744 CandyShared::Bool(i) => i.hash(state),
745 CandyShared::Blob(i) => i.hash(state),
746 CandyShared::Bytes(i) => i.hash(state),
747 CandyShared::Class(i) => {
748 for prop in i {
749 prop.name.hash(state);
750 prop.value.hash(state);
751 prop.immutable.hash(state);
752 }
753 }
754 CandyShared::Principal(i) => i.hash(state),
755 CandyShared::Option(i) => i.hash(state),
756 CandyShared::Array(i) => {
757 for prop in i {
758 prop.hash(state)
759 }
760 }
761 CandyShared::Nats(i) => i.hash(state),
762 CandyShared::Ints(i) => i.hash(state),
763 CandyShared::Floats(i) => {
764 let mut buffer: Vec<u8> = Vec::new();
765 for num in i {
766 buffer.append(num.to_string().into_bytes().as_mut());
767 }
768 buffer.hash(state)
769 }
770 CandyShared::Map(i) => {
771 for (key, value) in i {
772 key.hash(state);
773 value.hash(state);
774 }
775 }
776 CandyShared::Set(i) => {
777 for prop in i {
778 prop.hash(state)
779 }
780 }
781 }
782 }
783}
784
785impl PartialEq for CandyShared {
786 fn eq(&self, other: &Self) -> bool {
787 match (self, other) {
788 (CandyShared::Int(i1), CandyShared::Int(i2)) => i1 == i2,
789 (CandyShared::Int8(i1), CandyShared::Int8(i2)) => i1 == i2,
790 (CandyShared::Int16(i1), CandyShared::Int16(i2)) => i1 == i2,
791 (CandyShared::Int32(i1), CandyShared::Int32(i2)) => i1 == i2,
792 (CandyShared::Int64(i1), CandyShared::Int64(i2)) => i1 == i2,
793 (CandyShared::Nat(i1), CandyShared::Nat(i2)) => i1 == i2,
794 (CandyShared::Nat8(i1), CandyShared::Nat8(i2)) => i1 == i2,
795 (CandyShared::Nat16(i1), CandyShared::Nat16(i2)) => i1 == i2,
796 (CandyShared::Nat32(i1), CandyShared::Nat32(i2)) => i1 == i2,
797 (CandyShared::Nat64(i1), CandyShared::Nat64(i2)) => i1 == i2,
798 (CandyShared::Float(i1), CandyShared::Float(i2)) => i1 == i2,
799 (CandyShared::Text(i1), CandyShared::Text(i2)) => i1 == i2,
800 (CandyShared::Bool(i1), CandyShared::Bool(i2)) => i1 == i2,
801 (CandyShared::Blob(i1), CandyShared::Blob(i2)) => i1 == i2,
802 (CandyShared::Bytes(i1), CandyShared::Bytes(i2)) => i1 == i2,
803 (CandyShared::Class(i1), CandyShared::Class(i2)) => i1 == i2,
804 (CandyShared::Principal(i1), CandyShared::Principal(i2)) => i1 == i2,
805 (CandyShared::Option(i1), CandyShared::Option(i2)) => i1 == i2,
806 (CandyShared::Array(i1), CandyShared::Array(i2)) => i1 == i2,
807 (CandyShared::Nats(i1), CandyShared::Nats(i2)) => i1 == i2,
808 (CandyShared::Ints(i1), CandyShared::Ints(i2)) => i1 == i2,
809 (CandyShared::Floats(i1), CandyShared::Floats(i2)) => i1 == i2,
810 (CandyShared::Map(map1), CandyShared::Map(map2)) => {
811 if map1.len() != map2.len() {
812 false
813 } else {
814 for (key1, value1) in map1.iter() {
815 match map2.get(key1) {
816 Some(value2) => {
817 if !(value1 == value2) {
818 return false;
819 }
820 }
821 None => return false,
822 }
823 }
824 true
825 }
826 }
827 (CandyShared::Set(set1), CandyShared::Set(set2)) => {
828 if set1.len() != set2.len() {
829 false
830 } else {
831 for element in set1 {
832 if !set2.contains(element) {
833 return false;
834 }
835 }
836 true
837 }
838 }
839 _ => false,
840 }
841 }
842}
843
844impl Eq for CandyShared {}
845
846impl Display for CandyShared {
847 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
848 match self {
849 Self::Int(val) => write!(f, "{}", val.0),
850 Self::Int8(val) => write!(f, "{}", val),
851 Self::Int16(val) => write!(f, "{}", val),
852 Self::Int32(val) => write!(f, "{}", val),
853 Self::Int64(val) => write!(f, "{}", val),
854 Self::Nat(val) => write!(f, "{}", val.0),
855 Self::Nat8(val) => write!(f, "{}", val),
856 Self::Nat16(val) => write!(f, "{}", val),
857 Self::Nat32(val) => write!(f, "{}", val),
858 Self::Nat64(val) => write!(f, "{}", val),
859 Self::Float(val) => write!(f, "{}", val),
860 Self::Text(val) => write!(f, "{}", val),
861 Self::Bool(val) => write!(f, "{}", val),
862 Self::Blob(val) => write!(f, "{}", val.encode_hex::<String>()),
863 Self::Bytes(val) => {
864 write!(f, "{}", val.encode_hex::<String>())
865 }
866 Self::Class(val) => write!(f, "{}", PropertyShared::stringify_properties(val)),
867 Self::Principal(val) => write!(f, "{}", val),
868 Self::Option(val) => write!(
869 f,
870 "{}",
871 val.as_ref()
872 .map(|val| val.to_string())
873 .unwrap_or_else(|| "null".to_string())
874 ),
875 Self::Array(val) => write!(f, "{}", {
876 let mut ret = String::new();
877 ret.push('[');
878 ret.push_str(
879 val.iter()
880 .map(|val| format!("{{{}}}", val))
881 .collect::<Vec<String>>()
882 .join(" ")
883 .as_str(),
884 );
885 let _ = ret.trim_end();
886 ret.push(']');
887
888 ret
889 }),
890 Self::Nats(val) => write!(f, "{}", {
891 let mut ret = String::new();
892 ret.push('[');
893 ret.push_str(
894 val.iter()
895 .map(|val| val.to_string())
896 .collect::<Vec<String>>()
897 .join(" ")
898 .as_str(),
899 );
900 let _ = ret.trim_end();
901 ret.push(']');
902 ret
903 }),
904 Self::Ints(val) => write!(f, "{}", {
905 let mut ret = String::new();
906 ret.push('[');
907 ret.push_str(
908 val.iter()
909 .map(|val| val.to_string())
910 .collect::<Vec<String>>()
911 .join(" ")
912 .as_str(),
913 );
914 let _ = ret.trim_end();
915 ret.push(']');
916 ret
917 }),
918 Self::Floats(val) => write!(f, "{}", {
919 let mut ret = String::new();
920 ret.push('[');
921 ret.push_str(
922 val.iter()
923 .map(|val| val.to_string())
924 .collect::<Vec<String>>()
925 .join(" ")
926 .as_str(),
927 );
928 let _ = ret.trim_end();
929 ret.push(']');
930
931 ret
932 }),
933 Self::Map(val) => write!(f, "{:?}", val),
934 Self::Set(val) => write!(f, "{:?}", val),
935 }
936 }
937}