diff options
author | Sam Scott <sam.scott89@gmail.com> | 2020-06-03 10:26:45 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-03 10:26:45 -0400 |
commit | 77cb6730f9265591dad141f4f9b840069c9cd2b9 (patch) | |
tree | 6c4cd170f74d0c789e1059a91514d04dc06decba | |
parent | d5c2d3e44a5cbe2311111fb56bfe6bed8fabd961 (diff) |
Support actix-web v2 (#30)
* update dependencies
- actix-web v2
- percent encoding v2.1
- rust 2018 edition
- remove rustfmt no longer supported rules
* ci: add feature build matrix
* fix actix unit tests
Co-authored-by: Mario Reder <mreder1289@gmail.com>
-rw-r--r-- | .travis.yml | 9 | ||||
-rw-r--r-- | Cargo.toml | 18 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | rustfmt.toml | 3 | ||||
-rw-r--r-- | src/actix.rs | 25 | ||||
-rw-r--r-- | src/de/mod.rs | 2 | ||||
-rw-r--r-- | src/de/parse.rs | 40 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/ser.rs | 2 | ||||
-rw-r--r-- | tests/test_actix.rs | 143 | ||||
-rw-r--r-- | tests/test_deserialize.rs | 2 | ||||
-rw-r--r-- | tests/test_serialize.rs | 3 |
12 files changed, 139 insertions, 114 deletions
diff --git a/.travis.yml b/.travis.yml index 6e09135..f698d07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,11 @@ rust: - nightly matrix: allow_failures: - - rust: nightly
\ No newline at end of file + - rust: nightly +env: + - ARGS="--verbose" + - ARGS="--verbose --features=actix" + +script: + - cargo build $ARGS + - cargo test $ARGS @@ -1,5 +1,6 @@ [package] authors = ["Sam Scott <me@samjs.co.uk>"] +edition = "2018" categories = ["encoding", "web-programming"] description = "Querystrings for Serde" documentation = "https://docs.rs/serde_qs" @@ -8,7 +9,7 @@ license = "MIT/Apache-2.0" name = "serde_qs" repository = "https://github.com/samscott89/serde_qs" readme = "README.md" -version = "0.5.1" +version = "0.6.0" [badges] @@ -16,21 +17,22 @@ version = "0.5.1" repository = "samscott89/serde_qs" [dependencies] -actix-web = { version ="1.0.0", optional = true } -data-encoding = "2.1.2" -error-chain = "0.12.0" +actix-web = { version = "2.0", optional = true } +data-encoding = "2.2.1" +error-chain = "0.12.2" +futures = { version = "0.3", optional = true } percent-encoding = "2.1.0" -serde = "1.0.85" +serde = "1.0.111" [dev-dependencies] -csv = "1.0.5" +csv = "1.1.3" rand = "0.7.3" -serde_derive = "1.0.85" +serde_derive = "1.0.111" serde_urlencoded = "0.6.1" [features] default = [] -actix = ["actix-web"] +actix = ["actix-web", "futures"] [package.metadata.docs.rs] features = [ "actix" ] @@ -33,7 +33,7 @@ This crate works with Cargo and can be found on ```toml [dependencies] -serde_qs = "0.5" +serde_qs = "0.6" ``` [crates.io]: https://crates.io/crates/serde_qs diff --git a/rustfmt.toml b/rustfmt.toml index 70b05df..e696b4e 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,7 +1,6 @@ match_block_trailing_comma = true max_width = 80 newline_style = "Unix" -reorder_imported_names = true reorder_imports = true use_try_shorthand = true -where_trailing_comma = true +trailing_comma = "Vertical" diff --git a/src/actix.rs b/src/actix.rs index fa7e105..70dc6e1 100644 --- a/src/actix.rs +++ b/src/actix.rs @@ -2,12 +2,14 @@ //! //! Enable with the `actix` feature. +use crate::de::Config as QsConfig; +use crate::error::Error as QsError; + use actix_web::dev::Payload; use actix_web::{ Error as ActixError, FromRequest, HttpRequest, HttpResponse, ResponseError, }; -use de::Config as QsConfig; -use error::Error as QsError; +use futures::future::{ready, Ready}; use serde::de; use std::fmt; use std::fmt::{Debug, Display}; @@ -28,7 +30,7 @@ impl ResponseError for QsError { /// ```rust /// #[macro_use] extern crate serde_derive; /// extern crate actix_web; -/// use actix_web::{web, App}; +/// use actix_web::{web, App, HttpResponse}; /// use serde_qs::actix::QsQuery; /// /// #[derive(Deserialize)] @@ -38,7 +40,7 @@ impl ResponseError for QsError { /// /// // Use `QsQuery` extractor for query information. /// // The correct request for this handler would be `/users?id[]=1124&id[]=88"` -/// fn filter_users(info: QsQuery<UsersFilter>) -> String { +/// fn filter_users(info: QsQuery<UsersFilter>) -> HttpResponse { /// info.id.iter().map(|i| i.to_string()).collect::<Vec<String>>().join(", ").into() /// } /// @@ -88,7 +90,7 @@ where T: de::DeserializeOwned, { type Error = ActixError; - type Future = Result<Self, ActixError>; + type Future = Ready<Result<Self, ActixError>>; type Config = QsQueryConfig; #[inline] @@ -103,7 +105,7 @@ where .map(|c| &c.qs_config) .unwrap_or(&default_qsconfig); - qsconfig + let res = qsconfig .deserialize_str::<T>(req.query_string()) .map(|val| Ok(QsQuery(val))) .unwrap_or_else(move |e| { @@ -114,7 +116,8 @@ where }; Err(e) - }) + }); + ready(res) } } @@ -133,13 +136,13 @@ where /// } /// /// /// deserialize `Info` from request's querystring -/// fn index(info: QsQuery<Info>) -> String { -/// format!("Welcome {}!", info.username) +/// fn index(info: QsQuery<Info>) -> HttpResponse { +/// format!("Welcome {}!", info.username).into() /// } /// /// fn main() { /// let app = App::new().service( -/// web::resource("/index.html").data( +/// web::resource("/index.html").app_data( /// // change query extractor configuration /// QsQuery::<Info>::configure(|cfg| { /// cfg.error_handler(|err, req| { // <- create custom error response @@ -155,7 +158,7 @@ where pub struct QsQueryConfig { ehandler: - Option<Arc<Fn(QsError, &HttpRequest) -> ActixError + Send + Sync>>, + Option<Arc<dyn Fn(QsError, &HttpRequest) -> ActixError + Send + Sync>>, qs_config: QsConfig, } diff --git a/src/de/mod.rs b/src/de/mod.rs index a82a37b..923c4b9 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -38,7 +38,7 @@ mod parse; -use error::*; +use crate::error::*; use serde::de; use serde::de::IntoDeserializer; diff --git a/src/de/parse.rs b/src/de/parse.rs index 6f10ac3..c23aa7e 100644 --- a/src/de/parse.rs +++ b/src/de/parse.rs @@ -1,6 +1,5 @@ use super::*; -use percent_encoding; use serde::de; use std::borrow::Cow; @@ -206,7 +205,7 @@ fn replace_plus(input: &[u8]) -> Cow<[u8]> { } Cow::Owned(replaced) - } + }, } } @@ -235,25 +234,26 @@ impl<'a> Parser<'a> { /// present. fn collect_str(&mut self) -> Result<Cow<'a, str>> { let replaced = replace_plus(&self.inner[self.acc.0..self.acc.1 - 1]); - let ret:Result<Cow<'a, str>> = match percent_encoding::percent_decode(&replaced).decode_utf8()? { - Cow::Borrowed(_) => { - match replaced { - Cow::Borrowed(_) => { - // In this case, neither method made replacements, so we - // reuse the original bytes - let res = str::from_utf8(&self.inner[self.acc.0..self.acc.1 - 1])?; - Ok(Cow::Borrowed(res)) - }, - Cow::Owned(owned) => { - let res = String::from_utf8(owned)?; - Ok(Cow::Owned(res)) + let ret: Result<Cow<'a, str>> = + match percent_encoding::percent_decode(&replaced).decode_utf8()? { + Cow::Borrowed(_) => { + match replaced { + Cow::Borrowed(_) => { + // In this case, neither method made replacements, so we + // reuse the original bytes + let res = str::from_utf8( + &self.inner[self.acc.0..self.acc.1 - 1], + )?; + Ok(Cow::Borrowed(res)) + }, + Cow::Owned(owned) => { + let res = String::from_utf8(owned)?; + Ok(Cow::Owned(res)) + }, } - } - }, - Cow::Owned(owned) => { - Ok(Cow::Owned(owned)) - } - }; + }, + Cow::Owned(owned) => Ok(Cow::Owned(owned)), + }; self.clear_acc(); ret.map_err(Error::from) } @@ -202,12 +202,8 @@ while_true )] -#[cfg(feature = "actix")] -extern crate actix_web; -extern crate data_encoding; #[macro_use] extern crate error_chain; -extern crate percent_encoding; #[macro_use] extern crate serde; @@ -4,7 +4,7 @@ use data_encoding::BASE64URL_NOPAD as BASE64; use percent_encoding::{percent_encode, AsciiSet, NON_ALPHANUMERIC}; use serde::ser; -use error::*; +use crate::error::*; use std::borrow::Cow; use std::fmt::Display; diff --git a/tests/test_actix.rs b/tests/test_actix.rs index 532fa7c..34969d4 100644 --- a/tests/test_actix.rs +++ b/tests/test_actix.rs @@ -44,84 +44,99 @@ struct CommonParams { #[test] fn test_default_error_handler() { - let req = TestRequest::with_uri("/test").to_srv_request(); - let (req, mut pl) = req.into_parts(); - - let e = QsQuery::<Query>::from_request(&req, &mut pl).unwrap_err(); - assert_eq!( - e.as_response_error().error_response().status(), - StatusCode::BAD_REQUEST - ); + futures::executor::block_on(async { + let req = TestRequest::with_uri("/test").to_srv_request(); + let (req, mut pl) = req.into_parts(); + + let e = QsQuery::<Query>::from_request(&req, &mut pl) + .await + .unwrap_err(); + assert_eq!( + e.as_response_error().error_response().status(), + StatusCode::BAD_REQUEST + ); + }) } #[test] fn test_custom_error_handler() { - let req = TestRequest::with_uri("/test") - .data(QsQueryConfig::default().error_handler(|e, _| { - let resp = HttpResponse::UnprocessableEntity().finish(); - InternalError::from_response(e, resp).into() - })) - .to_srv_request(); - - let (req, mut pl) = req.into_parts(); - let query = QsQuery::<Query>::from_request(&req, &mut pl); - - assert!(query.is_err()); - assert_eq!( - query - .unwrap_err() - .as_response_error() - .error_response() - .status(), - StatusCode::UNPROCESSABLE_ENTITY - ); + futures::executor::block_on(async { + let req = TestRequest::with_uri("/test") + .app_data(QsQueryConfig::default().error_handler(|e, _| { + let resp = HttpResponse::UnprocessableEntity().finish(); + dbg!(&resp); + InternalError::from_response(e, resp).into() + })) + .to_srv_request(); + + let (req, mut pl) = req.into_parts(); + let query = QsQuery::<Query>::from_request(&req, &mut pl).await; + + assert!(query.is_err()); + assert_eq!( + query + .unwrap_err() + .as_response_error() + .error_response() + .status(), + StatusCode::UNPROCESSABLE_ENTITY + ); + }) } #[test] fn test_composite_querystring_extractor() { - let req = TestRequest::with_uri( - "/test?foo=1&bars[]=0&bars[]=1&limit=100&offset=50&remaining=true", - ) - .to_srv_request(); - let (req, mut pl) = req.into_parts(); - - let s = QsQuery::<Query>::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.foo, 1); - assert_eq!(s.bars, vec![0, 1]); - assert_eq!(s.common.limit, 100); - assert_eq!(s.common.offset, 50); - assert_eq!(s.common.remaining, true); + futures::executor::block_on(async { + let req = TestRequest::with_uri( + "/test?foo=1&bars[]=0&bars[]=1&limit=100&offset=50&remaining=true", + ) + .to_srv_request(); + let (req, mut pl) = req.into_parts(); + + let s = QsQuery::<Query>::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.foo, 1); + assert_eq!(s.bars, vec![0, 1]); + assert_eq!(s.common.limit, 100); + assert_eq!(s.common.offset, 50); + assert_eq!(s.common.remaining, true); + }) } #[test] fn test_default_qs_config() { - let req = TestRequest::with_uri( - "/test?foo=1&bars%5B%5D=3&limit=100&offset=50&remaining=true", - ) - .to_srv_request(); - let (req, mut pl) = req.into_parts(); - - let e = QsQuery::<Query>::from_request(&req, &mut pl).unwrap_err(); - assert_eq!( - e.as_response_error().error_response().status(), - StatusCode::BAD_REQUEST - ); + futures::executor::block_on(async { + let req = TestRequest::with_uri( + "/test?foo=1&bars%5B%5D=3&limit=100&offset=50&remaining=true", + ) + .to_srv_request(); + let (req, mut pl) = req.into_parts(); + + let e = QsQuery::<Query>::from_request(&req, &mut pl) + .await + .unwrap_err(); + assert_eq!( + e.as_response_error().error_response().status(), + StatusCode::BAD_REQUEST + ); + }) } #[test] fn test_custom_qs_config() { - let req = TestRequest::with_uri( - "/test?foo=1&bars%5B%5D=3&limit=100&offset=50&remaining=true", - ) - .data(QsQueryConfig::default().qs_config(QsConfig::new(5, false))) - .to_srv_request(); - - let (req, mut pl) = req.into_parts(); - - let s = QsQuery::<Query>::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.foo, 1); - assert_eq!(s.bars, vec![3]); - assert_eq!(s.common.limit, 100); - assert_eq!(s.common.offset, 50); - assert_eq!(s.common.remaining, true); + futures::executor::block_on(async { + let req = TestRequest::with_uri( + "/test?foo=1&bars%5B%5D=3&limit=100&offset=50&remaining=true", + ) + .app_data(QsQueryConfig::default().qs_config(QsConfig::new(5, false))) + .to_srv_request(); + + let (req, mut pl) = req.into_parts(); + + let s = QsQuery::<Query>::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.foo, 1); + assert_eq!(s.bars, vec![3]); + assert_eq!(s.common.limit, 100); + assert_eq!(s.common.offset, 50); + assert_eq!(s.common.remaining, true); + }) } diff --git a/tests/test_deserialize.rs b/tests/test_deserialize.rs index beede63..4e02823 100644 --- a/tests/test_deserialize.rs +++ b/tests/test_deserialize.rs @@ -609,4 +609,4 @@ fn deserialize_plus() { let test: Test = serde_qs::from_str("email=a%2Bb%40c.com").unwrap(); assert_eq!(test.email, "a+b@c.com"); -}
\ No newline at end of file +} diff --git a/tests/test_serialize.rs b/tests/test_serialize.rs index 8adae57..f650beb 100644 --- a/tests/test_serialize.rs +++ b/tests/test_serialize.rs @@ -5,6 +5,7 @@ extern crate serde_qs as qs; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] struct Address { city: String, + street: String, postcode: String, } @@ -25,6 +26,7 @@ fn serialize_struct() { phone: 12345, address: Address { city: "Carrot City".to_string(), + street: "Special-Street* No. 11".to_string(), postcode: "12345".to_string(), }, user_ids: vec![1, 2, 3, 4], @@ -34,6 +36,7 @@ fn serialize_struct() { qs::to_string(¶ms).unwrap(), "\ id=42&name=Acme&phone=12345&address[city]=Carrot+City&\ + address[street]=Special-Street*+No.+11&\ address[postcode]=12345&user_ids[0]=1&user_ids[1]=2&\ user_ids[2]=3&user_ids[3]=4" ); |