serde_qs/
lib.rs

1//! Serde support for querystring-style strings
2//!
3//! Querystrings are not formally defined and loosely take the form of
4//! _nested_ urlencoded queries.
5//!
6//! This library aims for compatability with the syntax of
7//! [qs](https://github.com/ljharb/qs) and also of the
8//! [`Rack::Utils::parse_nested_query`](http://www.rubydoc.info/github/rack/rack/Rack/Utils#parse_nested_query-class_method)
9//! implementation.
10//!
11//! For users who do *not* require nested URL parameters, it is highly
12//! recommended that the `serde_urlencoded` crate is used instead, which
13//! will almost certainly perform better for deserializing simple inputs.
14//!
15//! ## Supported Types
16//!
17//! At the **top level**, `serde_qs` only supports `struct`, `map`, and `enum`.
18//! These are the only top-level structs which can be de/serialized since
19//! Querystrings rely on having a (key, value) pair for each field, which
20//! necessitates this kind of structure.
21//!
22//! However, after the top level you should find all supported types can be
23//! de/serialized.
24//!
25//! Note that integer keys are reserved for array indices. That is, a string of
26//! the form `a[0]=1&a[1]=3` will deserialize to the ordered sequence `a =
27//! [1,3]`.
28//!
29//! ## Usage
30//!
31//! See the examples folder for a more detailed introduction.
32//!
33//! Serializing/Deserializing is designed to work with maps and structs.
34//!
35//! ```
36//! #[macro_use]
37//! extern crate serde_derive;
38//! extern crate serde_qs as qs;
39//!
40//! #[derive(Debug, PartialEq, Deserialize, Serialize)]
41//! struct Address {
42//!     city: String,
43//!     postcode: String,
44//! }
45//! #[derive(Debug, PartialEq, Deserialize, Serialize)]
46//! struct QueryParams {
47//!     id: u8,
48//!     name: String,
49//!     address: Address,
50//!     phone: u32,
51//!     user_ids: Vec<u8>,
52//! }
53//!
54//! # fn main() {
55//! let params = QueryParams {
56//!     id: 42,
57//!     name: "Acme".to_string(),
58//!     phone: 12345,
59//!     address: Address {
60//!         city: "Carrot City".to_string(),
61//!         postcode: "12345".to_string(),
62//!     },
63//!     user_ids: vec![1, 2, 3, 4],
64//! };
65//! let rec_params: QueryParams = qs::from_str("\
66//!     name=Acme&id=42&phone=12345&address[postcode]=12345&\
67//!     address[city]=Carrot+City&user_ids[0]=1&user_ids[1]=2&\
68//!     user_ids[2]=3&user_ids[3]=4")
69//!     .unwrap();
70//! assert_eq!(rec_params, params);
71//!
72//! # }
73//! ```
74//!
75//! ## Strict vs Non-Strict modes
76//!
77//! `serde_qs` supports two operating modes, which can be specified using
78//! [`Config`](struct.Config.html), and is all about how `serde_qs` handles square brackets.
79//!
80//! Techncially, square brackets should be encoded in URLs as `%5B` and `%5D`.
81//! However, they are often used in their raw format to specify querystrings
82//! such as `a[b]=123`.
83//!
84//! In strict mode, `serde_qs` will only tolerate unencoded square brackets
85//! to denote nested keys. So `a[b]=123` will decode as `{"a": {"b": 123}}`.
86//! This means that encoded square brackets can actually be part of the key.
87//! `a[b%5Bc%5D]=123` becomes `{"a": {"b[c]": 123}}`.
88//!
89//! However, since some implementations will automatically encode everything
90//! in the URL, we also have a non-strict mode. This means that `serde_qs`
91//! will assume that any encoded square brackets in the string were meant to
92//! be taken as nested keys. From the example before, `a[b%5Bc%5D]=123` will
93//! now become `{"a": {"b": {"c": 123 }}}`.
94//!
95//! Non-strict mode can be useful when, as said before, some middleware
96//! automatically encodes the brackets. But care must be taken to avoid
97//! using keys with square brackets in them, or unexpected things can
98//! happen.
99//!
100//! ## Flatten workaround
101//!
102//! A current [known limitation](https://github.com/serde-rs/serde/issues/1183)
103//! in `serde` is deserializing `#[serde(flatten)]` structs for formats which
104//! are not self-describing. This includes query strings: `12` can be an integer
105//! or a string, for example.
106//!
107//! We suggest the following workaround:
108//!
109//! ```
110//! extern crate serde;
111//! #[macro_use]
112//! extern crate serde_derive;
113//! extern crate serde_qs as qs;
114//! extern crate serde_with;
115//!
116//! use serde_with::rust::display_fromstr::deserialize as deserialize_fromstr;
117//!
118//! #[derive(Deserialize, Serialize, Debug, PartialEq)]
119//! struct Query {
120//!     a: u8,
121//!     #[serde(flatten)]
122//!     common: CommonParams,
123//! }
124//!
125//! #[derive(Deserialize, Serialize, Debug, PartialEq)]
126//! struct CommonParams {
127//!     #[serde(deserialize_with="deserialize_fromstr")]
128//!     limit: u64,
129//!     #[serde(deserialize_with="deserialize_fromstr")]
130//!     offset: u64,
131//!     #[serde(deserialize_with="deserialize_fromstr")]
132//!     remaining: bool,
133//! }
134//!
135//! fn main() {
136//!     let params = "a=1&limit=100&offset=50&remaining=true";
137//!     let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } };
138//!     let rec_query: Result<Query, _> = qs::from_str(params);
139//!     assert_eq!(rec_query.unwrap(), query);
140//! }
141//! ```
142//!
143//! ## Use with `actix_web` extractors
144//!
145//! The `actix` feature enables the use of `serde_qs::actix::QsQuery`, which
146//! is a direct substitute for the `actix_web::Query` and can be used as an extractor:
147//!
148//! ```ignore
149//! fn index(info: QsQuery<Info>) -> Result<String> {
150//!     Ok(format!("Welcome {}!", info.username))
151//! }
152//! ```
153//!
154//! Support for `actix-web 2.0.0` is available via the `actix2` feature.
155//!
156//! ## Use with `warp` filters
157//!
158//! The `warp` feature enables the use of `serde_qs::warp::query()`, which
159//! is a substitute for the `warp::query::query()` filter and can be used like this:
160//!
161//! ```ignore
162//! serde_qs::warp::query(Config::default())
163//!     .and_then(|info| async move {
164//!         Ok::<_, Rejection>(format!("Welcome {}!", info.username))
165//!     })
166//!     .recover(serde_qs::warp::recover_fn);
167//! ```
168//!
169
170#[macro_use]
171extern crate serde;
172
173#[cfg(any(feature = "actix", feature = "actix2"))]
174pub mod actix;
175mod de;
176mod error;
177mod ser;
178#[cfg(feature = "warp")]
179pub mod warp;
180
181#[doc(inline)]
182pub use de::Config;
183#[doc(inline)]
184pub use de::{from_bytes, from_str};
185pub use error::Error;
186#[doc(inline)]
187pub use ser::{to_string, to_writer, QsSerializer};