Repository: graphql-rust/juniper (language: rust)

Issue:
Floating point numbers issue
Hi, I came across the issue that all numbers without decimal part are treated as i32, which is a problem for handing floating point numbers. I.e. if I need large floating point number without a decimal part it's encoded as an integer by browsers:

```
>>> JSON.stringify({"x": 2.0 ** 35})
{"x":34359738368}
```

This means that you can't decode that json as `InputValue` reliably. As it will fail with the error like in #52.

I think treating everything that is > i32::max as floating point is fine, for InputValue? Or you you see any shortcommings of this approach?


Discussion / hints:
Mhm, this is a complicated topic.

The spec clearly says that integers are int32 (http://facebook.github.io/graphql/October2016/#sec-Int).
So anything larger must be either a float or a string for the type system.

And the spec also clearly says that numbers without an exponent or decimal part are Int values, not floats.

BUT the spec also says that **raw** values should be coerced to float where appropriate (from int, by appending `.0`). We actually do this conversion already.

But the lexer will fail with int values that exceed the `i32` range.

So the lexer would have to recognize and store  larger integers.
The spec doesn't say anything explicitly about this but it makes sense to allow larger integers on the lexer side and coerce them to floats later.

(PS: personally I'd probably use strings for large numeric values and manually cast them later one. That's a safer route to go considering you can have clients in all kinds of languages).

Fix patch:
diff --git a/changelog/master.md b/changelog/master.md
index 3f74cba68..5aa87418f 100644
--- a/changelog/master.md
+++ b/changelog/master.md
@@ -6,3 +6,13 @@
   **Note:** while this is not a Rust breaking change, if you relied on the serialization format (perhaps by storing serialized data in a database or making asumptions in your client code written in another language) it could be a breaking change for your application.
 
   [#151](https://github.com/graphql-rust/juniper/pull/151)
+
+* Large integers (> signed 32bit) are now deserialized as floats. Previously,
+  they produced the "integer out of range" error. For languages that do not
+  have distinction between integer and floating point types (including
+  javascript), this means large floating point values which do not have
+  fractional part could not be decoded (because they are represented without
+  a decimal part `.0`).
+
+  [#179](https://github.com/graphql-rust/juniper/pull/179)
+
diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs
index 72b96ef89..72048c794 100644
--- a/juniper/src/integrations/serde.rs
+++ b/juniper/src/integrations/serde.rs
@@ -79,7 +79,12 @@ impl<'de> de::Deserialize<'de> for InputValue {
                 if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) {
                     Ok(InputValue::int(value as i32))
                 } else {
-                    Err(E::custom("integer out of range"))
+                    // Browser's JSON.stringify serialize all numbers having no
+                    // fractional part as integers (no decimal point), so we
+                    // must parse large integers as floating point otherwise
+                    // we would error on transferring large floating point
+                    // numbers.
+                    Ok(InputValue::float(value as f64))
                 }
             }
 
@@ -90,7 +95,12 @@ impl<'de> de::Deserialize<'de> for InputValue {
                 if value <= i32::max_value() as u64 {
                     self.visit_i64(value as i64)
                 } else {
-                    Err(E::custom("integer out of range"))
+                    // Browser's JSON.stringify serialize all numbers having no
+                    // fractional part as integers (no decimal point), so we
+                    // must parse large integers as floating point otherwise
+                    // we would error on transferring large floating point
+                    // numbers.
+                    Ok(InputValue::float(value as f64))
                 }
             }
 
@@ -247,3 +257,24 @@ impl ser::Serialize for Value {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use serde_json::from_str;
+    use ast::InputValue;
+
+    #[test]
+    fn int() {
+        assert_eq!(from_str::<InputValue>("1235").unwrap(),
+                   InputValue::int(1235));
+    }
+
+    #[test]
+    fn float() {
+        assert_eq!(from_str::<InputValue>("2.0").unwrap(),
+                   InputValue::float(2.0));
+        // large value without a decimal part is also float
+        assert_eq!(from_str::<InputValue>("123567890123").unwrap(),
+                   InputValue::float(123567890123.0));
+    }
+}


Test patch: