Skip to main content

qs_rust/
decode.rs

1//! Public query-string decoding entrypoints.
2
3mod accumulate;
4mod flat;
5mod keys;
6mod scalar;
7mod scan;
8mod structured;
9
10use crate::error::DecodeError;
11use crate::options::DecodeOptions;
12use crate::structured_scan::scan_structured_keys;
13use crate::value::{Object, Value};
14
15use self::flat::{collect_pair_values, finalize_flat};
16pub(crate) use self::keys::split_key_into_segments;
17use self::scan::parse_query_string_values;
18use self::structured::decode_from_pairs_map;
19
20#[cfg(test)]
21use self::accumulate::combine_with_limit;
22#[cfg(test)]
23use self::flat::{DefaultAccumulator, FlatValues, ParsedFlatValue, value_list_length_for_combine};
24#[cfg(test)]
25use self::keys::{dot_to_bracket_top_level, find_recoverable_balanced_open, parse_keys};
26#[cfg(test)]
27use self::scalar::{
28    decode_component, decode_scalar, interpret_numeric_entities, interpret_numeric_entities_in_node,
29};
30/// Decodes a query string into an ordered object map.
31///
32/// The result preserves flat outputs when the input contains no structured key
33/// syntax, and falls back to the structured merge pipeline when bracket or dot
34/// notation is present.
35///
36/// # Errors
37///
38/// Returns [`DecodeError`] when the supplied [`DecodeOptions`] are invalid or
39/// when the input exceeds a configured limit that is enforced strictly.
40///
41/// # Examples
42///
43/// ```
44/// use qs_rust::{DecodeOptions, Value, decode};
45///
46/// let value = decode("a=1&b=2", &DecodeOptions::new()).unwrap();
47/// assert_eq!(value["a"], Value::String("1".to_owned()));
48/// assert_eq!(value["b"], Value::String("2".to_owned()));
49/// ```
50pub fn decode(input: &str, options: &DecodeOptions) -> Result<Object, DecodeError> {
51    options.validate()?;
52
53    if input.is_empty() {
54        return Ok(Object::new());
55    }
56
57    let parsed = parse_query_string_values(input, options)?;
58    if parsed.values.is_empty() {
59        return Ok(Object::new());
60    }
61
62    if !parsed.has_any_structured_syntax {
63        return finalize_flat(parsed.values, options);
64    }
65
66    let structured_scan = scan_structured_keys(parsed.values.key_refs(), options)?;
67    if !structured_scan.has_any_structured_syntax {
68        return finalize_flat(parsed.values, options);
69    }
70
71    decode_from_pairs_map(parsed.values, options, &structured_scan)
72}
73
74/// Decodes an iterator of already-separated key/value pairs into an ordered
75/// object map.
76///
77/// This entrypoint skips raw query-string scanning but otherwise applies the
78/// same flat-finalization and structured-merge rules as [`decode`].
79///
80/// # Errors
81///
82/// Returns [`DecodeError`] when the supplied [`DecodeOptions`] are invalid or
83/// when a configured list or depth limit is exceeded during reconstruction.
84pub fn decode_pairs<I>(pairs: I, options: &DecodeOptions) -> Result<Object, DecodeError>
85where
86    I: IntoIterator<Item = (String, Value)>,
87{
88    options.validate()?;
89
90    let parsed = collect_pair_values(pairs, options)?;
91    if parsed.values.is_empty() {
92        return Ok(Object::new());
93    }
94
95    if !parsed.has_any_structured_syntax {
96        return finalize_flat(parsed.values, options);
97    }
98
99    let structured_scan = scan_structured_keys(parsed.values.key_refs(), options)?;
100    if !structured_scan.has_any_structured_syntax {
101        return finalize_flat(parsed.values, options);
102    }
103
104    decode_from_pairs_map(parsed.values, options, &structured_scan)
105}
106
107#[cfg(test)]
108mod tests;