Implement `InputType` and `OutputType` for `Box<[T]>` and `Arc<[T]>`. [#805](https://github.com/async-graphql/async-graphql/issues/805)

This commit is contained in:
Sunli 2022-01-31 20:01:29 +08:00
parent 5563aafacd
commit bf05607e84
3 changed files with 115 additions and 1 deletions

View File

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# [3.0.28] 2022-1-30
- Implement `InputType` and `OutputType` for `Box<[T]>` and `Arc<[T]>`. [#805](https://github.com/async-graphql/async-graphql/issues/805)
# [3.0.27] 2022-1-28
- Fix possible stack overflow in validator, thanks @quapka.

View File

@ -1,8 +1,12 @@
use std::borrow::Cow;
use std::sync::Arc;
use crate::parser::types::Field;
use crate::resolver_utils::resolve_list;
use crate::{registry, ContextSelectionSet, OutputType, Positioned, ServerResult, Value};
use crate::{
registry, ContextSelectionSet, InputType, InputValueError, InputValueResult, OutputType,
Positioned, ServerResult, Value,
};
#[async_trait::async_trait]
impl<'a, T: OutputType + 'a> OutputType for &'a [T] {
@ -27,3 +31,83 @@ impl<'a, T: OutputType + 'a> OutputType for &'a [T] {
resolve_list(ctx, field, self.iter(), Some(self.len())).await
}
}
macro_rules! impl_output_slice_for_smart_ptr {
($ty:ty) => {
#[async_trait::async_trait]
impl<T: OutputType> OutputType for $ty {
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("[{}]", T::qualified_type_name()))
}
fn qualified_type_name() -> String {
format!("[{}]!", T::qualified_type_name())
}
fn create_type_info(registry: &mut registry::Registry) -> String {
T::create_type_info(registry);
Self::qualified_type_name()
}
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
field: &Positioned<Field>,
) -> ServerResult<Value> {
resolve_list(ctx, field, self.iter(), Some(self.len())).await
}
}
};
}
impl_output_slice_for_smart_ptr!(Box<[T]>);
impl_output_slice_for_smart_ptr!(Arc<[T]>);
macro_rules! impl_input_slice_for_smart_ptr {
($ty:ty) => {
impl<T: InputType> InputType for $ty {
type RawValueType = Self;
fn type_name() -> Cow<'static, str> {
Cow::Owned(format!("[{}]", T::qualified_type_name()))
}
fn qualified_type_name() -> String {
format!("[{}]!", T::qualified_type_name())
}
fn create_type_info(registry: &mut registry::Registry) -> String {
T::create_type_info(registry);
Self::qualified_type_name()
}
fn parse(value: Option<Value>) -> InputValueResult<Self> {
match value.unwrap_or_default() {
Value::List(values) => values
.into_iter()
.map(|value| InputType::parse(Some(value)))
.collect::<Result<_, _>>()
.map_err(InputValueError::propagate),
value => {
Ok(
vec![InputType::parse(Some(value))
.map_err(InputValueError::propagate)?]
.into(),
)
}
}
}
fn to_value(&self) -> Value {
Value::List(self.iter().map(InputType::to_value).collect())
}
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
Some(self)
}
}
};
}
impl_input_slice_for_smart_ptr!(Box<[T]>);
impl_input_slice_for_smart_ptr!(Arc<[T]>);

View File

@ -55,3 +55,29 @@ pub async fn test_input_box_str() {
})
);
}
#[tokio::test]
pub async fn test_input_box_slice() {
struct Query;
#[Object]
impl Query {
async fn box_slice(&self, s: Box<[i32]>) -> Box<[i32]> {
s
}
async fn arc_slice(&self, s: Arc<[i32]>) -> Arc<[i32]> {
s
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = r#"{ boxSlice(s: [1, 2, 3]) arcSlice(s: [4, 5, 6]) }"#;
assert_eq!(
schema.execute(query).await.into_result().unwrap().data,
value!({
"boxSlice": [1, 2, 3],
"arcSlice": [4, 5, 6],
})
);
}