Skip to content
This repository has been archived by the owner on Nov 26, 2023. It is now read-only.

Commit

Permalink
feat!: switch to darling & syn2, rename type to ty
Browse files Browse the repository at this point in the history
`syn2` doesn't allow to use keywords as part of `MetaItem` dtolnay/syn#1458

This is reason of renaming `type` to `ty`, which is breaking change
  • Loading branch information
DDtKey committed Nov 24, 2023
1 parent 96755a2 commit cf9bd4a
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 56 deletions.
18 changes: 0 additions & 18 deletions CHANGELOG.md

This file was deleted.

2 changes: 1 addition & 1 deletion examples/enum-role/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rocket_grants::GrantsFairing;
mod role;

// `proc-macro` way require specify your type. It can be an import or a full path.
#[rocket_grants::has_any_role("ADMIN", "role::Role::MANAGER", type = "Role")]
#[rocket_grants::has_any_role("ADMIN", "role::Role::MANAGER", ty = "Role")]
// For the `ADMIN` or `MANAGER` - endpoint will give the HTTP status 200, otherwise - 403
#[rocket::get("/macro_secured")]
async fn macro_secured() -> Status {
Expand Down
1 change: 1 addition & 0 deletions proc-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ edition.workspace = true
proc-macro = true

[dependencies]
darling = "0.20.3"
proc-macro2 = "1.0"
quote = "1"
syn = { version = "2.0", features = ["full", "derive", "extra-traits"] }
Expand Down
60 changes: 33 additions & 27 deletions proc-macro/src/expand.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use darling::ast::NestedMeta;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType};
use syn::parse::Parser;

Check warning on line 4 in proc-macro/src/expand.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `syn::parse::Parser`

warning: unused import: `syn::parse::Parser` --> proc-macro/src/expand.rs:4:5 | 4 | use syn::parse::Parser; | ^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 4 in proc-macro/src/expand.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `syn::parse::Parser`

warning: unused import: `syn::parse::Parser` --> proc-macro/src/expand.rs:4:5 | 4 | use syn::parse::Parser; | ^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use syn::{ExprLit, ItemFn, ReturnType};

pub(crate) struct HasPermissions {
check_fn: Ident,
Expand All @@ -9,10 +11,9 @@ pub(crate) struct HasPermissions {
}

impl HasPermissions {
pub fn new(check_fn: &str, args: AttributeArgs, func: ItemFn) -> syn::Result<Self> {
pub fn new(check_fn: &str, args: Args, func: ItemFn) -> syn::Result<Self> {
let check_fn: Ident = syn::parse_str(check_fn)?;

let args = Args::new(args)?;
if args.permissions.is_empty() {
return Err(syn::Error::new(
Span::call_site(),
Expand Down Expand Up @@ -48,7 +49,7 @@ impl ToTokens for HasPermissions {

let check_fn = &self.check_fn;

let args = if self.args.type_.is_some() {
let args = if self.args.ty.is_some() {
let permissions: Vec<syn::Expr> = self
.args
.permissions
Expand All @@ -67,9 +68,9 @@ impl ToTokens for HasPermissions {
}
};

let type_ = self
let ty = self
.args
.type_
.ty
.as_ref()
.map(|t| t.to_token_stream())
.unwrap_or(quote! {String});
Expand All @@ -83,7 +84,7 @@ impl ToTokens for HasPermissions {
let stream = quote! {
#(#fn_attrs)*
#func_vis #fn_async fn #fn_name #fn_generics(
_auth_details_: rocket_grants::permissions::AuthDetails<#type_>,
_auth_details_: rocket_grants::permissions::AuthDetails<#ty>,
#fn_args
) -> Result<#fn_output, rocket::http::Status> {
use rocket_grants::permissions::{PermissionsCheck, RolesCheck};
Expand All @@ -100,50 +101,55 @@ impl ToTokens for HasPermissions {
}
}

struct Args {
#[derive(Default)]
pub(crate) struct Args {
permissions: Vec<syn::LitStr>,
secure: Option<syn::Expr>,
type_: Option<syn::Expr>,
ty: Option<syn::Expr>,
}

impl Args {
fn new(args: AttributeArgs) -> syn::Result<Self> {
let mut permissions = Vec::with_capacity(args.len());
impl darling::FromMeta for Args {
fn from_list(items: &[NestedMeta]) -> darling::Result<Self> {
let mut permissions = Vec::new();
let mut secure = None;
let mut type_ = None;
for arg in args {
match arg {
NestedMeta::Lit(syn::Lit::Str(lit)) => {
permissions.push(lit);
}
let mut ty = None;

for item in items {
match item {
NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(lit_str),
value:
syn::Expr::Lit(ExprLit {
lit: syn::Lit::Str(lit_str),
..
}),
..
})) => {
if path.is_ident("secure") {
let expr = lit_str.parse().unwrap();
secure = Some(expr);
} else if path.is_ident("type") {
} else if path.is_ident("ty") {
let expr = lit_str.parse().unwrap();
type_ = Some(expr);
ty = Some(expr);
} else {
return Err(syn::Error::new_spanned(
path,
"Unknown identifier. Available: 'secure' and 'type'",
));
return Err(darling::Error::unknown_field_path(path));
}
}
NestedMeta::Lit(syn::Lit::Str(lit)) => {
permissions.push(lit.clone());
}
_ => {
return Err(syn::Error::new_spanned(arg, "Unknown attribute."));
return Err(darling::Error::custom(
"Unknown attribute, available: 'secure', 'ty' & string literal",
))
}
}
}

Ok(Args {
permissions,
secure,
type_,
ty,
})
}
}
24 changes: 19 additions & 5 deletions proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
extern crate proc_macro;
use darling::ast::NestedMeta;
use darling::FromMeta;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{parse_macro_input, AttributeArgs, ItemFn};
use syn::{parse_macro_input, ItemFn};

use crate::expand::HasPermissions;
use crate::expand::{Args, HasPermissions};

mod expand;

Expand All @@ -17,7 +19,7 @@ const HAS_ANY_ROLE: &str = "has_any_role";
/// Allow to add a conditional restriction based on handlers parameters.
/// Add the `secure` attribute followed by the the boolean expression to validate based on parameters
///
/// Also you can use you own types instead of Strings, just add `type` attribute with path to type
/// Also you can use you own types instead of Strings, just add `ty` attribute with path to type
/// # Examples
/// ```
/// use rocket::serde::json::Json;
Expand All @@ -44,7 +46,7 @@ const HAS_ANY_ROLE: &str = "has_any_role";
/// }
///
/// // User must have MyPermissionEnum::OpGetSecret (you own enum example)
/// #[rocket_grants::has_permissions("MyPermissionEnum::OpGetSecret", type = "MyPermissionEnum")]
/// #[rocket_grants::has_permissions("MyPermissionEnum::OpGetSecret", ty = "MyPermissionEnum")]
/// async fn macro_enum_secured() -> &'static str {
/// "some secured info"
/// }
Expand Down Expand Up @@ -103,7 +105,19 @@ pub fn has_any_role(args: TokenStream, input: TokenStream) -> TokenStream {
}

fn check_permissions(check_fn_name: &str, args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as AttributeArgs);
let args = match NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(darling::Error::from(e).write_errors());
}
};
let args = match Args::from_list(&args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};

let func = parse_macro_input!(input as ItemFn);

match HasPermissions::new(check_fn_name, args, func) {
Expand Down
4 changes: 2 additions & 2 deletions rocket-grants/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ async fn macro_secured() -> &'static str {
<br/>


Here is an example using the `type` and `secure` attributes. But these are independent features.
Here is an example using the `ty` and `secure` attributes. But these are independent features.

`secure` allows you to include some checks in the macro based on function params.

`type` allows you to use a custom type for the roles and permissions (then the fairing needs to be configured).
`ty` allows you to use a custom type for the roles and permissions (then the fairing needs to be configured).
Take a look at an [enum-role example](../examples/enum-role/src/main.rs)

```rust,ignore
Expand Down
2 changes: 1 addition & 1 deletion rocket-grants/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub use fairing::GrantsFairing;
/// struct User { id: i32 }
///
/// // You own type is also supported (need to configure fairing for this type as well):
/// #[rocket_grants::has_roles["Role::Admin", "Role::Manager", type = "Role"]]
/// #[rocket_grants::has_roles["Role::Admin", "Role::Manager", ty = "Role"]]
/// #[rocket::get("/enum")]
/// async fn role_enum_macro_secured() -> &'static str {
/// "some secured info"
Expand Down
4 changes: 2 additions & 2 deletions rocket-grants/tests/proc_macro/type_feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use rocket::local::asynchronous::{Client, LocalResponse};
use rocket_grants::{has_roles, GrantsFairing};

// Using imported custom type (in `use` section)
#[has_roles("ADMIN", type = "Role")]
#[has_roles("ADMIN", ty = "Role")]
#[rocket::get("/imported_enum_secure")]
async fn imported_path_enum_secure() -> Status {
Status::Ok
}

// Using a full path to a custom type (enum)
#[has_roles("crate::common::Role::ADMIN", type = "crate::common::Role")]
#[has_roles("crate::common::Role::ADMIN", ty = "crate::common::Role")]
#[rocket::get("/full_path_enum_secure")]
async fn full_path_enum_secure() -> Status {
Status::Ok
Expand Down

0 comments on commit cf9bd4a

Please sign in to comment.