mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-04 09:56:22 +02:00
Compare commits
6 Commits
files-v0.1
...
codegen-v0
Author | SHA1 | Date | |
---|---|---|---|
1ca9d87f0a | |||
967f965405 | |||
062e51e8ce | |||
a48e616def | |||
effa96f5e4 | |||
cc0b4be5b7 |
@ -1,5 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [0.1.6] - 2019-10-14
|
||||||
|
|
||||||
|
* Add option to redirect to a slash-ended path `Files` #1132
|
||||||
|
|
||||||
## [0.1.5] - 2019-10-08
|
## [0.1.5] - 2019-10-08
|
||||||
|
|
||||||
* Bump up `mime_guess` crate version to 2.0.1
|
* Bump up `mime_guess` crate version to 2.0.1
|
||||||
@ -8,17 +12,14 @@
|
|||||||
|
|
||||||
* Allow user defined request guards for `Files` #1113
|
* Allow user defined request guards for `Files` #1113
|
||||||
|
|
||||||
|
|
||||||
## [0.1.4] - 2019-07-20
|
## [0.1.4] - 2019-07-20
|
||||||
|
|
||||||
* Allow to disable `Content-Disposition` header #686
|
* Allow to disable `Content-Disposition` header #686
|
||||||
|
|
||||||
|
|
||||||
## [0.1.3] - 2019-06-28
|
## [0.1.3] - 2019-06-28
|
||||||
|
|
||||||
* Do not set `Content-Length` header, let actix-http set it #930
|
* Do not set `Content-Length` header, let actix-http set it #930
|
||||||
|
|
||||||
|
|
||||||
## [0.1.2] - 2019-06-13
|
## [0.1.2] - 2019-06-13
|
||||||
|
|
||||||
* Content-Length is 0 for NamedFile HEAD request #914
|
* Content-Length is 0 for NamedFile HEAD request #914
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-files"
|
name = "actix-files"
|
||||||
version = "0.1.5"
|
version = "0.1.6"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Static files support for actix web."
|
description = "Static files support for actix web."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -233,6 +233,7 @@ pub struct Files {
|
|||||||
directory: PathBuf,
|
directory: PathBuf,
|
||||||
index: Option<String>,
|
index: Option<String>,
|
||||||
show_index: bool,
|
show_index: bool,
|
||||||
|
redirect_to_slash: bool,
|
||||||
default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
|
default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
|
||||||
renderer: Rc<DirectoryRenderer>,
|
renderer: Rc<DirectoryRenderer>,
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
@ -246,6 +247,7 @@ impl Clone for Files {
|
|||||||
directory: self.directory.clone(),
|
directory: self.directory.clone(),
|
||||||
index: self.index.clone(),
|
index: self.index.clone(),
|
||||||
show_index: self.show_index,
|
show_index: self.show_index,
|
||||||
|
redirect_to_slash: self.redirect_to_slash,
|
||||||
default: self.default.clone(),
|
default: self.default.clone(),
|
||||||
renderer: self.renderer.clone(),
|
renderer: self.renderer.clone(),
|
||||||
file_flags: self.file_flags,
|
file_flags: self.file_flags,
|
||||||
@ -273,6 +275,7 @@ impl Files {
|
|||||||
directory: dir,
|
directory: dir,
|
||||||
index: None,
|
index: None,
|
||||||
show_index: false,
|
show_index: false,
|
||||||
|
redirect_to_slash: false,
|
||||||
default: Rc::new(RefCell::new(None)),
|
default: Rc::new(RefCell::new(None)),
|
||||||
renderer: Rc::new(directory_listing),
|
renderer: Rc::new(directory_listing),
|
||||||
mime_override: None,
|
mime_override: None,
|
||||||
@ -289,6 +292,14 @@ impl Files {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Redirects to a slash-ended path when browsing a directory.
|
||||||
|
///
|
||||||
|
/// By default never redirect.
|
||||||
|
pub fn redirect_to_slash_directory(mut self) -> Self {
|
||||||
|
self.redirect_to_slash = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set custom directory renderer
|
/// Set custom directory renderer
|
||||||
pub fn files_listing_renderer<F>(mut self, f: F) -> Self
|
pub fn files_listing_renderer<F>(mut self, f: F) -> Self
|
||||||
where
|
where
|
||||||
@ -389,10 +400,10 @@ impl HttpServiceFactory for Files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NewService for Files {
|
impl NewService for Files {
|
||||||
type Config = ();
|
|
||||||
type Request = ServiceRequest;
|
type Request = ServiceRequest;
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
type Config = ();
|
||||||
type Service = FilesService;
|
type Service = FilesService;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Future = Box<dyn Future<Item = Self::Service, Error = Self::InitError>>;
|
type Future = Box<dyn Future<Item = Self::Service, Error = Self::InitError>>;
|
||||||
@ -402,6 +413,7 @@ impl NewService for Files {
|
|||||||
directory: self.directory.clone(),
|
directory: self.directory.clone(),
|
||||||
index: self.index.clone(),
|
index: self.index.clone(),
|
||||||
show_index: self.show_index,
|
show_index: self.show_index,
|
||||||
|
redirect_to_slash: self.redirect_to_slash,
|
||||||
default: None,
|
default: None,
|
||||||
renderer: self.renderer.clone(),
|
renderer: self.renderer.clone(),
|
||||||
mime_override: self.mime_override.clone(),
|
mime_override: self.mime_override.clone(),
|
||||||
@ -429,6 +441,7 @@ pub struct FilesService {
|
|||||||
directory: PathBuf,
|
directory: PathBuf,
|
||||||
index: Option<String>,
|
index: Option<String>,
|
||||||
show_index: bool,
|
show_index: bool,
|
||||||
|
redirect_to_slash: bool,
|
||||||
default: Option<HttpService>,
|
default: Option<HttpService>,
|
||||||
renderer: Rc<DirectoryRenderer>,
|
renderer: Rc<DirectoryRenderer>,
|
||||||
mime_override: Option<Rc<MimeOverride>>,
|
mime_override: Option<Rc<MimeOverride>>,
|
||||||
@ -502,6 +515,16 @@ impl Service for FilesService {
|
|||||||
|
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
if let Some(ref redir_index) = self.index {
|
if let Some(ref redir_index) = self.index {
|
||||||
|
if self.redirect_to_slash && !req.path().ends_with('/') {
|
||||||
|
let redirect_to = format!("{}/", req.path());
|
||||||
|
return Either::A(ok(req.into_response(
|
||||||
|
HttpResponse::Found()
|
||||||
|
.header(header::LOCATION, redirect_to)
|
||||||
|
.body("")
|
||||||
|
.into_body(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let path = path.join(redir_index);
|
let path = path.join(redir_index);
|
||||||
|
|
||||||
match NamedFile::open(path) {
|
match NamedFile::open(path) {
|
||||||
@ -1169,6 +1192,34 @@ mod tests {
|
|||||||
assert!(format!("{:?}", bytes).contains("/tests/test.png"));
|
assert!(format!("{:?}", bytes).contains("/tests/test.png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_redirect_to_slash_directory() {
|
||||||
|
// should not redirect if no index
|
||||||
|
let mut srv = test::init_service(
|
||||||
|
App::new().service(Files::new("/", ".").redirect_to_slash_directory()),
|
||||||
|
);
|
||||||
|
let req = TestRequest::with_uri("/tests").to_request();
|
||||||
|
let resp = test::call_service(&mut srv, req);
|
||||||
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
|
// should redirect if index present
|
||||||
|
let mut srv = test::init_service(
|
||||||
|
App::new().service(
|
||||||
|
Files::new("/", ".")
|
||||||
|
.index_file("test.png")
|
||||||
|
.redirect_to_slash_directory(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let req = TestRequest::with_uri("/tests").to_request();
|
||||||
|
let resp = test::call_service(&mut srv, req);
|
||||||
|
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||||
|
|
||||||
|
// should not redirect if the path is wrong
|
||||||
|
let req = TestRequest::with_uri("/not_existing").to_request();
|
||||||
|
let resp = test::call_service(&mut srv, req);
|
||||||
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_static_files_bad_directory() {
|
fn test_static_files_bad_directory() {
|
||||||
let _st: Files = Files::new("/", "missing");
|
let _st: Files = Files::new("/", "missing");
|
||||||
|
@ -29,7 +29,6 @@ impl Response {
|
|||||||
STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED);
|
STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED);
|
||||||
|
|
||||||
STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES);
|
STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES);
|
||||||
STATIC_RESP!(MovedPermanenty, StatusCode::MOVED_PERMANENTLY);
|
|
||||||
STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY);
|
STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY);
|
||||||
STATIC_RESP!(Found, StatusCode::FOUND);
|
STATIC_RESP!(Found, StatusCode::FOUND);
|
||||||
STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER);
|
STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER);
|
||||||
|
@ -194,7 +194,7 @@ impl<B> Response<B> {
|
|||||||
self.head.extensions.borrow_mut()
|
self.head.extensions.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get body os this response
|
/// Get body of this response
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn body(&self) -> &ResponseBody<B> {
|
pub fn body(&self) -> &ResponseBody<B> {
|
||||||
&self.body
|
&self.body
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [0.1.3] - 2019-10-14
|
||||||
|
|
||||||
|
* Bump up `syn` & `quote` to 1.0
|
||||||
|
|
||||||
|
* Provide better error message
|
||||||
|
|
||||||
## [0.1.2] - 2019-06-04
|
## [0.1.2] - 2019-06-04
|
||||||
|
|
||||||
* Add macros for head, options, trace, connect and patch http methods
|
* Add macros for head, options, trace, connect and patch http methods
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
description = "Actix web proc macros"
|
description = "Actix web proc macros"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
@ -12,8 +12,9 @@ workspace = ".."
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "0.6.12"
|
quote = "1"
|
||||||
syn = { version = "0.15.34", features = ["full", "parsing", "extra-traits"] }
|
syn = { version = "1", features = ["full", "parsing"] }
|
||||||
|
proc-macro2 = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-web = { version = "1.0.0" }
|
actix-web = { version = "1.0.0" }
|
||||||
|
@ -58,7 +58,10 @@ use syn::parse_macro_input;
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Get);
|
let gen = match route::Route::new(args, input, route::GuardType::Get) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +73,10 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Post);
|
let gen = match route::Route::new(args, input, route::GuardType::Post) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +88,10 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Put);
|
let gen = match route::Route::new(args, input, route::GuardType::Put) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +103,10 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Delete);
|
let gen = match route::Route::new(args, input, route::GuardType::Delete) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +118,10 @@ pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn head(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn head(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Head);
|
let gen = match route::Route::new(args, input, route::GuardType::Head) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +133,10 @@ pub fn head(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Connect);
|
let gen = match route::Route::new(args, input, route::GuardType::Connect) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +148,10 @@ pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn options(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn options(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Options);
|
let gen = match route::Route::new(args, input, route::GuardType::Options) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +163,10 @@ pub fn options(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Trace);
|
let gen = match route::Route::new(args, input, route::GuardType::Trace) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +178,9 @@ pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(args as syn::AttributeArgs);
|
let args = parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let gen = route::Args::new(&args, input, route::GuardType::Patch);
|
let gen = match route::Route::new(args, input, route::GuardType::Patch) {
|
||||||
|
Ok(gen) => gen,
|
||||||
|
Err(err) => return err.to_compile_error().into(),
|
||||||
|
};
|
||||||
gen.generate()
|
gen.generate()
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
use syn::{AttributeArgs, Ident, NestedMeta};
|
||||||
|
|
||||||
enum ResourceType {
|
enum ResourceType {
|
||||||
Async,
|
Async,
|
||||||
Sync,
|
Sync,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ResourceType {
|
impl ToTokens for ResourceType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn to_tokens(&self, stream: &mut TokenStream2) {
|
||||||
match *self {
|
let ident = match self {
|
||||||
ResourceType::Async => write!(f, "to_async"),
|
ResourceType::Async => "to_async",
|
||||||
ResourceType::Sync => write!(f, "to"),
|
ResourceType::Sync => "to",
|
||||||
}
|
};
|
||||||
|
let ident = Ident::new(ident, Span::call_site());
|
||||||
|
stream.append(ident);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,63 +34,89 @@ pub enum GuardType {
|
|||||||
Patch,
|
Patch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GuardType {
|
impl GuardType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn as_str(&self) -> &'static str {
|
||||||
match *self {
|
match self {
|
||||||
GuardType::Get => write!(f, "Get"),
|
GuardType::Get => "Get",
|
||||||
GuardType::Post => write!(f, "Post"),
|
GuardType::Post => "Post",
|
||||||
GuardType::Put => write!(f, "Put"),
|
GuardType::Put => "Put",
|
||||||
GuardType::Delete => write!(f, "Delete"),
|
GuardType::Delete => "Delete",
|
||||||
GuardType::Head => write!(f, "Head"),
|
GuardType::Head => "Head",
|
||||||
GuardType::Connect => write!(f, "Connect"),
|
GuardType::Connect => "Connect",
|
||||||
GuardType::Options => write!(f, "Options"),
|
GuardType::Options => "Options",
|
||||||
GuardType::Trace => write!(f, "Trace"),
|
GuardType::Trace => "Trace",
|
||||||
GuardType::Patch => write!(f, "Patch"),
|
GuardType::Patch => "Patch",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Args {
|
impl ToTokens for GuardType {
|
||||||
name: syn::Ident,
|
fn to_tokens(&self, stream: &mut TokenStream2) {
|
||||||
path: String,
|
let ident = self.as_str();
|
||||||
ast: syn::ItemFn,
|
let ident = Ident::new(ident, Span::call_site());
|
||||||
resource_type: ResourceType,
|
stream.append(ident);
|
||||||
pub guard: GuardType,
|
}
|
||||||
pub extra_guards: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Args {
|
struct Args {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
path: syn::LitStr,
|
||||||
let ast = &self.ast;
|
guards: Vec<Ident>,
|
||||||
let guards = format!(".guard(actix_web::guard::{}())", self.guard);
|
}
|
||||||
let guards = self.extra_guards.iter().fold(guards, |acc, val| {
|
|
||||||
format!("{}.guard(actix_web::guard::fn_guard({}))", acc, val)
|
|
||||||
});
|
|
||||||
|
|
||||||
write!(
|
impl Args {
|
||||||
f,
|
fn new(args: AttributeArgs) -> syn::Result<Self> {
|
||||||
"
|
let mut path = None;
|
||||||
#[allow(non_camel_case_types)]
|
let mut guards = Vec::new();
|
||||||
pub struct {name};
|
for arg in args {
|
||||||
|
match arg {
|
||||||
impl actix_web::dev::HttpServiceFactory for {name} {{
|
NestedMeta::Lit(syn::Lit::Str(lit)) => match path {
|
||||||
fn register(self, config: &mut actix_web::dev::AppService) {{
|
None => {
|
||||||
{ast}
|
path = Some(lit);
|
||||||
|
}
|
||||||
let resource = actix_web::Resource::new(\"{path}\"){guards}.{to}({name});
|
_ => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
actix_web::dev::HttpServiceFactory::register(resource, config)
|
lit,
|
||||||
}}
|
"Multiple paths specified! Should be only one!",
|
||||||
}}",
|
));
|
||||||
name = self.name,
|
}
|
||||||
ast = quote!(#ast),
|
},
|
||||||
path = self.path,
|
NestedMeta::Meta(syn::Meta::NameValue(nv)) => {
|
||||||
guards = guards,
|
if nv.path.is_ident("guard") {
|
||||||
to = self.resource_type
|
if let syn::Lit::Str(lit) = nv.lit {
|
||||||
)
|
guards.push(Ident::new(&lit.value(), Span::call_site()));
|
||||||
|
} else {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
nv.lit,
|
||||||
|
"Attribute guard expects literal string!",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
nv.path,
|
||||||
|
"Unknown attribute key is specified. Allowed: guard",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg => {
|
||||||
|
return Err(syn::Error::new_spanned(arg, "Unknown attribute"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Args {
|
||||||
|
path: path.unwrap(),
|
||||||
|
guards,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Route {
|
||||||
|
name: syn::Ident,
|
||||||
|
args: Args,
|
||||||
|
ast: syn::ItemFn,
|
||||||
|
resource_type: ResourceType,
|
||||||
|
guard: GuardType,
|
||||||
|
}
|
||||||
|
|
||||||
fn guess_resource_type(typ: &syn::Type) -> ResourceType {
|
fn guess_resource_type(typ: &syn::Type) -> ResourceType {
|
||||||
let mut guess = ResourceType::Sync;
|
let mut guess = ResourceType::Sync;
|
||||||
|
|
||||||
@ -111,75 +139,73 @@ fn guess_resource_type(typ: &syn::Type) -> ResourceType {
|
|||||||
guess
|
guess
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Route {
|
||||||
pub fn new(args: &[syn::NestedMeta], input: TokenStream, guard: GuardType) -> Self {
|
pub fn new(
|
||||||
|
args: AttributeArgs,
|
||||||
|
input: TokenStream,
|
||||||
|
guard: GuardType,
|
||||||
|
) -> syn::Result<Self> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
panic!(
|
return Err(syn::Error::new(
|
||||||
"invalid server definition, expected: #[{}(\"some path\")]",
|
Span::call_site(),
|
||||||
guard
|
format!(
|
||||||
);
|
r#"invalid server definition, expected #[{}("<some path>")]"#,
|
||||||
|
guard.as_str().to_ascii_lowercase()
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
let ast: syn::ItemFn = syn::parse(input)?;
|
||||||
|
let name = ast.sig.ident.clone();
|
||||||
|
|
||||||
let ast: syn::ItemFn = syn::parse(input).expect("Parse input as function");
|
let args = Args::new(args)?;
|
||||||
let name = ast.ident.clone();
|
|
||||||
|
|
||||||
let mut extra_guards = Vec::new();
|
let resource_type = if ast.sig.asyncness.is_some() {
|
||||||
let mut path = None;
|
|
||||||
for arg in args {
|
|
||||||
match arg {
|
|
||||||
syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
|
|
||||||
if path.is_some() {
|
|
||||||
panic!("Multiple paths specified! Should be only one!")
|
|
||||||
}
|
|
||||||
let fname = quote!(#fname).to_string();
|
|
||||||
path = Some(fname.as_str()[1..fname.len() - 1].to_owned())
|
|
||||||
}
|
|
||||||
syn::NestedMeta::Meta(syn::Meta::NameValue(ident)) => {
|
|
||||||
match ident.ident.to_string().to_lowercase().as_str() {
|
|
||||||
"guard" => match ident.lit {
|
|
||||||
syn::Lit::Str(ref text) => extra_guards.push(text.value()),
|
|
||||||
_ => panic!("Attribute guard expects literal string!"),
|
|
||||||
},
|
|
||||||
attr => panic!(
|
|
||||||
"Unknown attribute key is specified: {}. Allowed: guard",
|
|
||||||
attr
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attr => panic!("Unknown attribute{:?}", attr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resource_type = if ast.asyncness.is_some() {
|
|
||||||
ResourceType::Async
|
ResourceType::Async
|
||||||
} else {
|
} else {
|
||||||
match ast.decl.output {
|
match ast.sig.output {
|
||||||
syn::ReturnType::Default => panic!(
|
syn::ReturnType::Default => {
|
||||||
"Function {} has no return type. Cannot be used as handler",
|
return Err(syn::Error::new_spanned(
|
||||||
name
|
ast,
|
||||||
),
|
"Function has no return type. Cannot be used as handler",
|
||||||
|
));
|
||||||
|
}
|
||||||
syn::ReturnType::Type(_, ref typ) => guess_resource_type(typ.as_ref()),
|
syn::ReturnType::Type(_, ref typ) => guess_resource_type(typ.as_ref()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = path.unwrap();
|
Ok(Self {
|
||||||
|
|
||||||
Self {
|
|
||||||
name,
|
name,
|
||||||
path,
|
args,
|
||||||
ast,
|
ast,
|
||||||
resource_type,
|
resource_type,
|
||||||
guard,
|
guard,
|
||||||
extra_guards,
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(&self) -> TokenStream {
|
pub fn generate(&self) -> TokenStream {
|
||||||
let text = self.to_string();
|
let name = &self.name;
|
||||||
|
let guard = &self.guard;
|
||||||
|
let ast = &self.ast;
|
||||||
|
let path = &self.args.path;
|
||||||
|
let extra_guards = &self.args.guards;
|
||||||
|
let resource_type = &self.resource_type;
|
||||||
|
let stream = quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub struct #name;
|
||||||
|
|
||||||
match text.parse() {
|
impl actix_web::dev::HttpServiceFactory for #name {
|
||||||
Ok(res) => res,
|
fn register(self, config: &mut actix_web::dev::AppService) {
|
||||||
Err(error) => panic!("Error: {:?}\nGenerated code: {}", error, text),
|
#ast
|
||||||
}
|
|
||||||
|
let resource = actix_web::Resource::new(#path)
|
||||||
|
.guard(actix_web::guard::#guard())
|
||||||
|
#(.guard(actix_web::guard::fn_guard(#extra_guards)))*
|
||||||
|
.#resource_type(#name);
|
||||||
|
|
||||||
|
actix_web::dev::HttpServiceFactory::register(resource, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stream.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user