sui_graphql_rpc/types/
big_int.rs

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