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).
79//! Strict mode has two parts:
80//! - how `serde_qs` handles square brackets
81//! - how `serde_qs` handles invalid UTF-8 percent decoded characters
82//!
83//! ### Square Brackets
84//!
85//! Technically, square brackets should be encoded in URLs as `%5B` and `%5D`.
86//! However, they are often used in their raw format to specify querystrings
87//! such as `a[b]=123`.
88//!
89//! In strict mode, `serde_qs` will only tolerate unencoded square brackets
90//! to denote nested keys. So `a[b]=123` will decode as `{"a": {"b": 123}}`.
91//! This means that encoded square brackets can actually be part of the key.
92//! `a[b%5Bc%5D]=123` becomes `{"a": {"b[c]": 123}}`.
93//!
94//! However, since some implementations will automatically encode everything
95//! in the URL, we also have a non-strict mode. This means that `serde_qs`
96//! will assume that any encoded square brackets in the string were meant to
97//! be taken as nested keys. From the example before, `a[b%5Bc%5D]=123` will
98//! now become `{"a": {"b": {"c": 123 }}}`.
99//!
100//! Non-strict mode can be useful when, as said before, some middleware
101//! automatically encodes the brackets. But care must be taken to avoid
102//! using keys with square brackets in them, or unexpected things can
103//! happen.
104//!
105//! ### Invalid UTF-8 Percent Encodings
106//!
107//! Sometimes querystrings may have percent-encoded data which does not decode
108//! to UTF-8. In some cases it is useful for this to cause errors, which is how
109//! `serde_qs` works in strict mode (the default). Whereas in other cases it
110//! can be useful to just replace such data with the unicode replacement
111//! character (� `U+FFFD`), which is how `serde_qs` works in non-strict mode.
112//!
113//! ## Flatten workaround
114//!
115//! A current [known limitation](https://github.com/serde-rs/serde/issues/1183)
116//! in `serde` is deserializing `#[serde(flatten)]` structs for formats which
117//! are not self-describing. This includes query strings: `12` can be an integer
118//! or a string, for example.
119//!
120//! We suggest the following workaround:
121//!
122//! ```
123//! extern crate serde;
124//! #[macro_use]
125//! extern crate serde_derive;
126//! extern crate serde_qs as qs;
127//! extern crate serde_with;
128//!
129//! use serde_with::{serde_as, DisplayFromStr};
130//!
131//! #[derive(Deserialize, Serialize, Debug, PartialEq)]
132//! struct Query {
133//!     a: u8,
134//!     #[serde(flatten)]
135//!     common: CommonParams,
136//! }
137//!
138//! #[serde_as]
139//! #[derive(Deserialize, Serialize, Debug, PartialEq)]
140//! struct CommonParams {
141//!     #[serde_as(as = "DisplayFromStr")]
142//!     limit: u64,
143//!     #[serde_as(as = "DisplayFromStr")]
144//!     offset: u64,
145//!     #[serde_as(as = "DisplayFromStr")]
146//!     remaining: bool,
147//! }
148//!
149//! fn main() {
150//!     let params = "a=1&limit=100&offset=50&remaining=true";
151//!     let query = Query { a: 1, common: CommonParams { limit: 100, offset: 50, remaining: true } };
152//!     let rec_query: Result<Query, _> = qs::from_str(params);
153//!     assert_eq!(rec_query.unwrap(), query);
154//! }
155//! ```
156//!
157//! ## Use with `actix_web` extractors
158//!
159//! The `actix4`, `actix3` or `actix2` features enable the use of `serde_qs::actix::QsQuery`, which
160//! is a direct substitute for the `actix_web::Query` and can be used as an extractor:
161//!
162//! ```ignore
163//! fn index(info: QsQuery<Info>) -> Result<String> {
164//!     Ok(format!("Welcome {}!", info.username))
165//! }
166//! ```
167//!
168//! Support for `actix-web 4.0` is available via the `actix4` feature.
169//! Support for `actix-web 3.0` is available via the `actix3` feature.
170//! Support for `actix-web 2.0` is available via the `actix2` feature.
171//!
172//! ## Use with `warp` filters
173//!
174//! The `warp` feature enables the use of `serde_qs::warp::query()`, which
175//! is a substitute for the `warp::query::query()` filter and can be used like this:
176//!
177//! ```ignore
178//! serde_qs::warp::query(Config::default())
179//!     .and_then(|info| async move {
180//!         Ok::<_, Rejection>(format!("Welcome {}!", info.username))
181//!     })
182//!     .recover(serde_qs::warp::recover_fn);
183//! ```
184//!
185
186#[macro_use]
187extern crate serde;
188
189#[cfg(any(feature = "actix4", feature = "actix3", feature = "actix2"))]
190pub mod actix;
191
192#[cfg(feature = "actix")]
193compile_error!(
194    r#"The `actix` feature was removed in v0.9 due to the proliferation of actix versions.
195You must now specify the desired actix version by number.
196
197E.g.
198
199serde_qs = { version = "0.9", features = ["actix4"] }
200
201"#
202);
203
204mod de;
205mod error;
206mod ser;
207pub(crate) mod utils;
208
209#[doc(inline)]
210pub use de::Config;
211#[doc(inline)]
212pub use de::{from_bytes, from_str};
213pub use error::Error;
214#[doc(inline)]
215pub use ser::{to_string, to_writer, Serializer};
216
217#[cfg(feature = "axum")]
218pub mod axum;
219
220#[cfg(feature = "warp")]
221pub mod warp;