Skip to content

Commit

Permalink
Allow field guards to support paths. #536
Browse files Browse the repository at this point in the history
  • Loading branch information
sunli829 committed Jun 8, 2021
1 parent 6dcb15c commit 4c98ad2
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 129 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

## Unreleased

- Allow field guards to support paths. [#536](https://github.com/async-graphql/async-graphql/issues/536)

## [2.9.1] 2021-06-08

- Rework error propagation. [#531](https://github.com/async-graphql/async-graphql/issues/531)
Expand Down
241 changes: 114 additions & 127 deletions derive/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,148 +133,135 @@ pub fn generate_guards(
args: &Meta,
) -> GeneratorResult<Option<TokenStream>> {
match args {
Meta::List(args) => match args.path.get_ident() {
Some(ident) => match ident.to_string().as_str() {
"guard" => {
if args.nested.len() != 1 {
return Err(Error::new_spanned(
args,
"Chained rules isn't possible anymore, please use operators.",
)
.into());
}
if let NestedMeta::Meta(rule) = &args.nested[0] {
generate_guards(crate_name, rule)
} else {
Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into())
}
Meta::List(args) => match args.path.get_ident().map(ToString::to_string) {
Some(ident) if ident == "guard" => {
if args.nested.len() != 1 {
return Err(Error::new_spanned(
args,
"Chained rules isn't possible anymore, please use operators.",
)
.into());
}
"and" => {
if args.nested.len() != 2 {
return Err(Error::new_spanned(
args,
"and operator support only 2 operands.",
)
.into());
}
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::and(#first_rule, #second_rule) },
))
if let NestedMeta::Meta(rule) = &args.nested[0] {
generate_guards(crate_name, rule)
} else {
Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into())
}
"or" => {
if args.nested.len() != 2 {
return Err(Error::new_spanned(
args,
"or operator support only 2 operands.",
)
.into());
}
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::or(#first_rule, #second_rule) },
))
}
Some(ident) if ident == "and" => {
if args.nested.len() != 2 {
return Err(
Error::new_spanned(args, "and operator support only 2 operands.").into(),
);
}
"chain" => {
if args.nested.len() < 2 {
return Err(Error::new_spanned(
args,
"chain operator need at least 1 operand.",
)
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards = Some(
quote! { #crate_name::guard::GuardExt::and(#guard, #guards) },
);
}
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::and(#first_rule, #second_rule) },
))
}
Some(ident) if ident == "or" => {
if args.nested.len() != 2 {
return Err(
Error::new_spanned(args, "or operator support only 2 operands.").into(),
);
}
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::or(#first_rule, #second_rule) },
))
}
Some(ident) if ident == "chain" => {
if args.nested.len() < 2 {
return Err(Error::new_spanned(
args,
"chain operator need at least 1 operand.",
)
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::and(#guard, #guards) });
}
}
Ok(guards)
}
"race" => {
if args.nested.len() < 2 {
return Err(Error::new_spanned(
args,
"race operator need at least 1 operand.",
)
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards = Some(
quote! { #crate_name::guard::GuardExt::or(#guard, #guards) },
);
}
Ok(guards)
}
Some(ident) if ident == "race" => {
if args.nested.len() < 2 {
return Err(
Error::new_spanned(args, "race operator need at least 1 operand.").into(),
);
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::or(#guard, #guards) });
}
}
Ok(guards)
}
_ => {
let ty = &args.path;
let mut params = Vec::new();
for attr in &args.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr {
let name = &nv.path;
if let Lit::Str(value) = &nv.lit {
let value_str = value.value();
if let Some(value_str) = value_str.strip_prefix('@') {
let getter_name = get_param_getter_ident(value_str);
params.push(quote! { #name: #getter_name()? });
} else {
let expr = syn::parse_str::<Expr>(&value_str)?;
params.push(quote! { #name: (#expr).into() });
}
Ok(guards)
}
_ => {
let ty = &args.path;
let mut params = Vec::new();
for attr in &args.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr {
let name = &nv.path;
if let Lit::Str(value) = &nv.lit {
let value_str = value.value();
if let Some(value_str) = value_str.strip_prefix('@') {
let getter_name = get_param_getter_ident(value_str);
params.push(quote! { #name: #getter_name()? });
} else {
return Err(Error::new_spanned(
&nv.lit,
"Value must be string literal",
)
.into());
let expr = syn::parse_str::<Expr>(&value_str)?;
params.push(quote! { #name: (#expr).into() });
}
} else {
return Err(
Error::new_spanned(attr, "Invalid property for guard").into()
);
return Err(Error::new_spanned(
&nv.lit,
"Value must be string literal",
)
.into());
}
} else {
return Err(Error::new_spanned(attr, "Invalid property for guard").into());
}
Ok(Some(quote! { #ty { #(#params),* } }))
}
},
None => Err(Error::new_spanned(args, "Invalid guards").into()),
Ok(Some(quote! { #ty { #(#params),* } }))
}
},
_ => Err(Error::new_spanned(args, "Invalid guards").into()),
}
Expand Down
8 changes: 6 additions & 2 deletions tests/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ enum Role {
Guest,
}

struct RoleGuard {
pub struct RoleGuard {
role: Role,
}

mod guards {
pub use super::RoleGuard;
}

#[async_trait::async_trait]
impl Guard for RoleGuard {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
Expand Down Expand Up @@ -61,7 +65,7 @@ impl Guard for AgeGuard {
pub async fn test_guard_simple_rule() {
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
#[graphql(guard(guards::RoleGuard(role = "Role::Admin")))]
value: i32,
}

Expand Down

0 comments on commit 4c98ad2

Please sign in to comment.