async_graphql/types/
string_number.rs

1use std::fmt::Display;
2
3use num_traits::Num;
4use serde::{Deserialize, Serialize};
5
6use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
7
8/// A numeric value represented by a string.
9#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
10#[serde(transparent)]
11#[cfg_attr(docsrs, doc(cfg(feature = "string_number")))]
12pub struct StringNumber<T: Num + Display>(pub T);
13
14impl<T: Num + Display + Default> Default for StringNumber<T> {
15    #[inline]
16    fn default() -> Self {
17        Self(Default::default())
18    }
19}
20
21#[Scalar(internal)]
22impl<T: Num + Display + Send + Sync> ScalarType for StringNumber<T>
23where
24    <T as Num>::FromStrRadixErr: Display,
25{
26    fn parse(value: Value) -> InputValueResult<Self> {
27        match value {
28            Value::String(s) => {
29                let n = T::from_str_radix(&s, 10)
30                    .map_err(|err| InputValueError::custom(err.to_string()))?;
31                Ok(StringNumber(n))
32            }
33            _ => Err(InputValueError::expected_type(value)),
34        }
35    }
36
37    fn is_valid(value: &Value) -> bool {
38        matches!(value, Value::String(_))
39    }
40
41    fn to_value(&self) -> Value {
42        Value::String(self.0.to_string())
43    }
44}
45
46#[cfg(test)]
47mod test {
48    use crate::*;
49
50    #[tokio::test]
51    async fn test_string_number() {
52        struct Query;
53
54        #[Object(internal)]
55        impl Query {
56            async fn value(&self, n: StringNumber<i32>) -> StringNumber<i32> {
57                n
58            }
59        }
60
61        let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
62        assert_eq!(
63            schema
64                .execute(
65                    r#"{
66                    value1: value(n: "100")
67                    value2: value(n: "-100")
68                    value3: value(n: "0")
69                    value4: value(n: "1")
70                }"#
71                )
72                .await
73                .into_result()
74                .unwrap()
75                .data,
76            value!({
77                "value1": "100",
78                "value2": "-100",
79                "value3": "0",
80                "value4": "1",
81            })
82        );
83    }
84}