async_graphql/types/
string_number.rs1use std::fmt::Display;
2
3use num_traits::Num;
4use serde::{Deserialize, Serialize};
5
6use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
7
8#[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}