diff --git a/async-graphql-parser/src/query_parser.rs b/async-graphql-parser/src/query_parser.rs index 4520252e..25f97840 100644 --- a/async-graphql-parser/src/query_parser.rs +++ b/async-graphql-parser/src/query_parser.rs @@ -6,7 +6,6 @@ use crate::{Error, Result}; use pest::iterators::Pair; use pest::Parser; use std::collections::BTreeMap; -use std::num::{ParseFloatError, ParseIntError}; #[derive(Parser)] #[grammar = "query.pest"] @@ -242,24 +241,17 @@ fn parse_value2(pair: Pair, pc: &mut PositionCalculator) -> Result Rule::object => parse_object_value(pair, pc)?, Rule::array => parse_array_value(pair, pc)?, Rule::variable => Value::Variable(parse_variable(pair, pc)?.into_inner()), - Rule::float => { + Rule::float | Rule::int => { let pos = pc.step(&pair); - Value::Float( + Value::Number( pair.as_str() .parse() - .map_err(|err: ParseFloatError| Error { + .map_err(|err: serde_json::Error| Error { pos, message: err.to_string(), })?, ) } - Rule::int => { - let pos = pc.step(&pair); - Value::Int(pair.as_str().parse().map_err(|err: ParseIntError| Error { - pos, - message: err.to_string(), - })?) - } Rule::string => Value::String({ let pos = pc.step(&pair); unquote_string(pair.as_str(), pos)? @@ -531,18 +523,8 @@ mod tests { #[test] fn test_parse_overflowing_int() { let query_ok = format!("mutation {{ add(big: {}) }} ", std::i32::MAX); - let query_overflow = format!("mutation {{ add(big: {}0) }} ", std::i32::MAX); + let query_overflow = format!("mutation {{ add(big: {}0000) }} ", std::i32::MAX); assert!(parse_query(query_ok).is_ok()); - assert!(parse_query(query_overflow).is_err()); - } - - #[test] - fn test_parse_overflowing_float() { - let query_ok = format!("mutation {{ add(big: {:.1}) }} ", std::f64::MAX); - let query_overflow = format!("mutation {{ add(big: 1{:.1}) }} ", std::f64::MAX); - assert!(parse_query(query_ok).is_ok()); - - // NOTE: This is also ok since overflow gets parsed to infinity. assert!(parse_query(query_overflow).is_ok()); } } diff --git a/async-graphql-parser/src/value.rs b/async-graphql-parser/src/value.rs index 9fe38432..3a58be66 100644 --- a/async-graphql-parser/src/value.rs +++ b/async-graphql-parser/src/value.rs @@ -33,8 +33,7 @@ impl Clone for UploadValue { pub enum Value { Null, Variable(String), - Int(i32), - Float(f64), + Number(serde_json::Number), String(String), Boolean(bool), Enum(String), @@ -51,8 +50,7 @@ impl serde::Serialize for Value { match self { Value::Null => serializer.serialize_none(), Value::Variable(variable) => serializer.serialize_str(&format!("${}", variable)), - Value::Int(value) => serializer.serialize_i32(*value), - Value::Float(value) => serializer.serialize_f64(*value), + Value::Number(value) => value.serialize(serializer), Value::String(value) => serializer.serialize_str(value), Value::Boolean(value) => serializer.serialize_bool(*value), Value::Enum(value) => serializer.serialize_str(value), @@ -87,8 +85,7 @@ impl PartialEq for Value { match (self, other) { (Variable(a), Variable(b)) => a.eq(b), - (Int(a), Int(b)) => a.eq(b), - (Float(a), Float(b)) => a.eq(b), + (Number(a), Number(b)) => a.eq(b), (String(a), String(b)) => a.eq(b), (Boolean(a), Boolean(b)) => a.eq(b), (Null, Null) => true, @@ -145,8 +142,7 @@ impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Value::Variable(name) => write!(f, "${}", name), - Value::Int(num) => write!(f, "{}", *num), - Value::Float(val) => write!(f, "{}", *val), + Value::Number(num) => write!(f, "{}", *num), Value::String(ref val) => write_quoted(val, f), Value::Boolean(true) => write!(f, "true"), Value::Boolean(false) => write!(f, "false"), @@ -188,8 +184,7 @@ impl From for serde_json::Value { match value { Value::Null => serde_json::Value::Null, Value::Variable(name) => name.into(), - Value::Int(n) => n.into(), - Value::Float(n) => n.into(), + Value::Number(n) => serde_json::Value::Number(n), Value::String(s) => s.into(), Value::Boolean(v) => v.into(), Value::Enum(e) => e.into(), @@ -213,8 +208,7 @@ impl From for Value { match value { serde_json::Value::Null => Value::Null, serde_json::Value::Bool(n) => Value::Boolean(n), - serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()), - serde_json::Value::Number(n) => Value::Int(n.as_i64().unwrap() as i32), + serde_json::Value::Number(n) => Value::Number(n), serde_json::Value::String(s) => Value::String(s), serde_json::Value::Array(ls) => Value::List(ls.into_iter().map(Into::into).collect()), serde_json::Value::Object(obj) => Value::Object( diff --git a/docs/en/src/custom_scalars.md b/docs/en/src/custom_scalars.md index e041e89a..f5fe85fe 100644 --- a/docs/en/src/custom_scalars.md +++ b/docs/en/src/custom_scalars.md @@ -15,12 +15,12 @@ struct StringNumber(i64); #[Scalar] impl ScalarType for StringNumber { fn parse(value: Value) -> InputValueResult { - if let Value::String(value) = value { + if let Value::String(value) = &value { // Parse the integer value - value.parse().map(StringNumber)? + Ok(value.parse().map(StringNumber)?) } else { // If the type does not match - InputValueError::ExpectedType + Err(InputValueError::ExpectedType(value)) } } diff --git a/docs/zh-CN/src/custom_scalars.md b/docs/zh-CN/src/custom_scalars.md index 19b71c1a..2c7e4ca4 100644 --- a/docs/zh-CN/src/custom_scalars.md +++ b/docs/zh-CN/src/custom_scalars.md @@ -15,12 +15,12 @@ struct StringNumber(i64); #[Scalar] impl ScalarType for StringNumber { fn parse(value: Value) -> InputValueResult { - if let Value::String(value) = value { + if let Value::String(value) = &value { // 解析整数 - value.parse().map(StringNumber)? + Ok(value.parse().map(StringNumber)?) } else { // 类型不匹配 - InputValueError::ExpectedType + Err(InputValueError::ExpectedType(value)) } } diff --git a/src/base.rs b/src/base.rs index 88b4200a..7c3690d7 100644 --- a/src/base.rs +++ b/src/base.rs @@ -116,15 +116,16 @@ pub trait InputObjectType: InputValueType {} /// #[Scalar] /// impl ScalarType for MyInt { /// fn parse(value: Value) -> InputValueResult { -/// if let Value::Int(n) = value { -/// Ok(MyInt(n as i32)) -/// } else { -/// Err(InputValueError::ExpectedType(value)) +/// if let Value::Number(n) = &value { +/// if let Some(n) = n.as_i64() { +/// return Ok(MyInt(n as i32)); +/// } /// } +/// Err(InputValueError::ExpectedType(value)) /// } /// /// fn to_value(&self) -> Value { -/// Value::Int(self.0) +/// Value::Number(self.0.into()) /// } /// } /// ``` diff --git a/src/scalars/any.rs b/src/scalars/any.rs index 31d78be9..3900da5b 100644 --- a/src/scalars/any.rs +++ b/src/scalars/any.rs @@ -46,7 +46,11 @@ mod test { #[test] fn test_conversion_ok() { - let value = Value::List(vec![Value::Int(1.into()), Value::Float(2.0), Value::Null]); + let value = Value::List(vec![ + Value::Number(1.into()), + Value::Boolean(true), + Value::Null, + ]); let expected = Any(value.clone()); let output: Any = value.into(); assert_eq!(output, expected); diff --git a/src/scalars/floats.rs b/src/scalars/floats.rs index a905e694..04adacc1 100644 --- a/src/scalars/floats.rs +++ b/src/scalars/floats.rs @@ -1,48 +1,35 @@ use crate::{InputValueError, InputValueResult, ScalarType, Value}; use async_graphql_derive::Scalar; -/// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). -#[Scalar(internal, name = "Float")] -impl ScalarType for f32 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - Value::Float(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } +macro_rules! float_scalar { + ($($ty:ty),*) => { + $( + /// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). + #[Scalar(internal, name = "Float")] + impl ScalarType for $ty { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => Ok(n + .as_f64() + .ok_or_else(|| InputValueError::from("Invalid number"))? + as Self), + _ => Err(InputValueError::ExpectedType(value)), + } + } - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) | Value::Float(_) => true, - _ => false, - } - } + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(_) => true, + _ => false, + } + } - fn to_value(&self) -> Value { - Value::Float(*self as f64) + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from_f64(*self as f64).unwrap()) + } + } + )* } } -/// The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point). -#[Scalar(internal, name = "Float")] -impl ScalarType for f64 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - Value::Float(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) | Value::Float(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Float(*self as f64) - } -} +float_scalar!(f32, f64); diff --git a/src/scalars/id.rs b/src/scalars/id.rs index 3da422a9..e70c9a98 100644 --- a/src/scalars/id.rs +++ b/src/scalars/id.rs @@ -77,7 +77,7 @@ impl PartialEq<&str> for ID { impl ScalarType for ID { fn parse(value: Value) -> InputValueResult { match value { - Value::Int(n) => Ok(ID(n.to_string())), + Value::Number(n) if n.is_i64() => Ok(ID(n.to_string())), Value::String(s) => Ok(ID(s)), _ => Err(InputValueError::ExpectedType(value)), } @@ -85,7 +85,8 @@ impl ScalarType for ID { fn is_valid(value: &Value) -> bool { match value { - Value::Int(_) | Value::String(_) => true, + Value::Number(n) if n.is_i64() => true, + Value::String(_) => true, _ => false, } } diff --git a/src/scalars/integers.rs b/src/scalars/integers.rs index 72820db1..13d2dc61 100644 --- a/src/scalars/integers.rs +++ b/src/scalars/integers.rs @@ -1,191 +1,85 @@ use crate::{InputValueError, InputValueResult, ScalarType, Value}; use async_graphql_derive::Scalar; -/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -#[Scalar(internal, name = "Int")] -impl ScalarType for i8 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Int(*self as i32) - } -} - -/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -#[Scalar(internal, name = "Int")] -impl ScalarType for i16 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Int(*self as i32) - } -} - -/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -#[Scalar(internal, name = "Int")] -impl ScalarType for i32 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Int(*self as i32) - } -} - -/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -#[Scalar(internal, name = "Int")] -impl ScalarType for u8 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Int(*self as i32) - } -} - -/// The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. -#[Scalar(internal, name = "Int")] -impl ScalarType for u16 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::Int(*self as i32) - } -} - -/// The `Int64` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^64) and 2^64 - 1. -#[Scalar(internal, name = "Int64")] -impl ScalarType for i64 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => Ok(n as Self), - Value::String(s) => Ok(s.parse()?), - _ => Err(InputValueError::ExpectedType(value)), - } - } - - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) | Value::String(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::String(self.to_string()) - } -} - -/// The `UInt64` scalar type represents non-fractional signed whole numeric values. Int can represent values between 0 and 2^64. -#[Scalar(internal, name = "UInt64")] -impl ScalarType for u32 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => { - if n < 0 { - return Err(InputValueError::Custom("Expect a positive number.".into())); +macro_rules! int_scalar { + ($($ty:ty),*) => { + $( + /// The `Int` scalar type represents non-fractional whole numeric values. + #[Scalar(internal, name = "Int")] + impl ScalarType for $ty { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_i64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n < Self::MIN as i64 || n > Self::MAX as i64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + Self::MIN, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), } - Ok(n as Self) } - Value::String(s) => Ok(s.parse()?), - _ => Err(InputValueError::ExpectedType(value)), - } - } - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) | Value::String(_) => true, - _ => false, - } - } - - fn to_value(&self) -> Value { - Value::String(self.to_string()) - } -} - -/// The `UInt64` scalar type represents non-fractional signed whole numeric values. Int can represent values between 0 and 2^64. -#[Scalar(internal, name = "UInt64")] -impl ScalarType for u64 { - fn parse(value: Value) -> InputValueResult { - match value { - Value::Int(n) => { - if n < 0 { - return Err(InputValueError::Custom("Expect a positive number.".into())); + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_i64() => true, + _ => false, } - Ok(n as Self) } - Value::String(s) => Ok(s.parse()?), - _ => Err(InputValueError::ExpectedType(value)), - } - } - fn is_valid(value: &Value) -> bool { - match value { - Value::Int(_) | Value::String(_) => true, - _ => false, + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as i64)) + } } - } - - fn to_value(&self) -> Value { - Value::String(self.to_string()) - } + )* + }; } + +macro_rules! uint_scalar { + ($($ty:ty),*) => { + $( + /// The `Int` scalar type represents non-fractional whole numeric values. + #[Scalar(internal, name = "Int")] + impl ScalarType for $ty { + fn parse(value: Value) -> InputValueResult { + match value { + Value::Number(n) => { + let n = n + .as_u64() + .ok_or_else(|| InputValueError::from("Invalid number"))?; + if n > Self::MAX as u64 { + return Err(InputValueError::from(format!( + "Only integers from {} to {} are accepted.", + 0, + Self::MAX + ))); + } + Ok(n as Self) + } + _ => Err(InputValueError::ExpectedType(value)), + } + } + + fn is_valid(value: &Value) -> bool { + match value { + Value::Number(n) if n.is_u64() => true, + _ => false, + } + } + + fn to_value(&self) -> Value { + Value::Number(serde_json::Number::from(*self as u64)) + } + } + )* + }; +} + +int_scalar!(i8, i16, i32, i64); +uint_scalar!(u8, u16, u32, u64); diff --git a/src/validators/int_validators.rs b/src/validators/int_validators.rs index 2ce367df..762fb0d4 100644 --- a/src/validators/int_validators.rs +++ b/src/validators/int_validators.rs @@ -4,72 +4,69 @@ use crate::Value; /// Integer range validator pub struct IntRange { /// Minimum value, including this value. - pub min: i32, + pub min: i64, /// Maximum value, including this value. - pub max: i32, + pub max: i64, } impl InputValueValidator for IntRange { fn is_valid(&self, value: &Value) -> Option { - if let Value::Int(n) = value { - if *n < self.min || *n > self.max { - Some(format!( - "the value is {}, must be between {} and {}", - *n, self.min, self.max - )) - } else { - None + if let Value::Number(n) = value { + if let Some(n) = n.as_i64() { + if n < self.min || n > self.max { + return Some(format!( + "the value is {}, must be between {} and {}", + n, self.min, self.max + )); + } } - } else { - None } + None } } /// Integer less then validator pub struct IntLessThan { /// Less then this value. - pub value: i32, + pub value: i64, } impl InputValueValidator for IntLessThan { fn is_valid(&self, value: &Value) -> Option { - if let Value::Int(n) = value { - if *n >= self.value { - Some(format!( - "the value is {}, must be less than {}", - *n, self.value - )) - } else { - None + if let Value::Number(n) = value { + if let Some(n) = n.as_i64() { + if n >= self.value { + return Some(format!( + "the value is {}, must be less than {}", + n, self.value + )); + } } - } else { - None } + None } } /// Integer greater then validator pub struct IntGreaterThan { /// Greater then this value. - pub value: i32, + pub value: i64, } impl InputValueValidator for IntGreaterThan { fn is_valid(&self, value: &Value) -> Option { - if let Value::Int(n) = value { - if *n <= self.value { - Some(format!( - "the value is {}, must be greater than {}", - *n, self.value - )) - } else { - None + if let Value::Number(n) = value { + if let Some(n) = n.as_i64() { + if n <= self.value { + return Some(format!( + "the value is {}, must be greater than {}", + n, self.value + )); + } } - } else { - None } + None } } @@ -78,37 +75,35 @@ pub struct IntNonZero {} impl InputValueValidator for IntNonZero { fn is_valid(&self, value: &Value) -> Option { - if let Value::Int(n) = value { - if *n == 0 { - Some(format!("the value is {}, must be nonzero", *n,)) - } else { - None + if let Value::Number(n) = value { + if let Some(n) = n.as_i64() { + if n == 0 { + return Some(format!("the value is {}, must be nonzero", n)); + } } - } else { - None } + None } } /// Integer equal validator pub struct IntEqual { /// equal this value. - pub value: i32, + pub value: i64, } impl InputValueValidator for IntEqual { fn is_valid(&self, value: &Value) -> Option { - if let Value::Int(n) = value { - if *n != self.value { - Some(format!( - "the value is {}, must be equal to {}", - *n, self.value - )) - } else { - None + if let Value::Number(n) = value { + if let Some(n) = n.as_i64() { + if n != self.value { + return Some(format!( + "the value is {}, must be equal to {}", + n, self.value + )); + } } - } else { - None } + None } } diff --git a/tests/input_value.rs b/tests/input_value.rs index dc38c4c7..bd5e09a3 100644 --- a/tests/input_value.rs +++ b/tests/input_value.rs @@ -6,13 +6,13 @@ pub async fn test_input_value_custom_error() { #[Object] impl Query { - async fn parse_int(&self, _n: i64) -> bool { + async fn parse_int(&self, _n: i8) -> bool { true } } let schema = Schema::new(Query, EmptyMutation, EmptySubscription); - let query = r#"{ parseInt(n:"A") }"#; + let query = r#"{ parseInt(n:289) }"#; assert_eq!( schema.execute(&query).await.unwrap_err(), Error::Query { @@ -22,7 +22,7 @@ pub async fn test_input_value_custom_error() { }, path: None, err: QueryError::ParseInputValue { - reason: "invalid digit found in string".to_string() + reason: "Only integers from -128 to 127 are accepted.".to_string() }, } ); diff --git a/tests/introspection.rs b/tests/introspection.rs index f861bea1..d0900da7 100644 --- a/tests/introspection.rs +++ b/tests/introspection.rs @@ -84,7 +84,7 @@ impl ScalarType for TestScalar { } fn to_value(&self) -> Value { - Value::Int(self.0.clone()) + Value::Number(self.0.into()) } }