summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Scott <sam.scott89@gmail.com>2019-01-23 12:06:41 -0500
committerSam Scott <sam.scott89@gmail.com>2019-01-23 12:06:41 -0500
commit30c3802305fab702c1704091013c5af874997c01 (patch)
tree41bd13460f5752caed77ec459efe9264b1629e24 /src
parent3fe8fc3dc024cf46c25223ce3f8fb7bcbfb15547 (diff)
Add feature for easy use in `actix_web` extractors.
Diffstat (limited to 'src')
-rw-r--r--src/actix.rs148
-rw-r--r--src/lib.rs15
2 files changed, 163 insertions, 0 deletions
diff --git a/src/actix.rs b/src/actix.rs
new file mode 100644
index 0000000..06cafc6
--- /dev/null
+++ b/src/actix.rs
@@ -0,0 +1,148 @@
+//! Functionality for using `serde_qs` with `actix_web`.
+//!
+//! Enable with the `actix` feature.
+
+use actix_web::FromRequest;
+use actix_web::HttpRequest;
+use serde::de;
+
+use std::ops::{Deref, DerefMut};
+use std::rc::Rc;
+use std::fmt;
+
+use error::Error as QsError;
+
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
+/// Extract typed information from from the request's query.
+/// `serde_qs` equivalent to `actix_web::Query`.
+///
+/// ## Example
+///
+/// ```rust
+/// # extern crate actix_web;
+/// # extern crate serde_qs;
+/// #[macro_use] extern crate serde_derive;
+/// use actix_web::{App, http};
+/// use serde_qs::actix::QsQuery;
+///
+///#[derive(Deserialize)]
+///pub struct Request {
+/// id: Vec<u64>,
+///}
+///
+/// // use `with` extractor for query info
+/// // this handler get called only if request's query contains `username` field
+/// // The correct request for this handler would be `/index.html?id[]=1&id[]=2"`
+/// fn index(info: QsQuery<Request>) -> String {
+/// format!("Request for client with list of ids={:?}", info.id)
+/// }
+///
+/// fn main() {
+/// let app = App::new().resource(
+/// "/index.html",
+/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
+/// }
+/// ```
+pub struct QsQuery<T>(T);
+
+impl<T> Deref for QsQuery<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.0
+ }
+}
+
+impl<T> DerefMut for QsQuery<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.0
+ }
+}
+
+impl<T> QsQuery<T> {
+ /// Deconstruct to a inner value
+ pub fn into_inner(self) -> T {
+ self.0
+ }
+}
+
+impl<T, S> FromRequest<S> for QsQuery<T>
+where
+ T: de::DeserializeOwned,
+{
+ type Config = QsQueryConfig<S>;
+ type Result = Result<Self, actix_web::Error>;
+
+ #[inline]
+ fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
+ let req2 = req.clone();
+ let err = Rc::clone(&cfg.ehandler);
+ super::from_str::<T>(req.query_string())
+ .map_err(move |e| (*err)(e, &req2))
+ .map(QsQuery)
+ }
+}
+
+/// QsQuery extractor configuration
+///
+/// ```rust
+/// # extern crate actix_web;
+/// # extern crate serde_qs;
+/// #[macro_use] extern crate serde_derive;
+/// use actix_web::{error, http, App, HttpResponse, Result};
+/// use serde_qs::actix::QsQuery;
+///
+/// #[derive(Deserialize)]
+/// struct Info {
+/// username: String,
+/// }
+///
+/// /// deserialize `Info` from request's body, max payload size is 4kb
+/// fn index(info: QsQuery<Info>) -> Result<String> {
+/// Ok(format!("Welcome {}!", info.username))
+/// }
+///
+/// fn main() {
+/// let app = App::new().resource("/index.html", |r| {
+/// r.method(http::Method::GET).with_config(index, |cfg| {
+/// cfg.0.error_handler(|err, req| {
+/// // <- create custom error response
+/// error::InternalError::from_response(err.description().to_string(), HttpResponse::Conflict().finish()).into()
+/// });
+/// })
+/// });
+/// }
+/// ```
+pub struct QsQueryConfig<S> {
+ ehandler: Rc<Fn(QsError, &HttpRequest<S>) -> actix_web::Error>,
+}
+impl<S> QsQueryConfig<S> {
+ /// Set custom error handler
+ pub fn error_handler<F>(&mut self, f: F) -> &mut Self
+ where
+ F: Fn(QsError, &HttpRequest<S>) -> actix_web::Error + 'static,
+ {
+ self.ehandler = Rc::new(f);
+ self
+ }
+}
+
+impl<S> Default for QsQueryConfig<S> {
+ fn default() -> Self {
+ QsQueryConfig {
+ ehandler: Rc::new(|_, _| actix_web::error::UrlencodedError::Parse.into()),
+ }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for QsQuery<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for QsQuery<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 53bfef2..b08c49c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -146,6 +146,17 @@
//! assert_eq!(rec_query.unwrap(), query);
//! }
//! ```
+//!
+//! ## Use with `actix_web` extractors
+//!
+//! The `actix` feature enables the use of `serde_qs::actix::QsQuery`, which
+//! is a direct substitute for the `actix_web::Query` and can be used as an extractor:
+//!
+//! ```ignore
+//! fn index(info: QsQuery<Info>) -> Result<String> {
+//! Ok(format!("Welcome {}!", info.username))
+//! }
+//! ```
#![allow()]
#![deny(
@@ -192,6 +203,8 @@
while_true
)]
+#[cfg(feature = "actix")]
+extern crate actix_web;
extern crate data_encoding;
#[macro_use]
extern crate error_chain;
@@ -199,6 +212,8 @@ extern crate percent_encoding;
#[macro_use]
extern crate serde;
+#[cfg(feature = "actix")]
+pub mod actix;
mod de;
mod error;
mod ser;