diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a1831ff..8ca1305a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,3 +28,14 @@ jobs: run: cargo build --all --verbose - name: Run tests run: cargo test --all --verbose + + # build on nightly + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + components: clippy, rustfmt + - name: Build on nightly without features + run: cargo build --no-default-features + - name: Build on nightly + run: cargo build --all --verbose diff --git a/derive/src/input_object.rs b/derive/src/input_object.rs index 3bf6a615..e6458201 100644 --- a/derive/src/input_object.rs +++ b/derive/src/input_object.rs @@ -75,7 +75,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult get_fields.push(quote! { let #ident: #ty = #crate_name::InputValueType::parse( Some(#crate_name::Value::Object(obj.clone())) - ).map_err(#crate_name::InputValueError::propogate)?; + ).map_err(#crate_name::InputValueError::propagate)?; }); fields.push(ident); @@ -111,7 +111,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult let #ident: #ty = { match obj.get(#name) { Some(value) => #crate_name::InputValueType::parse(Some(value.clone())) - .map_err(#crate_name::InputValueError::propogate)?, + .map_err(#crate_name::InputValueError::propagate)?, None => #default, } }; @@ -119,7 +119,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult } else { get_fields.push(quote! { let #ident: #ty = #crate_name::InputValueType::parse(obj.get(#name).cloned()) - .map_err(#crate_name::InputValueError::propogate)?; + .map_err(#crate_name::InputValueError::propagate)?; }); } diff --git a/docs/en/src/custom_scalars.md b/docs/en/src/custom_scalars.md index 7565269f..38ce23e8 100644 --- a/docs/en/src/custom_scalars.md +++ b/docs/en/src/custom_scalars.md @@ -19,7 +19,7 @@ impl ScalarType for StringNumber { Ok(value.parse().map(StringNumber)?) } else { // If the type does not match - Err(InputValueError::ExpectedType(value)) + Err(InputValueError::expected_type(value)) } } diff --git a/docs/en/src/field_guard.md b/docs/en/src/field_guard.md index 1e5bd35f..0bdb8fa6 100644 --- a/docs/en/src/field_guard.md +++ b/docs/en/src/field_guard.md @@ -31,11 +31,11 @@ This attribute support 4 operators to create complex rules : - `and` : perform a `and` operation between two rules. (If one rule return an error the `and` operator will return the error. If both rules return a error it's the first one that will be returned). -- `or` : perform a `and` operation between two rules. (If both rules return an error the error returned is the first one) +- `or` : perform a `or` operation between two rules. (If both rules return an error the error returned is the first one) -- `chain` : take a set of rules and run them until one return an error or return `Ok()` if all rules pass. +- `chain` : take a set of rules and run them until one return an error or return `Ok` if all rules pass. -- `race` : take a set of rules and run them until one return `Ok()` if they all fail it return the last error. +- `race` : take a set of rules and run them until one return `Ok` if they all fail it return the last error. ```rust #[derive(SimpleObject)] diff --git a/docs/zh-CN/src/custom_scalars.md b/docs/zh-CN/src/custom_scalars.md index 2c7e4ca4..71e15df5 100644 --- a/docs/zh-CN/src/custom_scalars.md +++ b/docs/zh-CN/src/custom_scalars.md @@ -20,7 +20,7 @@ impl ScalarType for StringNumber { Ok(value.parse().map(StringNumber)?) } else { // 类型不匹配 - Err(InputValueError::ExpectedType(value)) + Err(InputValueError::expected_type(value)) } } diff --git a/docs/zh-CN/src/field_guard.md b/docs/zh-CN/src/field_guard.md new file mode 100644 index 00000000..76a554d7 --- /dev/null +++ b/docs/zh-CN/src/field_guard.md @@ -0,0 +1,68 @@ +# 字段守卫(Field Guard) + +您可以在给`Object`的字段定义`守卫`。 这允许在运行字段的代码逻辑之前添加检查。 +义`守卫`由你预先定义的规则组成。 规则是一种实现`Guard`特质的结构。 +```rust +#[derive(Eq, PartialEq, Copy, Clone)] +enum Role { + Admin, + Guest, +} + +struct RoleGuard { + role: Role, +} + +#[async_trait::async_trait] +impl Guard for RoleGuard { + async fn check(&self, ctx: &Context<'_>) -> Result<()> { + if ctx.data_opt::() == Some(&self.role) { + Ok(()) + } else { + Err("Forbidden".into()) + } + } +} +``` + +一旦定义了规则,就可以在`guard`字段属性中使用它。 +此属性支持4个运算符创建复杂的规则: + +-`and`:在两个规则之间执行`与`运算。 (如果一个规则返回错误,则`and`运算符将返回错误。如果两个规则均返回错误,则将是第一个返回的错误)。 + +-`or`:在两个规则之间执行`或`运算。 (如果两个规则都返回错误,则返回的错误是第一个) + +-`chain`:采用一组规则并运行它们,直到返回错误或如果所有规则都通过则返回`Ok`。 + +-`race`:采用一组规则并运行它们,直到其中一个返回`Ok`。 + +```rust +#[derive(SimpleObject)] +struct Query { + #[graphql(guard(RoleGuard(role = "Role::Admin")))] + value: i32, + #[graphql(guard(and( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#) + )))] + value2: i32, + #[graphql(guard(or( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#) + )))] + value3: i32, + #[graphql(guard(chain( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#), + AgeGuard(age = r#"21"#) + )))] + value4: i32, + #[graphql(guard(race( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#), + AgeGuard(age = r#"21"#) + )))] + value5: i32, +} +``` + diff --git a/src/error.rs b/src/error.rs index 3ac86ce3..e6eef7ea 100644 --- a/src/error.rs +++ b/src/error.rs @@ -122,8 +122,8 @@ impl InputValueError { Self::new(format!(r#"Failed to parse "{}": {}"#, T::type_name(), msg)) } - /// Propogate the error message to a different type. - pub fn propogate(self) -> InputValueError { + /// Propagate the error message to a different type. + pub fn propagate(self) -> InputValueError { InputValueError::new(format!( r#"{} (occurred while parsing "{}")"#, self.message, diff --git a/src/guard.rs b/src/guard.rs index 33f423a6..1759afea 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -1,7 +1,6 @@ //! Field guards use crate::{Context, Result}; -use serde::export::PhantomData; /// Field guard /// @@ -50,37 +49,3 @@ impl Guard for Or { self.0.check(ctx).await.or(second_result) } } - -/// Field post guard -/// -/// This is a post-condition for a field that is resolved if `Ok(()` is returned, otherwise an error is returned. -/// -/// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro. -#[async_trait::async_trait] -pub trait PostGuard { - /// Check whether to allow the result of the field through. - async fn check(&self, ctx: &Context<'_>, result: &T) -> Result<()>; -} - -/// An extension trait for `PostGuard` -pub trait PostGuardExt: PostGuard + Sized { - /// Merge the two guards. - fn and>(self, other: R) -> PostAnd { - PostAnd(self, other, PhantomData) - } -} - -impl, R: Send + Sync> PostGuardExt for T {} - -/// PostGuard for [`PostGuardExt::and`](trait.PostGuardExt.html#method.and). -pub struct PostAnd, B: PostGuard>(A, B, PhantomData); - -#[async_trait::async_trait] -impl + Send + Sync, B: PostGuard + Send + Sync> PostGuard - for PostAnd -{ - async fn check(&self, ctx: &Context<'_>, result: &T) -> Result<()> { - self.0.check(ctx, result).await?; - self.1.check(ctx, result).await - } -} diff --git a/src/types/external/bson.rs b/src/types/external/bson.rs index cf96f2af..94eace3f 100644 --- a/src/types/external/bson.rs +++ b/src/types/external/bson.rs @@ -24,7 +24,7 @@ impl ScalarType for ObjectId { impl ScalarType for UtcDateTime { fn parse(value: Value) -> InputValueResult { >::parse(value) - .map_err(InputValueError::propogate) + .map_err(InputValueError::propagate) .map(UtcDateTime::from) } diff --git a/src/types/external/json_object/btreemap.rs b/src/types/external/json_object/btreemap.rs index 663563ec..2720c96c 100644 --- a/src/types/external/json_object/btreemap.rs +++ b/src/types/external/json_object/btreemap.rs @@ -16,7 +16,7 @@ where .into_iter() .map(|(name, value)| Ok((name.into_string(), T::parse(Some(value))?))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), _ => Err(InputValueError::expected_type(value)), } } diff --git a/src/types/external/json_object/hashmap.rs b/src/types/external/json_object/hashmap.rs index e992f9c0..e6463dfd 100644 --- a/src/types/external/json_object/hashmap.rs +++ b/src/types/external/json_object/hashmap.rs @@ -16,7 +16,7 @@ where .into_iter() .map(|(name, value)| Ok((name.into_string(), T::parse(Some(value))?))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), _ => Err(InputValueError::expected_type(value)), } } diff --git a/src/types/external/list/btree_set.rs b/src/types/external/list/btree_set.rs index f0b76d68..bb9b69d4 100644 --- a/src/types/external/list/btree_set.rs +++ b/src/types/external/list/btree_set.rs @@ -29,11 +29,11 @@ impl InputValueType for BTreeSet { .into_iter() .map(|value| InputValueType::parse(Some(value))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), value => Ok({ let mut result = Self::default(); result.insert( - InputValueType::parse(Some(value)).map_err(InputValueError::propogate)?, + InputValueType::parse(Some(value)).map_err(InputValueError::propagate)?, ); result }), diff --git a/src/types/external/list/hash_set.rs b/src/types/external/list/hash_set.rs index 88d547f1..c12d2ea0 100644 --- a/src/types/external/list/hash_set.rs +++ b/src/types/external/list/hash_set.rs @@ -31,11 +31,11 @@ impl InputValueType for HashSet { .into_iter() .map(|value| InputValueType::parse(Some(value))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), value => Ok({ let mut result = Self::default(); result.insert( - InputValueType::parse(Some(value)).map_err(InputValueError::propogate)?, + InputValueType::parse(Some(value)).map_err(InputValueError::propagate)?, ); result }), diff --git a/src/types/external/list/linked_list.rs b/src/types/external/list/linked_list.rs index 28ecad4f..8b2362d8 100644 --- a/src/types/external/list/linked_list.rs +++ b/src/types/external/list/linked_list.rs @@ -29,11 +29,11 @@ impl InputValueType for LinkedList { .into_iter() .map(|value| InputValueType::parse(Some(value))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), value => Ok({ let mut result = Self::default(); result.push_front( - InputValueType::parse(Some(value)).map_err(InputValueError::propogate)?, + InputValueType::parse(Some(value)).map_err(InputValueError::propagate)?, ); result }), diff --git a/src/types/external/list/vec.rs b/src/types/external/list/vec.rs index 98ff2807..710cd889 100644 --- a/src/types/external/list/vec.rs +++ b/src/types/external/list/vec.rs @@ -28,9 +28,9 @@ impl InputValueType for Vec { .into_iter() .map(|value| InputValueType::parse(Some(value))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), value => Ok(vec![ - InputValueType::parse(Some(value)).map_err(InputValueError::propogate)? + InputValueType::parse(Some(value)).map_err(InputValueError::propagate)? ]), } } diff --git a/src/types/external/list/vec_deque.rs b/src/types/external/list/vec_deque.rs index 4eb9e796..913bcadb 100644 --- a/src/types/external/list/vec_deque.rs +++ b/src/types/external/list/vec_deque.rs @@ -29,11 +29,11 @@ impl InputValueType for VecDeque { .into_iter() .map(|value| InputValueType::parse(Some(value))) .collect::>() - .map_err(InputValueError::propogate), + .map_err(InputValueError::propagate), value => Ok({ let mut result = Self::default(); result.push_back( - InputValueType::parse(Some(value)).map_err(InputValueError::propogate)?, + InputValueType::parse(Some(value)).map_err(InputValueError::propagate)?, ); result }), diff --git a/src/types/external/optional.rs b/src/types/external/optional.rs index 83cac8a9..ddac1602 100644 --- a/src/types/external/optional.rs +++ b/src/types/external/optional.rs @@ -25,7 +25,7 @@ impl InputValueType for Option { match value.unwrap_or_default() { Value::Null => Ok(None), value => Ok(Some( - T::parse(Some(value)).map_err(InputValueError::propogate)?, + T::parse(Some(value)).map_err(InputValueError::propagate)?, )), } } diff --git a/src/types/maybe_undefined.rs b/src/types/maybe_undefined.rs index 3c6bebb1..ae3b6f3c 100644 --- a/src/types/maybe_undefined.rs +++ b/src/types/maybe_undefined.rs @@ -130,7 +130,7 @@ impl InputValueType for MaybeUndefined { None => Ok(MaybeUndefined::Undefined), Some(Value::Null) => Ok(MaybeUndefined::Null), Some(value) => Ok(MaybeUndefined::Value( - T::parse(Some(value)).map_err(InputValueError::propogate)?, + T::parse(Some(value)).map_err(InputValueError::propagate)?, )), } }