From 2cb96d0fc4386b1ec5de8912cb0df0f271fa7cf8 Mon Sep 17 00:00:00 2001
From: Caio <c410.f3r@gmail.com>
Date: Thu, 21 May 2020 05:13:31 -0300
Subject: [PATCH] impl GraphQLScalar for NaiveTime (#657)

* impl GraphQLScalar for NaiveTime

* Add feature
---
 juniper/Cargo.toml                 |  1 +
 juniper/src/integrations/chrono.rs | 75 ++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 202b03c0..69e970a6 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -31,6 +31,7 @@ default = [
     "url",
     "uuid",
 ]
+scalar-naivetime = []
 
 [dependencies]
 juniper_codegen = { version = "0.14.2", path = "../juniper_codegen"  }
diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs
index 58696754..0d5a1696 100644
--- a/juniper/src/integrations/chrono.rs
+++ b/juniper/src/integrations/chrono.rs
@@ -11,6 +11,8 @@
 |                         |                        | precise enough for nanoseconds.           |
 |                         |                        | Values will be truncated to microsecond   |
 |                         |                        | resolution.                               |
+| `NaiveTime`             | H:M:S                  | Optional. Use the `scalar-naivetime`      |
+|                         |                        | feature.                                  |
 
 */
 #![allow(clippy::needless_lifetimes)]
@@ -99,6 +101,30 @@ where
     }
 }
 
+#[cfg(feature = "scalar-naivetime")]
+#[crate::graphql_scalar_internal(description = "NaiveTime")]
+impl<S> GraphQLScalar for NaiveTime
+where
+    S: ScalarValue,
+{
+    fn resolve(&self) -> Value {
+        Value::scalar(self.format("%H:%M:%S").to_string())
+    }
+
+    fn from_input_value(v: &InputValue) -> Option<NaiveTime> {
+        v.as_string_value()
+            .and_then(|s| NaiveTime::parse_from_str(s, "%H:%M:%S").ok())
+    }
+
+    fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
+        if let ScalarToken::String(value) = value {
+            Ok(S::from(value.to_owned()))
+        } else {
+            Err(ParseError::UnexpectedToken(Token::Scalar(value)))
+        }
+    }
+}
+
 // JSON numbers (i.e. IEEE doubles) are not precise enough for nanosecond
 // datetimes. Values will be truncated to microsecond resolution.
 #[crate::graphql_scalar_internal(description = "NaiveDateTime")]
@@ -194,6 +220,20 @@ mod test {
         assert_eq!(parsed.day(), d);
     }
 
+    #[test]
+    #[cfg(feature = "scalar-naivetime")]
+    fn naivetime_from_input_value() {
+        let input: crate::InputValue<DefaultScalarValue>;
+        input = InputValue::scalar("21:12:19".to_string());
+        let [h, m, s] = [21, 12, 19];
+        let parsed: NaiveTime = crate::FromInputValue::from_input_value(&input).unwrap();
+        let expected = NaiveTime::from_hms(h, m, s);
+        assert_eq!(parsed, expected);
+        assert_eq!(parsed.hour(), h);
+        assert_eq!(parsed.minute(), m);
+        assert_eq!(parsed.second(), s);
+    }
+
     #[test]
     fn naivedatetime_from_input_value() {
         let raw = 1_000_000_000_f64;
@@ -223,6 +263,27 @@ mod integration_test {
         struct Root;
 
         #[crate::graphql_object_internal]
+        #[cfg(feature = "scalar-naivetime")]
+        impl Root {
+            fn exampleNaiveDate() -> NaiveDate {
+                NaiveDate::from_ymd(2015, 3, 14)
+            }
+            fn exampleNaiveDateTime() -> NaiveDateTime {
+                NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11)
+            }
+            fn exampleNaiveTime() -> NaiveTime {
+                NaiveTime::from_hms(16, 7, 8)
+            }
+            fn exampleDateTimeFixedOffset() -> DateTime<FixedOffset> {
+                DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00").unwrap()
+            }
+            fn exampleDateTimeUtc() -> DateTime<Utc> {
+                Utc.timestamp(61, 0)
+            }
+        }
+
+        #[crate::graphql_object_internal]
+        #[cfg(not(feature = "scalar-naivetime"))]
         impl Root {
             fn exampleNaiveDate() -> NaiveDate {
                 NaiveDate::from_ymd(2015, 3, 14)
@@ -238,6 +299,18 @@ mod integration_test {
             }
         }
 
+        #[cfg(feature = "scalar-naivetime")]
+        let doc = r#"
+        {
+            exampleNaiveDate,
+            exampleNaiveDateTime,
+            exampleNaiveTime,
+            exampleDateTimeFixedOffset,
+            exampleDateTimeUtc,
+        }
+        "#;
+
+        #[cfg(not(feature = "scalar-naivetime"))]
         let doc = r#"
         {
             exampleNaiveDate,
@@ -265,6 +338,8 @@ mod integration_test {
                 vec![
                     ("exampleNaiveDate", Value::scalar("2015-03-14")),
                     ("exampleNaiveDateTime", Value::scalar(1_467_969_011.0)),
+                    #[cfg(feature = "scalar-naivetime")]
+                    ("exampleNaiveTime", Value::scalar("16:07:08")),
                     (
                         "exampleDateTimeFixedOffset",
                         Value::scalar("1996-12-19T16:39:57-08:00"),