iota_graphql_rpc/types/
big_int.rs1use std::str::FromStr;
6
7use async_graphql::*;
8use move_core_types::u256::U256;
9use serde::{Deserialize, Serialize};
10
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
12#[serde(transparent)]
13pub(crate) struct BigInt(String);
14
15#[derive(thiserror::Error, Debug, PartialEq, Eq)]
16#[error("The provided string is not a number")]
17pub(crate) struct NotANumber;
18
19#[Scalar(use_type_description = true)]
20impl ScalarType for BigInt {
21 fn parse(value: Value) -> InputValueResult<Self> {
22 match value {
23 Value::String(s) => BigInt::from_str(&s)
24 .map_err(|_| InputValueError::custom("Not a number".to_string())),
25 _ => Err(InputValueError::expected_type(value)),
26 }
27 }
28
29 fn to_value(&self) -> Value {
30 Value::String(self.0.clone())
31 }
32}
33
34impl Description for BigInt {
35 fn description() -> &'static str {
36 "String representation of an arbitrary width, possibly signed integer."
37 }
38}
39
40impl FromStr for BigInt {
41 type Err = NotANumber;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 let mut r = s;
45 let mut signed = false;
46 if let Some(suffix) = s.strip_prefix('-') {
48 r = suffix;
49 signed = true;
50 }
51 r = r.trim_start_matches('0');
52
53 if r.is_empty() {
54 Ok(BigInt("0".to_string()))
55 } else if r.chars().all(|c| c.is_ascii_digit()) {
56 Ok(BigInt(format!("{}{}", if signed { "-" } else { "" }, r)))
57 } else {
58 Err(NotANumber)
59 }
60 }
61}
62
63macro_rules! impl_From {
64 ($($t:ident),*) => {
65 $(impl From<$t> for BigInt {
66 fn from(value: $t) -> Self {
67 BigInt(value.to_string())
68 }
69 })*
70 }
71}
72
73impl_From!(u8, u16, u32, i64, u64, i128, u128, U256);
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn from_value() {
81 assert_eq!(BigInt::from_str("123").unwrap(), BigInt("123".to_string()));
82 assert_eq!(
83 BigInt::from_str("-123").unwrap(),
84 BigInt("-123".to_string())
85 );
86 assert_eq!(
87 BigInt::from_str("00233").unwrap(),
88 BigInt("233".to_string())
89 );
90 assert_eq!(BigInt::from_str("0").unwrap(), BigInt("0".to_string()));
91 assert_eq!(BigInt::from_str("-0").unwrap(), BigInt("0".to_string()));
92 assert_eq!(BigInt::from_str("000").unwrap(), BigInt("0".to_string()));
93 assert_eq!(BigInt::from_str("-000").unwrap(), BigInt("0".to_string()));
94
95 assert!(BigInt::from_str("123a").is_err());
96 assert!(BigInt::from_str("a123").is_err());
97 assert!(BigInt::from_str("123-").is_err());
98 assert!(BigInt::from_str(" 123").is_err());
99 }
100
101 #[test]
102 fn from_primitives() {
103 assert_eq!(BigInt::from(123u8), BigInt("123".to_string()));
104
105 assert_eq!(BigInt::from(12_345u16), BigInt("12345".to_string()));
106
107 assert_eq!(BigInt::from(123_456u32), BigInt("123456".to_string()));
108
109 assert_eq!(
110 BigInt::from(-12_345_678_901i64),
111 BigInt("-12345678901".to_string()),
112 );
113
114 assert_eq!(
115 BigInt::from(12_345_678_901u64),
116 BigInt("12345678901".to_string()),
117 );
118
119 assert_eq!(
120 BigInt::from(-123_456_789_012_345_678_901i128),
121 BigInt("-123456789012345678901".to_string()),
122 );
123
124 assert_eq!(
125 BigInt::from(123_456_789_012_345_678_901u128),
126 BigInt("123456789012345678901".to_string()),
127 );
128
129 assert_eq!(
130 BigInt::from(U256::from_str("12345678901234567890123456789012345678901").unwrap()),
131 BigInt("12345678901234567890123456789012345678901".to_string())
132 );
133
134 assert_eq!(BigInt::from(1000i64 - 1200i64), BigInt("-200".to_string()));
135 assert_eq!(BigInt::from(-1200i64), BigInt("-1200".to_string()));
136 }
137}