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};