Merge branch 'master' into perf/schema-fnv-hash-map

This commit is contained in:
Sam Rijs 2017-10-02 19:43:49 +11:00 committed by GitHub
commit 74c5f562e3
6 changed files with 179 additions and 5 deletions

View file

@ -22,10 +22,11 @@ path = "benches/bench.rs"
[features]
nightly = []
expose-test-schema = []
default = ["url", "uuid"]
default = ["chrono", "url", "uuid"]
[dependencies]
fnv = "1.0.3"
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" }

View file

@ -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 {

View 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);
}
}

View file

@ -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;

View file

@ -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))]
@ -125,6 +130,9 @@ extern crate serde_json;
extern crate fnv;
extern crate ordermap;
#[cfg(any(test, feature = "chrono"))]
extern crate chrono;
#[cfg(any(test, feature = "url"))]
extern crate url;
@ -142,7 +150,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]

View file

@ -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 ]);