juniper/integrations/
bigdecimal.rs1use std::str::FromStr as _;
12
13use crate::{graphql_scalar, InputValue, ScalarValue, Value};
14
15#[graphql_scalar(
28 with = bigdecimal_scalar,
29 parse_token(i32, f64, String),
30 specified_by_url = "https://docs.rs/bigdecimal",
31)]
32type BigDecimal = bigdecimal::BigDecimal;
33
34mod bigdecimal_scalar {
35 use super::*;
36
37 pub(super) fn to_output<S: ScalarValue>(v: &BigDecimal) -> Value<S> {
38 Value::scalar(v.to_string())
39 }
40
41 pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<BigDecimal, String> {
42 if let Some(i) = v.as_int_value() {
43 Ok(BigDecimal::from(i))
44 } else if let Some(f) = v.as_float_value() {
45 let mut buf = ryu::Buffer::new();
48 BigDecimal::from_str(buf.format(f))
49 .map_err(|e| format!("Failed to parse `BigDecimal` from `Float`: {e}"))
50 } else {
51 v.as_string_value()
52 .ok_or_else(|| format!("Expected `String`, found: {v}"))
53 .and_then(|s| {
54 BigDecimal::from_str(s)
55 .map_err(|e| format!("Failed to parse `BigDecimal` from `String`: {e}"))
56 })
57 }
58 }
59}
60
61#[cfg(test)]
62mod test {
63 use std::str::FromStr as _;
64
65 use crate::{graphql_input_value, FromInputValue as _, InputValue, ToInputValue as _};
66
67 use super::BigDecimal;
68
69 #[test]
70 fn parses_correct_input() {
71 for (input, expected) in [
72 (graphql_input_value!("4.20"), "4.20"),
73 (graphql_input_value!("0"), "0"),
74 (
75 graphql_input_value!("999999999999.999999999"),
76 "999999999999.999999999",
77 ),
78 (
79 graphql_input_value!("87553378877997984345"),
80 "87553378877997984345",
81 ),
82 (graphql_input_value!(123), "123"),
83 (graphql_input_value!(0), "0"),
84 (graphql_input_value!(43.44), "43.44"),
85 ] {
86 let input: InputValue = input;
87 let parsed = BigDecimal::from_input_value(&input);
88 let expected = BigDecimal::from_str(expected).unwrap();
89
90 assert!(
91 parsed.is_ok(),
92 "failed to parse `{input:?}`: {:?}",
93 parsed.unwrap_err(),
94 );
95 assert_eq!(parsed.unwrap(), expected, "input: {input:?}");
96 }
97 }
98
99 #[test]
100 fn fails_on_invalid_input() {
101 for input in [
102 graphql_input_value!(""),
103 graphql_input_value!("0,0"),
104 graphql_input_value!("12,"),
105 graphql_input_value!("1996-12-19T14:23:43"),
106 graphql_input_value!("i'm not even a number"),
107 graphql_input_value!(null),
108 graphql_input_value!(false),
109 ] {
110 let input: InputValue = input;
111 let parsed = BigDecimal::from_input_value(&input);
112
113 assert!(parsed.is_err(), "allows input: {input:?}");
114 }
115 }
116
117 #[test]
118 fn formats_correctly() {
119 for raw in [
120 "4.20",
121 "0",
122 "999999999999.999999999",
123 "87553378877997984345",
124 "123",
125 "43.44",
126 ] {
127 let actual: InputValue = BigDecimal::from_str(raw).unwrap().to_input_value();
128
129 assert_eq!(actual, graphql_input_value!((raw)), "on value: {raw}");
130 }
131 }
132}