Rework to implement `InputType` and `OutputType` for `HashMap` and `BTreeMap`.
This commit is contained in:
parent
eb27b0856e
commit
f718df1a42
|
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [3.0.13] 2021-12-06
|
||||
|
||||
- No longer assumes that a subscription stream that failed to resolve has ended. [#744](https://github.com/async-graphql/async-graphql/issues/744)
|
||||
- Rework to implement `InputType` and `OutputType` for `HashMap` and `BTreeMap`.
|
||||
|
||||
## [3.0.12] 2021-12-05
|
||||
|
||||
|
|
|
@ -1,31 +1,54 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_graphql_parser::types::Field;
|
||||
use async_graphql_parser::Positioned;
|
||||
use async_graphql_value::{from_value, to_value};
|
||||
use indexmap::IndexMap;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::registry::{MetaType, Registry};
|
||||
use crate::{
|
||||
InputType, InputValueError, InputValueResult, Name, OutputType, Scalar, ScalarType, Value,
|
||||
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
|
||||
ServerResult, Value,
|
||||
};
|
||||
|
||||
/// A scalar that can represent any JSON Object value.
|
||||
#[Scalar(internal, name = "JSONObject")]
|
||||
impl<K, T> ScalarType for BTreeMap<K, T>
|
||||
impl<K, V> InputType for BTreeMap<K, V>
|
||||
where
|
||||
K: ToString + FromStr + Ord + Sync + Send,
|
||||
K: ToString + FromStr + Ord + Send + Sync,
|
||||
K::Err: Display,
|
||||
T: OutputType + InputType,
|
||||
V: Serialize + DeserializeOwned + Send + Sync,
|
||||
{
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
type RawValueType = Self;
|
||||
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSONObject")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_input_type::<Self, _>(|_| MetaType::Scalar {
|
||||
name: <Self as InputType>::type_name().to_string(),
|
||||
description: Some("A scalar that can represent any JSON Object value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(value: Option<Value>) -> InputValueResult<Self> {
|
||||
let value = value.unwrap_or_default();
|
||||
match value {
|
||||
Value::Object(map) => map
|
||||
.into_iter()
|
||||
.map(|(name, value)| {
|
||||
Ok((
|
||||
K::from_str(&name).map_err(|err| {
|
||||
InputValueError::custom(format!("object key: {}", err))
|
||||
InputValueError::<Self>::custom(format!("object key: {}", err))
|
||||
})?,
|
||||
T::parse(Some(value))?,
|
||||
from_value(value).map_err(|err| format!("object value: {}", err))?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
|
@ -37,8 +60,51 @@ where
|
|||
fn to_value(&self) -> Value {
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(Name::new(name.to_string()), value.to_value());
|
||||
map.insert(
|
||||
Name::new(name.to_string()),
|
||||
to_value(value).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
Value::Object(map)
|
||||
}
|
||||
|
||||
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<K, V> OutputType for BTreeMap<K, V>
|
||||
where
|
||||
K: ToString + Ord + Send + Sync,
|
||||
V: Serialize + Send + Sync,
|
||||
{
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSONObject")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<Self, _>(|_| MetaType::Scalar {
|
||||
name: <Self as OutputType>::type_name().to_string(),
|
||||
description: Some("A scalar that can represent any JSON Object value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn resolve(
|
||||
&self,
|
||||
_ctx: &ContextSelectionSet<'_>,
|
||||
_field: &Positioned<Field>,
|
||||
) -> ServerResult<Value> {
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(
|
||||
Name::new(name.to_string()),
|
||||
to_value(value).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
Ok(Value::Object(map))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,55 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::hash::Hash;
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_graphql_parser::types::Field;
|
||||
use async_graphql_parser::Positioned;
|
||||
use async_graphql_value::{from_value, to_value};
|
||||
use indexmap::IndexMap;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::registry::{MetaType, Registry};
|
||||
use crate::{
|
||||
InputType, InputValueError, InputValueResult, Name, OutputType, Scalar, ScalarType, Value,
|
||||
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
|
||||
ServerResult, Value,
|
||||
};
|
||||
|
||||
/// A scalar that can represent any JSON Object value.
|
||||
#[Scalar(internal, name = "JSONObject")]
|
||||
impl<K, V> ScalarType for HashMap<K, V>
|
||||
impl<K, V> InputType for HashMap<K, V>
|
||||
where
|
||||
K: ToString + FromStr + Eq + Hash + Sync + Send,
|
||||
K: ToString + FromStr + Eq + Hash + Send + Sync,
|
||||
K::Err: Display,
|
||||
V: OutputType + InputType,
|
||||
V: Serialize + DeserializeOwned + Send + Sync,
|
||||
{
|
||||
fn parse(value: Value) -> InputValueResult<Self> {
|
||||
type RawValueType = Self;
|
||||
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSONObject")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_input_type::<Self, _>(|_| MetaType::Scalar {
|
||||
name: <Self as InputType>::type_name().to_string(),
|
||||
description: Some("A scalar that can represent any JSON Object value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(value: Option<Value>) -> InputValueResult<Self> {
|
||||
let value = value.unwrap_or_default();
|
||||
match value {
|
||||
Value::Object(map) => map
|
||||
.into_iter()
|
||||
.map(|(name, value)| {
|
||||
Ok((
|
||||
K::from_str(&name).map_err(|err| {
|
||||
InputValueError::custom(format!("object key: {}", err))
|
||||
InputValueError::<Self>::custom(format!("object key: {}", err))
|
||||
})?,
|
||||
V::parse(Some(value))?,
|
||||
from_value(value).map_err(|err| format!("object value: {}", err))?,
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
|
@ -38,8 +61,51 @@ where
|
|||
fn to_value(&self) -> Value {
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(Name::new(name.to_string()), value.to_value());
|
||||
map.insert(
|
||||
Name::new(name.to_string()),
|
||||
to_value(value).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
Value::Object(map)
|
||||
}
|
||||
|
||||
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<K, V> OutputType for HashMap<K, V>
|
||||
where
|
||||
K: ToString + Eq + Hash + Send + Sync,
|
||||
V: Serialize + Send + Sync,
|
||||
{
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("JSONObject")
|
||||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<Self, _>(|_| MetaType::Scalar {
|
||||
name: <Self as OutputType>::type_name().to_string(),
|
||||
description: Some("A scalar that can represent any JSON Object value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn resolve(
|
||||
&self,
|
||||
_ctx: &ContextSelectionSet<'_>,
|
||||
_field: &Positioned<Field>,
|
||||
) -> ServerResult<Value> {
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(
|
||||
Name::new(name.to_string()),
|
||||
to_value(value).unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
Ok(Value::Object(map))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,9 +46,9 @@ impl<T: DeserializeOwned + Serialize + Send + Sync> InputType for Json<T> {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<Json<T>, _>(|_| MetaType::Scalar {
|
||||
registry.create_input_type::<Json<T>, _>(|_| MetaType::Scalar {
|
||||
name: <Self as InputType>::type_name().to_string(),
|
||||
description: None,
|
||||
description: Some("A scalar that can represent any JSON value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
|
@ -77,7 +77,7 @@ impl<T: Serialize + Send + Sync> OutputType for Json<T> {
|
|||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<Json<T>, _>(|_| MetaType::Scalar {
|
||||
name: <Self as OutputType>::type_name().to_string(),
|
||||
description: None,
|
||||
description: Some("A scalar that can represent any JSON value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
|
@ -101,9 +101,9 @@ impl InputType for serde_json::Value {
|
|||
}
|
||||
|
||||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<serde_json::Value, _>(|_| MetaType::Scalar {
|
||||
registry.create_input_type::<serde_json::Value, _>(|_| MetaType::Scalar {
|
||||
name: <Self as InputType>::type_name().to_string(),
|
||||
description: None,
|
||||
description: Some("A scalar that can represent any JSON value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
|
@ -132,7 +132,7 @@ impl OutputType for serde_json::Value {
|
|||
fn create_type_info(registry: &mut Registry) -> String {
|
||||
registry.create_output_type::<serde_json::Value, _>(|_| MetaType::Scalar {
|
||||
name: <Self as OutputType>::type_name().to_string(),
|
||||
description: None,
|
||||
description: Some("A scalar that can represent any JSON value."),
|
||||
is_valid: |_| true,
|
||||
visible: None,
|
||||
specified_by_url: None,
|
||||
|
|
Loading…
Reference in New Issue