From d22c9f9fb12db30ee1cb7183ce8cf395c707f30d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 20 Jul 2023 10:49:01 +0100 Subject: [PATCH 1/3] update syn to 2 in web codegen (#3081) --- actix-multipart-derive/CHANGES.md | 1 + actix-multipart-derive/Cargo.toml | 4 +- actix-router/src/resource.rs | 3 +- actix-web-codegen/CHANGES.md | 1 + actix-web-codegen/Cargo.toml | 4 +- actix-web-codegen/src/route.rs | 223 ++++++++++-------- .../trybuild/routes-missing-args-fail.stderr | 12 +- .../tests/trybuild/simple-fail.stderr | 32 ++- 8 files changed, 164 insertions(+), 116 deletions(-) diff --git a/actix-multipart-derive/CHANGES.md b/actix-multipart-derive/CHANGES.md index caf75aeb3..8a78900ec 100644 --- a/actix-multipart-derive/CHANGES.md +++ b/actix-multipart-derive/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased +- Update `syn` dependency to `2`. - Minimum supported Rust version (MSRV) is now 1.65 due to transitive `time` dependency. ## 0.6.0 - 2023-02-26 diff --git a/actix-multipart-derive/Cargo.toml b/actix-multipart-derive/Cargo.toml index aca6de84a..41e687e50 100644 --- a/actix-multipart-derive/Cargo.toml +++ b/actix-multipart-derive/Cargo.toml @@ -17,11 +17,11 @@ all-features = true proc-macro = true [dependencies] -darling = "0.14" +darling = "0.20" parse-size = "1" proc-macro2 = "1" quote = "1" -syn = "1" +syn = "2" [dev-dependencies] actix-multipart = "0.6" diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index 860993a37..b8aa6c224 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -252,7 +252,7 @@ impl ResourceDef { /// Multi-pattern resources can be constructed by providing a slice (or vec) of patterns. /// /// # Panics - /// Panics if path pattern is malformed. + /// Panics if any path patterns are malformed. /// /// # Examples /// ``` @@ -838,6 +838,7 @@ impl ResourceDef { fn construct(paths: T, is_prefix: bool) -> Self { let patterns = paths.patterns(); + let (pat_type, segments) = match &patterns { Patterns::Single(pattern) => ResourceDef::parse(pattern, is_prefix, false), diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index b9e4b0aad..2c5f17226 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased - 2023-xx-xx +- Update `syn` dependency to `2`. - Minimum supported Rust version (MSRV) is now 1.65 due to transitive `time` dependency. ## 4.2.0 - 2023-02-26 diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 4f2fdc566..c202c5d6e 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,10 +18,10 @@ proc-macro = true actix-router = "0.5" proc-macro2 = "1" quote = "1" -syn = { version = "1", features = ["full", "extra-traits"] } +syn = { version = "2", features = ["full", "extra-traits"] } [dev-dependencies] -actix-macros = "0.2.3" +actix-macros = "0.2.4" actix-rt = "2.2" actix-test = "0.1" actix-utils = "3" diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index e87d37941..525a1c8ba 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -4,7 +4,54 @@ use actix_router::ResourceDef; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens, TokenStreamExt}; -use syn::{parse_macro_input, AttributeArgs, Ident, LitStr, Meta, NestedMeta, Path}; +use syn::{punctuated::Punctuated, Ident, LitStr, Path, Token}; + +#[derive(Debug)] +pub struct RouteArgs { + path: syn::LitStr, + options: Punctuated, +} + +impl syn::parse::Parse for RouteArgs { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + // path to match: "/foo" + let path = input.parse::().map_err(|mut err| { + err.combine(syn::Error::new( + err.span(), + r#"invalid service definition, expected #[("")]"#, + )); + + err + })?; + + // verify that path pattern is valid + let _ = ResourceDef::new(path.value()); + + // if there's no comma, assume that no options are provided + if !input.peek(Token![,]) { + return Ok(Self { + path, + options: Punctuated::new(), + }); + } + + // advance past comma separator + input.parse::()?; + + // if next char is a literal, assume that it is a string and show multi-path error + if input.cursor().literal().is_some() { + return Err(syn::Error::new( + Span::call_site(), + r#"Multiple paths specified! There should be only one."#, + )); + } + + // zero or more options: name = "foo" + let options = input.parse_terminated(syn::MetaNameValue::parse, Token![,])?; + + Ok(Self { path, options }) + } +} macro_rules! standard_method_type { ( @@ -182,111 +229,90 @@ struct Args { } impl Args { - fn new(args: AttributeArgs, method: Option) -> syn::Result { - let mut path = None; + fn new(args: RouteArgs, method: Option) -> syn::Result { let mut resource_name = None; let mut guards = Vec::new(); let mut wrappers = Vec::new(); let mut methods = HashSet::new(); - if args.is_empty() { - return Err(syn::Error::new( - Span::call_site(), - format!( - r#"invalid service definition, expected #[{}("")]"#, - method - .map_or("route", |it| it.as_str()) - .to_ascii_lowercase() - ), - )); - } - let is_route_macro = method.is_none(); if let Some(method) = method { methods.insert(MethodTypeExt::Standard(method)); } - for arg in args { - match arg { - NestedMeta::Lit(syn::Lit::Str(lit)) => match path { - None => { - let _ = ResourceDef::new(lit.value()); - path = Some(lit); - } - _ => { - return Err(syn::Error::new_spanned( - lit, - "Multiple paths specified! Should be only one!", - )); - } - }, - - NestedMeta::Meta(syn::Meta::NameValue(nv)) => { - if nv.path.is_ident("name") { - if let syn::Lit::Str(lit) = nv.lit { - resource_name = Some(lit); - } else { - return Err(syn::Error::new_spanned( - nv.lit, - "Attribute name expects literal string!", - )); - } - } else if nv.path.is_ident("guard") { - if let syn::Lit::Str(lit) = nv.lit { - guards.push(lit.parse::()?); - } else { - return Err(syn::Error::new_spanned( - nv.lit, - "Attribute guard expects literal string!", - )); - } - } else if nv.path.is_ident("wrap") { - if let syn::Lit::Str(lit) = nv.lit { - wrappers.push(lit.parse()?); - } else { - return Err(syn::Error::new_spanned( - nv.lit, - "Attribute wrap expects type", - )); - } - } else if nv.path.is_ident("method") { - if !is_route_macro { - return Err(syn::Error::new_spanned( + for nv in args.options { + if nv.path.is_ident("name") { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = nv.value + { + resource_name = Some(lit); + } else { + return Err(syn::Error::new_spanned( + nv.value, + "Attribute name expects literal string!", + )); + } + } else if nv.path.is_ident("guard") { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = nv.value + { + guards.push(lit.parse::()?); + } else { + return Err(syn::Error::new_spanned( + nv.value, + "Attribute guard expects literal string!", + )); + } + } else if nv.path.is_ident("wrap") { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = nv.value + { + wrappers.push(lit.parse()?); + } else { + return Err(syn::Error::new_spanned( + nv.value, + "Attribute wrap expects type", + )); + } + } else if nv.path.is_ident("method") { + if !is_route_macro { + return Err(syn::Error::new_spanned( &nv, "HTTP method forbidden here. To handle multiple methods, use `route` instead", )); - } else if let syn::Lit::Str(ref lit) = nv.lit { - if !methods.insert(MethodTypeExt::try_from(lit)?) { - return Err(syn::Error::new_spanned( - &nv.lit, - format!( - "HTTP method defined more than once: `{}`", - lit.value() - ), - )); - } - } else { - return Err(syn::Error::new_spanned( - nv.lit, - "Attribute method expects literal string!", - )); - } - } else { + } else if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = nv.value.clone() + { + if !methods.insert(MethodTypeExt::try_from(&lit)?) { return Err(syn::Error::new_spanned( - nv.path, - "Unknown attribute key is specified. Allowed: guard, method and wrap", + nv.value, + format!("HTTP method defined more than once: `{}`", lit.value()), )); } + } else { + return Err(syn::Error::new_spanned( + nv.value, + "Attribute method expects literal string!", + )); } - - arg => { - return Err(syn::Error::new_spanned(arg, "Unknown attribute.")); - } + } else { + return Err(syn::Error::new_spanned( + nv.path, + "Unknown attribute key is specified. Allowed: guard, method and wrap", + )); } } Ok(Args { - path: path.unwrap(), + path: args.path, resource_name, guards, wrappers, @@ -312,11 +338,7 @@ pub struct Route { } impl Route { - pub fn new( - args: AttributeArgs, - ast: syn::ItemFn, - method: Option, - ) -> syn::Result { + pub fn new(args: RouteArgs, ast: syn::ItemFn, method: Option) -> syn::Result { let name = ast.sig.ident.clone(); // Try and pull out the doc comments so that we can reapply them to the generated struct. @@ -324,7 +346,7 @@ impl Route { let doc_attributes = ast .attrs .iter() - .filter(|attr| attr.path.is_ident("doc")) + .filter(|attr| attr.path().is_ident("doc")) .cloned() .collect(); @@ -360,7 +382,7 @@ impl Route { let doc_attributes = ast .attrs .iter() - .filter(|attr| attr.path.is_ident("doc")) + .filter(|attr| attr.path().is_ident("doc")) .cloned() .collect(); @@ -455,7 +477,11 @@ pub(crate) fn with_method( args: TokenStream, input: TokenStream, ) -> TokenStream { - let args = parse_macro_input!(args as syn::AttributeArgs); + let args = match syn::parse(args) { + Ok(args) => args, + // on parse error, make IDEs happy; see fn docs + Err(err) => return input_and_compile_error(input, err), + }; let ast = match syn::parse::(input.clone()) { Ok(ast) => ast, @@ -480,7 +506,7 @@ pub(crate) fn with_methods(input: TokenStream) -> TokenStream { let (methods, others) = ast .attrs .into_iter() - .map(|attr| match MethodType::from_path(&attr.path) { + .map(|attr| match MethodType::from_path(attr.path()) { Ok(method) => Ok((method, attr)), Err(_) => Err(attr), }) @@ -492,13 +518,8 @@ pub(crate) fn with_methods(input: TokenStream) -> TokenStream { .into_iter() .map(Result::unwrap) .map(|(method, attr)| { - attr.parse_meta().and_then(|args| { - if let Meta::List(args) = args { - Args::new(args.nested.into_iter().collect(), Some(method)) - } else { - Err(syn::Error::new_spanned(attr, "Invalid input for macro")) - } - }) + attr.parse_args() + .and_then(|args| Args::new(args, Some(method))) }) .collect::, _>>() { diff --git a/actix-web-codegen/tests/trybuild/routes-missing-args-fail.stderr b/actix-web-codegen/tests/trybuild/routes-missing-args-fail.stderr index e845241a4..2e84c296a 100644 --- a/actix-web-codegen/tests/trybuild/routes-missing-args-fail.stderr +++ b/actix-web-codegen/tests/trybuild/routes-missing-args-fail.stderr @@ -1,4 +1,4 @@ -error: invalid service definition, expected #[get("")] +error: unexpected end of input, expected string literal --> tests/trybuild/routes-missing-args-fail.rs:4:1 | 4 | #[get] @@ -6,11 +6,19 @@ error: invalid service definition, expected #[get("")] | = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Invalid input for macro +error: invalid service definition, expected #[("")] --> tests/trybuild/routes-missing-args-fail.rs:4:1 | 4 | #[get] | ^^^^^^ + | + = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected attribute arguments in parentheses: #[get(...)] + --> tests/trybuild/routes-missing-args-fail.rs:4:3 + | +4 | #[get] + | ^^^ error[E0277]: the trait bound `fn() -> impl std::future::Future {index}: HttpServiceFactory` is not satisfied --> tests/trybuild/routes-missing-args-fail.rs:13:55 diff --git a/actix-web-codegen/tests/trybuild/simple-fail.stderr b/actix-web-codegen/tests/trybuild/simple-fail.stderr index cffc81ff8..3b3f9d850 100644 --- a/actix-web-codegen/tests/trybuild/simple-fail.stderr +++ b/actix-web-codegen/tests/trybuild/simple-fail.stderr @@ -1,26 +1,42 @@ -error: Unknown attribute. - --> $DIR/simple-fail.rs:3:15 +error: expected `=` + --> $DIR/simple-fail.rs:3:1 | 3 | #[get("/one", other)] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected identifier or literal +error: expected string literal --> $DIR/simple-fail.rs:8:8 | 8 | #[post(/two)] | ^ -error: Unknown attribute. +error: invalid service definition, expected #[("")] + --> $DIR/simple-fail.rs:8:8 + | +8 | #[post(/two)] + | ^ + +error: expected string literal --> $DIR/simple-fail.rs:15:9 | 15 | #[patch(PATCH_PATH)] | ^^^^^^^^^^ -error: Multiple paths specified! Should be only one! - --> $DIR/simple-fail.rs:20:19 +error: invalid service definition, expected #[("")] + --> $DIR/simple-fail.rs:15:9 + | +15 | #[patch(PATCH_PATH)] + | ^^^^^^^^^^ + +error: Multiple paths specified! There should be only one. + --> $DIR/simple-fail.rs:20:1 | 20 | #[delete("/four", "/five")] - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `delete` (in Nightly builds, run with -Z macro-backtrace for more info) error: HTTP method forbidden here. To handle multiple methods, use `route` instead --> $DIR/simple-fail.rs:25:19 From 1040bc3d17e491d133da733c419c00d727846600 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 20 Jul 2023 18:36:49 +0800 Subject: [PATCH 2/3] Add missing status code constructor methods on `HttpResponse` (#3042) --- actix-web/CHANGES.md | 1 + actix-web/src/response/http_codes.rs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index 35d695328..e96f852e5 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -6,6 +6,7 @@ - Add `HttpServer::{bind, listen}_auto_h2c()` method behind new `http2` crate feature. - Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards. +- Add several missing convenience methods on `HttpResponse` for their respective status codes. ### Changed diff --git a/actix-web/src/response/http_codes.rs b/actix-web/src/response/http_codes.rs index 2ac83046a..db5f392c9 100644 --- a/actix-web/src/response/http_codes.rs +++ b/actix-web/src/response/http_codes.rs @@ -25,12 +25,12 @@ impl HttpResponse { NonAuthoritativeInformation, StatusCode::NON_AUTHORITATIVE_INFORMATION ); - static_resp!(NoContent, StatusCode::NO_CONTENT); static_resp!(ResetContent, StatusCode::RESET_CONTENT); static_resp!(PartialContent, StatusCode::PARTIAL_CONTENT); static_resp!(MultiStatus, StatusCode::MULTI_STATUS); static_resp!(AlreadyReported, StatusCode::ALREADY_REPORTED); + static_resp!(ImUsed, StatusCode::IM_USED); static_resp!(MultipleChoices, StatusCode::MULTIPLE_CHOICES); static_resp!(MovedPermanently, StatusCode::MOVED_PERMANENTLY); @@ -42,10 +42,10 @@ impl HttpResponse { static_resp!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT); static_resp!(BadRequest, StatusCode::BAD_REQUEST); - static_resp!(NotFound, StatusCode::NOT_FOUND); static_resp!(Unauthorized, StatusCode::UNAUTHORIZED); static_resp!(PaymentRequired, StatusCode::PAYMENT_REQUIRED); static_resp!(Forbidden, StatusCode::FORBIDDEN); + static_resp!(NotFound, StatusCode::NOT_FOUND); static_resp!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED); static_resp!(NotAcceptable, StatusCode::NOT_ACCEPTABLE); static_resp!( @@ -57,13 +57,18 @@ impl HttpResponse { static_resp!(Gone, StatusCode::GONE); static_resp!(LengthRequired, StatusCode::LENGTH_REQUIRED); static_resp!(PreconditionFailed, StatusCode::PRECONDITION_FAILED); - static_resp!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED); static_resp!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE); static_resp!(UriTooLong, StatusCode::URI_TOO_LONG); static_resp!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE); static_resp!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE); static_resp!(ExpectationFailed, StatusCode::EXPECTATION_FAILED); + static_resp!(ImATeapot, StatusCode::IM_A_TEAPOT); + static_resp!(MisdirectedRequest, StatusCode::MISDIRECTED_REQUEST); static_resp!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY); + static_resp!(Locked, StatusCode::LOCKED); + static_resp!(FailedDependency, StatusCode::FAILED_DEPENDENCY); + static_resp!(UpgradeRequired, StatusCode::UPGRADE_REQUIRED); + static_resp!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED); static_resp!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS); static_resp!( RequestHeaderFieldsTooLarge, @@ -83,6 +88,11 @@ impl HttpResponse { static_resp!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES); static_resp!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE); static_resp!(LoopDetected, StatusCode::LOOP_DETECTED); + static_resp!(NotExtended, StatusCode::NOT_EXTENDED); + static_resp!( + NetworkAuthenticationRequired, + StatusCode::NETWORK_AUTHENTICATION_REQUIRED + ); } #[cfg(test)] From 3eb5a059ade249dae4468a8bb6eb5784e1dfdb10 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 20 Jul 2023 11:02:15 +0100 Subject: [PATCH 3/3] chore: address clippy warnings --- actix-http/src/encoding/mod.rs | 2 +- actix-http/src/ws/mask.rs | 2 +- actix-http/tests/test_server.rs | 8 ++--- actix-router/src/resource.rs | 7 +++- actix-web/src/http/header/accept.rs | 8 ++--- actix-web/src/http/header/accept_charset.rs | 2 +- actix-web/src/http/header/accept_encoding.rs | 14 ++++---- actix-web/src/http/header/accept_language.rs | 12 +++---- actix-web/src/http/header/allow.rs | 6 ++-- actix-web/src/http/header/cache_control.rs | 12 +++---- actix-web/src/http/header/content_language.rs | 4 +-- actix-web/src/http/header/content_range.rs | 36 ++++++++----------- actix-web/src/http/header/content_type.rs | 4 +-- actix-web/src/http/header/date.rs | 2 +- actix-web/src/http/header/etag.rs | 30 ++++++++-------- actix-web/src/http/header/expires.rs | 2 +- actix-web/src/http/header/if_match.rs | 6 ++-- .../src/http/header/if_modified_since.rs | 2 +- actix-web/src/http/header/if_none_match.rs | 10 +++--- actix-web/src/http/header/if_range.rs | 6 ++-- .../src/http/header/if_unmodified_since.rs | 2 +- actix-web/src/http/header/last_modified.rs | 2 +- 22 files changed, 87 insertions(+), 92 deletions(-) diff --git a/actix-http/src/encoding/mod.rs b/actix-http/src/encoding/mod.rs index 7823f3979..6801b5fb0 100644 --- a/actix-http/src/encoding/mod.rs +++ b/actix-http/src/encoding/mod.rs @@ -12,7 +12,7 @@ pub use self::{decoder::Decoder, encoder::Encoder}; /// Special-purpose writer for streaming (de-)compression. /// /// Pre-allocates 8KiB of capacity. -pub(self) struct Writer { +struct Writer { buf: BytesMut, } diff --git a/actix-http/src/ws/mask.rs b/actix-http/src/ws/mask.rs index be72e5631..115a8cf9b 100644 --- a/actix-http/src/ws/mask.rs +++ b/actix-http/src/ws/mask.rs @@ -50,7 +50,7 @@ mod tests { #[test] fn test_apply_mask() { let mask = [0x6d, 0xb6, 0xb2, 0x80]; - let unmasked = vec![ + let unmasked = [ 0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9, 0x12, 0x03, ]; diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index cfb4d17b8..f55b1b36c 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -1,5 +1,3 @@ -#![allow(clippy::uninlined_format_args)] - use std::{ convert::Infallible, io::{Read, Write}, @@ -139,7 +137,7 @@ async fn expect_continue_h1() { #[actix_rt::test] async fn chunked_payload() { - let chunk_sizes = vec![32768, 32, 32768]; + let chunk_sizes = [32768, 32, 32768]; let total_size: usize = chunk_sizes.iter().sum(); let mut srv = test_server(|| { @@ -402,7 +400,7 @@ async fn content_length() { let mut srv = test_server(|| { HttpService::build() .h1(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); + let idx: usize = req.uri().path()[1..].parse().unwrap(); let statuses = [ StatusCode::NO_CONTENT, StatusCode::CONTINUE, @@ -411,7 +409,7 @@ async fn content_length() { StatusCode::OK, StatusCode::NOT_FOUND, ]; - ok::<_, Infallible>(Response::new(statuses[indx])) + ok::<_, Infallible>(Response::new(statuses[idx])) }) .tcp() }) diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index b8aa6c224..a3e89a1fe 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -1529,7 +1529,12 @@ mod tests { assert!(!resource.resource_path_from_iter(&mut s, &mut ["item"].iter())); let mut s = String::new(); - assert!(resource.resource_path_from_iter(&mut s, &mut vec!["item", "item2"].iter())); + + assert!(resource.resource_path_from_iter( + &mut s, + #[allow(clippy::useless_vec)] + &mut vec!["item", "item2"].iter() + )); assert_eq!(s, "/user/item/item2/"); } diff --git a/actix-web/src/http/header/accept.rs b/actix-web/src/http/header/accept.rs index 1be136b19..99c95175f 100644 --- a/actix-web/src/http/header/accept.rs +++ b/actix-web/src/http/header/accept.rs @@ -78,7 +78,7 @@ common_header! { // Tests from the RFC crate::http::header::common_header_test!( test1, - vec![b"audio/*; q=0.2, audio/basic"], + [b"audio/*; q=0.2, audio/basic"], Some(Accept(vec![ QualityItem::new("audio/*".parse().unwrap(), q(0.2)), QualityItem::max("audio/basic".parse().unwrap()), @@ -86,7 +86,7 @@ common_header! { crate::http::header::common_header_test!( test2, - vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"], + [b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"], Some(Accept(vec![ QualityItem::new(mime::TEXT_PLAIN, q(0.5)), QualityItem::max(mime::TEXT_HTML), @@ -99,13 +99,13 @@ common_header! { // Custom tests crate::http::header::common_header_test!( test3, - vec![b"text/plain; charset=utf-8"], + [b"text/plain; charset=utf-8"], Some(Accept(vec![ QualityItem::max(mime::TEXT_PLAIN_UTF_8), ]))); crate::http::header::common_header_test!( test4, - vec![b"text/plain; charset=utf-8; q=0.5"], + [b"text/plain; charset=utf-8; q=0.5"], Some(Accept(vec![ QualityItem::new(mime::TEXT_PLAIN_UTF_8, q(0.5)), ]))); diff --git a/actix-web/src/http/header/accept_charset.rs b/actix-web/src/http/header/accept_charset.rs index c7f7e1a68..43a7861fe 100644 --- a/actix-web/src/http/header/accept_charset.rs +++ b/actix-web/src/http/header/accept_charset.rs @@ -57,6 +57,6 @@ common_header! { test_parse_and_format { // Test case from RFC - common_header_test!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]); + common_header_test!(test1, [b"iso-8859-5, unicode-1-1;q=0.8"]); } } diff --git a/actix-web/src/http/header/accept_encoding.rs b/actix-web/src/http/header/accept_encoding.rs index 715126a03..cc80e7bb0 100644 --- a/actix-web/src/http/header/accept_encoding.rs +++ b/actix-web/src/http/header/accept_encoding.rs @@ -50,31 +50,31 @@ common_header! { (AcceptEncoding, header::ACCEPT_ENCODING) => (QualityItem>)* test_parse_and_format { - common_header_test!(no_headers, vec![b""; 0], Some(AcceptEncoding(vec![]))); - common_header_test!(empty_header, vec![b""; 1], Some(AcceptEncoding(vec![]))); + common_header_test!(no_headers, [b""; 0], Some(AcceptEncoding(vec![]))); + common_header_test!(empty_header, [b""; 1], Some(AcceptEncoding(vec![]))); common_header_test!( order_of_appearance, - vec![b"br, gzip"], + [b"br, gzip"], Some(AcceptEncoding(vec![ QualityItem::max(Preference::Specific(Encoding::brotli())), QualityItem::max(Preference::Specific(Encoding::gzip())), ])) ); - common_header_test!(any, vec![b"*"], Some(AcceptEncoding(vec![ + common_header_test!(any, [b"*"], Some(AcceptEncoding(vec![ QualityItem::max(Preference::Any), ]))); // Note: Removed quality 1 from gzip - common_header_test!(implicit_quality, vec![b"gzip, identity; q=0.5, *;q=0"]); + common_header_test!(implicit_quality, [b"gzip, identity; q=0.5, *;q=0"]); // Note: Removed quality 1 from gzip - common_header_test!(implicit_quality_out_of_order, vec![b"compress;q=0.5, gzip"]); + common_header_test!(implicit_quality_out_of_order, [b"compress;q=0.5, gzip"]); common_header_test!( only_gzip_no_identity, - vec![b"gzip, *; q=0"], + [b"gzip, *; q=0"], Some(AcceptEncoding(vec![ QualityItem::max(Preference::Specific(Encoding::gzip())), QualityItem::zero(Preference::Any), diff --git a/actix-web/src/http/header/accept_language.rs b/actix-web/src/http/header/accept_language.rs index 9943e121f..b1d588f8d 100644 --- a/actix-web/src/http/header/accept_language.rs +++ b/actix-web/src/http/header/accept_language.rs @@ -58,19 +58,19 @@ common_header! { (AcceptLanguage, header::ACCEPT_LANGUAGE) => (QualityItem>)* test_parse_and_format { - common_header_test!(no_headers, vec![b""; 0], Some(AcceptLanguage(vec![]))); + common_header_test!(no_headers, [b""; 0], Some(AcceptLanguage(vec![]))); - common_header_test!(empty_header, vec![b""; 1], Some(AcceptLanguage(vec![]))); + common_header_test!(empty_header, [b""; 1], Some(AcceptLanguage(vec![]))); common_header_test!( example_from_rfc, - vec![b"da, en-gb;q=0.8, en;q=0.7"] + [b"da, en-gb;q=0.8, en;q=0.7"] ); common_header_test!( not_ordered_by_weight, - vec![b"en-US, en; q=0.5, fr"], + [b"en-US, en; q=0.5, fr"], Some(AcceptLanguage(vec![ QualityItem::max("en-US".parse().unwrap()), QualityItem::new("en".parse().unwrap(), q(0.5)), @@ -80,7 +80,7 @@ common_header! { common_header_test!( has_wildcard, - vec![b"fr-CH, fr; q=0.9, en; q=0.8, de; q=0.7, *; q=0.5"], + [b"fr-CH, fr; q=0.9, en; q=0.8, de; q=0.7, *; q=0.5"], Some(AcceptLanguage(vec![ QualityItem::max("fr-CH".parse().unwrap()), QualityItem::new("fr".parse().unwrap(), q(0.9)), @@ -137,7 +137,7 @@ impl AcceptLanguage { b.quality.cmp(&a.quality) }); - types.into_iter().map(|qitem| qitem.item).collect() + types.into_iter().map(|q_item| q_item.item).collect() } } diff --git a/actix-web/src/http/header/allow.rs b/actix-web/src/http/header/allow.rs index d0ef96486..1ae8f00dc 100644 --- a/actix-web/src/http/header/allow.rs +++ b/actix-web/src/http/header/allow.rs @@ -51,12 +51,12 @@ crate::http::header::common_header! { // From the RFC crate::http::header::common_header_test!( test1, - vec![b"GET, HEAD, PUT"], + [b"GET, HEAD, PUT"], Some(HeaderField(vec![Method::GET, Method::HEAD, Method::PUT]))); // Own tests crate::http::header::common_header_test!( test2, - vec![b"OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH"], + [b"OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH"], Some(HeaderField(vec![ Method::OPTIONS, Method::GET, @@ -69,7 +69,7 @@ crate::http::header::common_header! { Method::PATCH]))); crate::http::header::common_header_test!( test3, - vec![b""], + [b""], Some(HeaderField(Vec::::new()))); } } diff --git a/actix-web/src/http/header/cache_control.rs b/actix-web/src/http/header/cache_control.rs index 37629313e..77e22d1c3 100644 --- a/actix-web/src/http/header/cache_control.rs +++ b/actix-web/src/http/header/cache_control.rs @@ -47,13 +47,13 @@ common_header! { (CacheControl, header::CACHE_CONTROL) => (CacheDirective)+ test_parse_and_format { - common_header_test!(no_headers, vec![b""; 0], None); - common_header_test!(empty_header, vec![b""; 1], None); - common_header_test!(bad_syntax, vec![b"foo="], None); + common_header_test!(no_headers, [b""; 0], None); + common_header_test!(empty_header, [b""; 1], None); + common_header_test!(bad_syntax, [b"foo="], None); common_header_test!( multiple_headers, - vec![&b"no-cache"[..], &b"private"[..]], + [&b"no-cache"[..], &b"private"[..]], Some(CacheControl(vec![ CacheDirective::NoCache, CacheDirective::Private, @@ -62,7 +62,7 @@ common_header! { common_header_test!( argument, - vec![b"max-age=100, private"], + [b"max-age=100, private"], Some(CacheControl(vec![ CacheDirective::MaxAge(100), CacheDirective::Private, @@ -71,7 +71,7 @@ common_header! { common_header_test!( extension, - vec![b"foo, bar=baz"], + [b"foo, bar=baz"], Some(CacheControl(vec![ CacheDirective::Extension("foo".to_owned(), None), CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned())), diff --git a/actix-web/src/http/header/content_language.rs b/actix-web/src/http/header/content_language.rs index ff317e1de..5b0797ef3 100644 --- a/actix-web/src/http/header/content_language.rs +++ b/actix-web/src/http/header/content_language.rs @@ -48,7 +48,7 @@ common_header! { (ContentLanguage, CONTENT_LANGUAGE) => (QualityItem)+ test_parse_and_format { - crate::http::header::common_header_test!(test1, vec![b"da"]); - crate::http::header::common_header_test!(test2, vec![b"mi, en"]); + crate::http::header::common_header_test!(test1, [b"da"]); + crate::http::header::common_header_test!(test2, [b"mi, en"]); } } diff --git a/actix-web/src/http/header/content_range.rs b/actix-web/src/http/header/content_range.rs index 8befffd95..884926195 100644 --- a/actix-web/src/http/header/content_range.rs +++ b/actix-web/src/http/header/content_range.rs @@ -13,59 +13,59 @@ crate::http::header::common_header! { test_parse_and_format { crate::http::header::common_header_test!(test_bytes, - vec![b"bytes 0-499/500"], + [b"bytes 0-499/500"], Some(ContentRange(ContentRangeSpec::Bytes { range: Some((0, 499)), instance_length: Some(500) }))); crate::http::header::common_header_test!(test_bytes_unknown_len, - vec![b"bytes 0-499/*"], + [b"bytes 0-499/*"], Some(ContentRange(ContentRangeSpec::Bytes { range: Some((0, 499)), instance_length: None }))); crate::http::header::common_header_test!(test_bytes_unknown_range, - vec![b"bytes */500"], + [b"bytes */500"], Some(ContentRange(ContentRangeSpec::Bytes { range: None, instance_length: Some(500) }))); crate::http::header::common_header_test!(test_unregistered, - vec![b"seconds 1-2"], + [b"seconds 1-2"], Some(ContentRange(ContentRangeSpec::Unregistered { unit: "seconds".to_owned(), resp: "1-2".to_owned() }))); crate::http::header::common_header_test!(test_no_len, - vec![b"bytes 0-499"], + [b"bytes 0-499"], None::); crate::http::header::common_header_test!(test_only_unit, - vec![b"bytes"], + [b"bytes"], None::); crate::http::header::common_header_test!(test_end_less_than_start, - vec![b"bytes 499-0/500"], + [b"bytes 499-0/500"], None::); crate::http::header::common_header_test!(test_blank, - vec![b""], + [b""], None::); crate::http::header::common_header_test!(test_bytes_many_spaces, - vec![b"bytes 1-2/500 3"], + [b"bytes 1-2/500 3"], None::); crate::http::header::common_header_test!(test_bytes_many_slashes, - vec![b"bytes 1-2/500/600"], + [b"bytes 1-2/500/600"], None::); crate::http::header::common_header_test!(test_bytes_many_dashes, - vec![b"bytes 1-2-3/500"], + [b"bytes 1-2-3/500"], None::); } @@ -113,21 +113,13 @@ pub enum ContentRangeSpec { }, } -fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> { - let mut iter = s.splitn(2, separator); - match (iter.next(), iter.next()) { - (Some(a), Some(b)) => Some((a, b)), - _ => None, - } -} - impl FromStr for ContentRangeSpec { type Err = ParseError; fn from_str(s: &str) -> Result { - let res = match split_in_two(s, ' ') { + let res = match s.split_once(' ') { Some(("bytes", resp)) => { - let (range, instance_length) = split_in_two(resp, '/').ok_or(ParseError::Header)?; + let (range, instance_length) = resp.split_once('/').ok_or(ParseError::Header)?; let instance_length = if instance_length == "*" { None @@ -139,7 +131,7 @@ impl FromStr for ContentRangeSpec { None } else { let (first_byte, last_byte) = - split_in_two(range, '-').ok_or(ParseError::Header)?; + range.split_once('-').ok_or(ParseError::Header)?; let first_byte = first_byte.parse().map_err(|_| ParseError::Header)?; let last_byte = last_byte.parse().map_err(|_| ParseError::Header)?; if last_byte < first_byte { diff --git a/actix-web/src/http/header/content_type.rs b/actix-web/src/http/header/content_type.rs index bf86afffa..c43ef8a2f 100644 --- a/actix-web/src/http/header/content_type.rs +++ b/actix-web/src/http/header/content_type.rs @@ -45,11 +45,11 @@ crate::http::header::common_header! { test_parse_and_format { crate::http::header::common_header_test!( test_text_html, - vec![b"text/html"], + [b"text/html"], Some(HeaderField(mime::TEXT_HTML))); crate::http::header::common_header_test!( test_image_star, - vec![b"image/*"], + [b"image/*"], Some(HeaderField(mime::IMAGE_STAR))); } diff --git a/actix-web/src/http/header/date.rs b/actix-web/src/http/header/date.rs index a8ac65660..ac30424f0 100644 --- a/actix-web/src/http/header/date.rs +++ b/actix-web/src/http/header/date.rs @@ -32,7 +32,7 @@ crate::http::header::common_header! { (Date, DATE) => [HttpDate] test_parse_and_format { - crate::http::header::common_header_test!(test1, vec![b"Tue, 15 Nov 1994 08:12:31 GMT"]); + crate::http::header::common_header_test!(test1, [b"Tue, 15 Nov 1994 08:12:31 GMT"]); } } diff --git a/actix-web/src/http/header/etag.rs b/actix-web/src/http/header/etag.rs index 78f5447b3..b82208288 100644 --- a/actix-web/src/http/header/etag.rs +++ b/actix-web/src/http/header/etag.rs @@ -49,50 +49,50 @@ crate::http::header::common_header! { test_parse_and_format { // From the RFC crate::http::header::common_header_test!(test1, - vec![b"\"xyzzy\""], + [b"\"xyzzy\""], Some(ETag(EntityTag::new_strong("xyzzy".to_owned())))); crate::http::header::common_header_test!(test2, - vec![b"W/\"xyzzy\""], + [b"W/\"xyzzy\""], Some(ETag(EntityTag::new_weak("xyzzy".to_owned())))); crate::http::header::common_header_test!(test3, - vec![b"\"\""], + [b"\"\""], Some(ETag(EntityTag::new_strong("".to_owned())))); // Own tests crate::http::header::common_header_test!(test4, - vec![b"\"foobar\""], + [b"\"foobar\""], Some(ETag(EntityTag::new_strong("foobar".to_owned())))); crate::http::header::common_header_test!(test5, - vec![b"\"\""], + [b"\"\""], Some(ETag(EntityTag::new_strong("".to_owned())))); crate::http::header::common_header_test!(test6, - vec![b"W/\"weak-etag\""], + [b"W/\"weak-etag\""], Some(ETag(EntityTag::new_weak("weak-etag".to_owned())))); crate::http::header::common_header_test!(test7, - vec![b"W/\"\x65\x62\""], + [b"W/\"\x65\x62\""], Some(ETag(EntityTag::new_weak("\u{0065}\u{0062}".to_owned())))); crate::http::header::common_header_test!(test8, - vec![b"W/\"\""], + [b"W/\"\""], Some(ETag(EntityTag::new_weak("".to_owned())))); crate::http::header::common_header_test!(test9, - vec![b"no-dquotes"], + [b"no-dquotes"], None::); crate::http::header::common_header_test!(test10, - vec![b"w/\"the-first-w-is-case-sensitive\""], + [b"w/\"the-first-w-is-case-sensitive\""], None::); crate::http::header::common_header_test!(test11, - vec![b""], + [b""], None::); crate::http::header::common_header_test!(test12, - vec![b"\"unmatched-dquotes1"], + [b"\"unmatched-dquotes1"], None::); crate::http::header::common_header_test!(test13, - vec![b"unmatched-dquotes2\""], + [b"unmatched-dquotes2\""], None::); crate::http::header::common_header_test!(test14, - vec![b"matched-\"dquotes\""], + [b"matched-\"dquotes\""], None::); crate::http::header::common_header_test!(test15, - vec![b"\""], + [b"\""], None::); } } diff --git a/actix-web/src/http/header/expires.rs b/actix-web/src/http/header/expires.rs index 55fe5acc5..b677ab527 100644 --- a/actix-web/src/http/header/expires.rs +++ b/actix-web/src/http/header/expires.rs @@ -36,6 +36,6 @@ crate::http::header::common_header! { test_parse_and_format { // Test case from RFC - crate::http::header::common_header_test!(test1, vec![b"Thu, 01 Dec 1994 16:00:00 GMT"]); + crate::http::header::common_header_test!(test1, [b"Thu, 01 Dec 1994 16:00:00 GMT"]); } } diff --git a/actix-web/src/http/header/if_match.rs b/actix-web/src/http/header/if_match.rs index e299d30fe..e0b46a6c3 100644 --- a/actix-web/src/http/header/if_match.rs +++ b/actix-web/src/http/header/if_match.rs @@ -52,17 +52,17 @@ common_header! { test_parse_and_format { crate::http::header::common_header_test!( test1, - vec![b"\"xyzzy\""], + [b"\"xyzzy\""], Some(HeaderField::Items( vec![EntityTag::new_strong("xyzzy".to_owned())]))); crate::http::header::common_header_test!( test2, - vec![b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""], + [b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""], Some(HeaderField::Items( vec![EntityTag::new_strong("xyzzy".to_owned()), EntityTag::new_strong("r2d2xxxx".to_owned()), EntityTag::new_strong("c3piozzzz".to_owned())]))); - crate::http::header::common_header_test!(test3, vec![b"*"], Some(IfMatch::Any)); + crate::http::header::common_header_test!(test3, [b"*"], Some(IfMatch::Any)); } } diff --git a/actix-web/src/http/header/if_modified_since.rs b/actix-web/src/http/header/if_modified_since.rs index 897210944..8547ff490 100644 --- a/actix-web/src/http/header/if_modified_since.rs +++ b/actix-web/src/http/header/if_modified_since.rs @@ -35,6 +35,6 @@ crate::http::header::common_header! { test_parse_and_format { // Test case from RFC - crate::http::header::common_header_test!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); + crate::http::header::common_header_test!(test1, [b"Sat, 29 Oct 1994 19:43:31 GMT"]); } } diff --git a/actix-web/src/http/header/if_none_match.rs b/actix-web/src/http/header/if_none_match.rs index 86d7da9b2..1a424df96 100644 --- a/actix-web/src/http/header/if_none_match.rs +++ b/actix-web/src/http/header/if_none_match.rs @@ -52,11 +52,11 @@ crate::http::header::common_header! { (IfNoneMatch, IF_NONE_MATCH) => {Any / (EntityTag)+} test_parse_and_format { - crate::http::header::common_header_test!(test1, vec![b"\"xyzzy\""]); - crate::http::header::common_header_test!(test2, vec![b"W/\"xyzzy\""]); - crate::http::header::common_header_test!(test3, vec![b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""]); - crate::http::header::common_header_test!(test4, vec![b"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\""]); - crate::http::header::common_header_test!(test5, vec![b"*"]); + crate::http::header::common_header_test!(test1, [b"\"xyzzy\""]); + crate::http::header::common_header_test!(test2, [b"W/\"xyzzy\""]); + crate::http::header::common_header_test!(test3, [b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""]); + crate::http::header::common_header_test!(test4, [b"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\""]); + crate::http::header::common_header_test!(test5, [b"*"]); } } diff --git a/actix-web/src/http/header/if_range.rs b/actix-web/src/http/header/if_range.rs index d7375c4c5..3e8727ab0 100644 --- a/actix-web/src/http/header/if_range.rs +++ b/actix-web/src/http/header/if_range.rs @@ -111,7 +111,7 @@ mod test_parse_and_format { use super::IfRange as HeaderField; use crate::http::header::*; - crate::http::header::common_header_test!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); - crate::http::header::common_header_test!(test2, vec![b"\"abc\""]); - crate::http::header::common_header_test!(test3, vec![b"this-is-invalid"], None::); + crate::http::header::common_header_test!(test1, [b"Sat, 29 Oct 1994 19:43:31 GMT"]); + crate::http::header::common_header_test!(test2, [b"\"abc\""]); + crate::http::header::common_header_test!(test3, [b"this-is-invalid"], None::); } diff --git a/actix-web/src/http/header/if_unmodified_since.rs b/actix-web/src/http/header/if_unmodified_since.rs index 2ee3160b4..afa4eb8e5 100644 --- a/actix-web/src/http/header/if_unmodified_since.rs +++ b/actix-web/src/http/header/if_unmodified_since.rs @@ -35,6 +35,6 @@ crate::http::header::common_header! { test_parse_and_format { // Test case from RFC - crate::http::header::common_header_test!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); + crate::http::header::common_header_test!(test1, [b"Sat, 29 Oct 1994 19:43:31 GMT"]); } } diff --git a/actix-web/src/http/header/last_modified.rs b/actix-web/src/http/header/last_modified.rs index 59e649bea..724a38bbc 100644 --- a/actix-web/src/http/header/last_modified.rs +++ b/actix-web/src/http/header/last_modified.rs @@ -34,6 +34,6 @@ crate::http::header::common_header! { test_parse_and_format { // Test case from RFC - crate::http::header::common_header_test!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]); + crate::http::header::common_header_test!(test1, [b"Sat, 29 Oct 1994 19:43:31 GMT"]); } }