Skip to main content

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}