serde_qs/config.rs
1use std::io::Write;
2
3use serde::de;
4
5use crate::error::Result;
6use crate::{Deserializer, Serializer};
7
8/// Configuration for serialization and deserialization behavior.
9///
10/// The `Config` struct allows you to customize how `serde_qs` handles
11/// querystrings, including nesting depth limits and encoding preferences.
12///
13/// ## Nesting Depth
14///
15/// The `max_depth` parameter controls how deeply nested structures can be.
16/// This is important for preventing denial-of-service attacks from maliciously
17/// crafted inputs with excessive nesting. A `max_depth` of 0 means no nesting
18/// is allowed (flat key-value pairs only).
19///
20/// Default value: `max_depth = 5`
21///
22/// ```
23/// use serde_qs::Config;
24/// use std::collections::HashMap;
25///
26/// let config = Config::new().max_depth(0);;
27/// let map: HashMap<String, String> = config.deserialize_str("a[b][c]=1")
28/// .unwrap();
29/// assert_eq!(map.get("a[b][c]").unwrap(), "1");
30///
31/// let config = Config::new().max_depth(10);;
32/// let map: HashMap<String, HashMap<String, HashMap<String, String>>> =
33/// config.deserialize_str("a[b][c]=1").unwrap();
34/// assert_eq!(map.get("a").unwrap().get("b").unwrap().get("c").unwrap(), "1");
35/// ```
36///
37#[derive(Clone, Copy, Debug)]
38pub struct Config {
39 pub(crate) max_depth: usize,
40 pub(crate) use_form_encoding: bool,
41 pub(crate) array_format: ArrayFormat,
42 pub(crate) duplicate_key_behavior: DuplicateKeyBehavior,
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum ArrayFormat {
47 /// Use the `a[0]=1&a[1]=2` format.
48 Indexed,
49 /// Use the `a[]=1&a[]=2` format.
50 EmptyIndexed,
51 /// Use the `a=1&a=2` format.
52 Unindexed,
53}
54
55/// Controls behavior when a scalar field receives multiple values.
56///
57/// By default, `serde_qs` follows the Rails convention of taking the last value
58/// when multiple values are provided for a scalar (non-sequence) field.
59///
60/// ```
61/// use serde::Deserialize;
62/// use serde_qs::{Config, DuplicateKeyBehavior};
63///
64/// #[derive(Deserialize)]
65/// struct Query {
66/// field: String,
67/// }
68///
69/// // Default behavior: take the last value
70/// let q: Query = serde_qs::from_str("field=a&field=b").unwrap();
71/// assert_eq!(q.field, "b");
72///
73/// // With Error behavior: return an error
74/// let config = Config::new().duplicate_key_behavior(DuplicateKeyBehavior::Error);
75/// let result: Result<Query, _> = config.deserialize_str("field=a&field=b");
76/// assert!(result.is_err());
77/// ```
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
79pub enum DuplicateKeyBehavior {
80 /// Take the last value (default, matches Rails convention).
81 #[default]
82 TakeLast,
83 /// Return an error when multiple values are provided.
84 Error,
85}
86
87impl Default for Config {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl Config {
94 pub const fn new() -> Self {
95 Self {
96 max_depth: 5,
97 use_form_encoding: cfg!(feature = "default_to_form_encoding"),
98 array_format: ArrayFormat::Indexed,
99 duplicate_key_behavior: DuplicateKeyBehavior::TakeLast,
100 }
101 }
102
103 /// Specifies the maximum depth key that `serde_qs` will attempt to
104 /// deserialize. Default is 5.
105 pub const fn max_depth(mut self, max_depth: usize) -> Self {
106 self.max_depth = max_depth;
107 self
108 }
109
110 /// By default, `serde_qs` uses query-string encoding, as defined
111 /// in [WHATWG](https://url.spec.whatwg.org/#query-percent-encode-set).
112 ///
113 /// This is a relatively lax encoding scheme, which does not
114 /// percent encode many characters (like square brackets).
115 ///
116 /// This makes it possible to encode nested keys like `a[b][c]=1`
117 /// in a relatively compact way. Keys that include square brackets
118 /// will get percent-encoded.
119 ///
120 /// e.g. `{ a: { "[x]": 1 } }` will be encoded as `a[%5Bx%5D]=1`
121 ///
122 /// Note that when using form encoding this means the keys will get
123 /// percent-encoded _twice_.
124 ///
125 /// e.g. `{ a: { "[x]": 1 } }` will be encoded as `a%5B%255Bx%255D%5D=1`
126 ///
127 /// To use form encoding, set this to `true`.
128 /// Alternatively, you can use the `default_to_form_encoding` Cargo feature
129 /// to set this to `true` by default.
130 pub const fn use_form_encoding(mut self, use_form_encoding: bool) -> Self {
131 self.use_form_encoding = use_form_encoding;
132 self
133 }
134
135 /// Specifies how arrays should be formatted in the querystring
136 /// during serialization.
137 ///
138 /// The default is `Indexed`, which results in keys like `a[0]=1&a[1]=2`.
139 pub const fn array_format(mut self, array_format: ArrayFormat) -> Self {
140 self.array_format = array_format;
141 self
142 }
143
144 /// Specifies how duplicate keys for scalar fields should be handled.
145 ///
146 /// By default, `serde_qs` takes the last value when multiple values are
147 /// provided for a scalar field (matching Rails convention). Set this to
148 /// [`DuplicateKeyBehavior::Error`] to return an error instead.
149 pub const fn duplicate_key_behavior(
150 mut self,
151 duplicate_key_behavior: DuplicateKeyBehavior,
152 ) -> Self {
153 self.duplicate_key_behavior = duplicate_key_behavior;
154 self
155 }
156
157 /// Deserializes a querystring from a `&[u8]` using this `Config`.
158 pub fn deserialize_bytes<'de, T: de::Deserialize<'de>>(self, input: &'de [u8]) -> Result<T> {
159 T::deserialize(Deserializer::with_config(self, input)?)
160 }
161
162 /// Deserializes a querystring from a `&str` using this `Config`.
163 pub fn deserialize_str<'de, T: de::Deserialize<'de>>(self, input: &'de str) -> Result<T> {
164 self.deserialize_bytes(input.as_bytes())
165 }
166
167 /// Serializes an object to a querystring using this `Config`.
168 pub fn serialize_string<T: serde::Serialize>(self, input: &T) -> Result<String> {
169 // initialize the buffer with 128 bytes
170 // this is a guess based on what `serde_json` does
171 let mut buffer = Vec::with_capacity(128);
172 let mut serializer = Serializer::new(&mut buffer, self);
173 input.serialize(&mut serializer)?;
174 String::from_utf8(buffer).map_err(crate::Error::from)
175 }
176
177 /// Serializes an object to a querystring using this `Config`.
178 pub fn serialize_to_writer<T: serde::Serialize, W: Write>(
179 self,
180 input: &T,
181 writer: &mut W,
182 ) -> Result<()> {
183 let mut serializer = Serializer::new(writer, self);
184 input.serialize(&mut serializer)
185 }
186}