chrono feature: Implement GraphQLType for chrono types
Fixes https://github.com/graphql-rust/juniper/issues/87
This commit is contained in:
parent
0f9276b5f1
commit
2a2534525d
6 changed files with 179 additions and 5 deletions
|
@ -22,9 +22,10 @@ path = "benches/bench.rs"
|
|||
[features]
|
||||
nightly = []
|
||||
expose-test-schema = []
|
||||
default = ["url", "uuid"]
|
||||
default = ["chrono", "url", "uuid"]
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "^0.4.0", optional = true }
|
||||
ordermap = { version = "^0.2.11", features = ["serde-1"] }
|
||||
serde = { version = "^1.0.8" }
|
||||
serde_derive = {version="^1.0.8" }
|
||||
|
|
|
@ -338,6 +338,14 @@ impl InputValue {
|
|||
}
|
||||
}
|
||||
|
||||
/// View the underlying float value, if present.
|
||||
pub fn as_float_value(&self) -> Option<f64> {
|
||||
match *self {
|
||||
InputValue::Float(f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// View the underlying string value, if present.
|
||||
pub fn as_string_value(&self) -> Option<&str> {
|
||||
match *self {
|
||||
|
|
140
juniper/src/integrations/chrono.rs
Normal file
140
juniper/src/integrations/chrono.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*!
|
||||
|
||||
# Supported types
|
||||
|
||||
| Rust Type | JSON Serialization | Notes |
|
||||
|-------------------------|------------------------|-------------------------------------------|
|
||||
| `DateTime<FixedOffset>` | RFC3339 string | |
|
||||
| `DateTime<Utc>` | RFC3339 string | |
|
||||
| `NaiveDate` | RFC3339 string | |
|
||||
| `NaiveDateTime` | float (unix timestamp) | JSON numbers (i.e. IEEE doubles) are not |
|
||||
| | | precise enough for nanoseconds. |
|
||||
| | | Values will be truncated to microsecond |
|
||||
| | | resolution. |
|
||||
|
||||
*/
|
||||
use chrono::prelude::*;
|
||||
|
||||
use ::Value;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub static RFC3339_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S%z";
|
||||
|
||||
graphql_scalar!(DateTime<FixedOffset> {
|
||||
description: "DateTime"
|
||||
|
||||
resolve(&self) -> Value {
|
||||
Value::string(self.to_rfc3339())
|
||||
}
|
||||
|
||||
from_input_value(v: &InputValue) -> Option<DateTime<FixedOffset>> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| DateTime::parse_from_rfc3339(s).ok())
|
||||
}
|
||||
});
|
||||
|
||||
graphql_scalar!(DateTime<Utc> {
|
||||
description: "DateTime"
|
||||
|
||||
resolve(&self) -> Value {
|
||||
Value::string(self.to_rfc3339())
|
||||
}
|
||||
|
||||
from_input_value(v: &InputValue) -> Option<DateTime<Utc>> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| (s.parse::<DateTime<Utc>>().ok()))
|
||||
}
|
||||
});
|
||||
|
||||
// Don't use `Date` as the docs say:
|
||||
// "[Date] should be considered ambiguous at best, due to the "
|
||||
// inherent lack of precision required for the time zone resolution.
|
||||
// For serialization and deserialization uses, it is best to use
|
||||
// `NaiveDate` instead."
|
||||
graphql_scalar!(NaiveDate {
|
||||
description: "NaiveDate"
|
||||
|
||||
resolve(&self) -> Value {
|
||||
Value::string(self.format(RFC3339_FORMAT).to_string())
|
||||
}
|
||||
|
||||
from_input_value(v: &InputValue) -> Option<NaiveDate> {
|
||||
v.as_string_value()
|
||||
.and_then(|s| NaiveDate::parse_from_str(s, RFC3339_FORMAT).ok())
|
||||
}
|
||||
});
|
||||
|
||||
/// JSON numbers (i.e. IEEE doubles) are not precise enough for nanosecond
|
||||
/// datetimes. Values will be truncated to microsecond resolution.
|
||||
graphql_scalar!(NaiveDateTime {
|
||||
description: "NaiveDateTime"
|
||||
|
||||
resolve(&self) -> Value {
|
||||
Value::float(self.timestamp() as f64)
|
||||
}
|
||||
|
||||
from_input_value(v: &InputValue) -> Option<NaiveDateTime> {
|
||||
v.as_float_value()
|
||||
.and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0))
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use chrono::prelude::*;
|
||||
use super::RFC3339_FORMAT;
|
||||
|
||||
#[test]
|
||||
fn datetime_fixedoffset_from_input_value() {
|
||||
let raw = "2014-11-28T21:00:09+09:00";
|
||||
let input = ::InputValue::String(raw.to_string());
|
||||
|
||||
let parsed: DateTime<FixedOffset> = ::FromInputValue::from_input_value(&input).unwrap();
|
||||
let expected = DateTime::parse_from_rfc3339(raw).unwrap();
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn datetime_utc_from_input_value() {
|
||||
let raw = "2014-11-28T21:00:09+09:00";
|
||||
let input = ::InputValue::String(raw.to_string());
|
||||
|
||||
let parsed: DateTime<Utc> = ::FromInputValue::from_input_value(&input).unwrap();
|
||||
let expected = DateTime::parse_from_rfc3339(raw).unwrap().with_timezone(&Utc);
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naivedate_from_input_value() {
|
||||
let raw = "1996-12-19T16:39:57-08:00";
|
||||
let input = ::InputValue::String(raw.to_string());
|
||||
|
||||
let parsed: NaiveDate = ::FromInputValue::from_input_value(&input).unwrap();
|
||||
let expected = NaiveDate::parse_from_str(raw, &RFC3339_FORMAT).unwrap();
|
||||
let expected_via_datetime = DateTime::parse_from_rfc3339(raw).unwrap().date().naive_utc();
|
||||
let expected_via_ymd = NaiveDate::from_ymd(1996, 12, 19);
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
assert_eq!(parsed, expected_via_datetime);
|
||||
assert_eq!(parsed, expected_via_ymd);
|
||||
|
||||
assert_eq!(parsed.year(), 1996);
|
||||
assert_eq!(parsed.month(), 12);
|
||||
assert_eq!(parsed.day(), 19);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naivedatetime_from_input_value() {
|
||||
let raw = 1_000_000_000_f64;
|
||||
let input = ::InputValue::Float(raw);
|
||||
|
||||
let parsed: NaiveDateTime = ::FromInputValue::from_input_value(&input).unwrap();
|
||||
let expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap();
|
||||
|
||||
assert_eq!(parsed, expected);
|
||||
assert_eq!(raw, expected.timestamp() as f64);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
#[doc(hidden)]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
/// GraphQL support for [chrono](https://github.com/chronotope/chrono) types.
|
||||
pub mod chrono;
|
||||
|
||||
#[cfg(feature = "url")]
|
||||
mod url;
|
||||
/// GraphQL support for [url](https://github.com/servo/rust-url) types.
|
||||
pub mod url;
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
mod uuid;
|
||||
/// GraphQL support for [uuid](https://doc.rust-lang.org/uuid/uuid/struct.Uuid.html) types.
|
||||
pub mod uuid;
|
||||
|
|
|
@ -95,6 +95,10 @@ Adding per type, field, and argument documentation is possible directly from
|
|||
this macro. For more in-depth information on how to expose fields and types, see
|
||||
the [`graphql_object!`][3] macro.
|
||||
|
||||
### Built-in object type integrations
|
||||
|
||||
Juniper has [built-in integrations][object_integrations] for converting existing object types to
|
||||
GraphQL objects for popular crates.
|
||||
|
||||
## Integrating with web servers
|
||||
|
||||
|
@ -108,6 +112,7 @@ To support this, Juniper offers additional crates that integrate with popular we
|
|||
[3]: macro.graphql_object!.html
|
||||
[Iron]: http://ironframework.io
|
||||
[Rocket]: https://rocket.rs
|
||||
[object_integrations]: integrations/index.html
|
||||
|
||||
*/
|
||||
#![cfg_attr(feature = "nightly", feature(test))]
|
||||
|
@ -124,6 +129,9 @@ extern crate serde_json;
|
|||
|
||||
extern crate ordermap;
|
||||
|
||||
#[cfg(any(test, feature = "chrono"))]
|
||||
extern crate chrono;
|
||||
|
||||
#[cfg(any(test, feature = "url"))]
|
||||
extern crate url;
|
||||
|
||||
|
@ -141,7 +149,9 @@ mod types;
|
|||
mod schema;
|
||||
mod validation;
|
||||
mod executor;
|
||||
mod integrations;
|
||||
// This needs to be public until docs have support for private modules:
|
||||
// https://github.com/rust-lang/cargo/issues/1520
|
||||
pub mod integrations;
|
||||
pub mod graphiql;
|
||||
pub mod http;
|
||||
#[macro_use]
|
||||
|
|
|
@ -76,6 +76,14 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
/// View the underlying float value, if present.
|
||||
pub fn as_float_value(&self) -> Option<&f64> {
|
||||
match *self {
|
||||
Value::Float(ref f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// View the underlying object value, if present.
|
||||
pub fn as_object_value(&self) -> Option<&OrderMap<String, Value>> {
|
||||
match *self {
|
||||
|
@ -179,7 +187,7 @@ impl<T> From<Option<T>> for Value where Value: From<T> {
|
|||
/// passed in.
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate juniper;
|
||||
///
|
||||
///
|
||||
/// graphql_value!(1234);
|
||||
/// graphql_value!("test");
|
||||
/// graphql_value!([ 1234, "test", true ]);
|
||||
|
|
Loading…
Reference in a new issue