Skip to main content

deser_core/de/
lexical.rs

1//! Parsing of lexical atoms.
2//!
3//! See [`Atom::Lexical`](crate::Atom::Lexical) and [`LexicalRules`].
4use alloc::format;
5use alloc::string::String;
6use core::fmt::Write;
7use core::num::{IntErrorKind, ParseIntError};
8
9use crate::State;
10use crate::error::{Error, ErrorKind, discarded_error};
11
12/// The longest part of a value that is included in error messages.
13const MAX_QUOTED: usize = 64;
14
15/// How [lexical atoms](crate::Atom::Lexical) are interpreted.
16///
17/// Lexical atoms are text whose type the format cannot express.  The types
18/// they are delivered to interpret them: numbers and booleans parse them,
19/// strings take them as they are.  What text means beyond that depends on
20/// where it comes from.  Keys of JSON objects are strict (a `bool` key is
21/// `true` or `false`), everything in a query string or an environment
22/// variable is text so `on` and `yes` are booleans too and empty values are
23/// missing values.
24///
25/// The rules are an extension value, the default are the
26/// [strict](Self::STRICT) rules.  Formats where everything is text have
27/// other defaults (query strings, environment variables and CSV use the
28/// [lenient](Self::LENIENT) rules), which rules in the
29/// [`Context`](crate::Context) (or in the [`State`], see
30/// [`set`](Self::set)) override.  As they are part of the state (and not
31/// of the atoms), lexical atoms that are buffered and replayed are
32/// interpreted with the rules of the deserialization they are replayed in.
33///
34/// ```
35/// use deser::de::{DeserializeDriver, LexicalRules};
36/// use deser::{Atom, Text};
37///
38/// let mut out = None::<bool>;
39/// let mut driver = DeserializeDriver::new(&mut out);
40/// LexicalRules::LENIENT.set(driver.state_mut());
41/// driver.emit(Atom::Lexical(Text::borrowed("on"))).unwrap();
42/// drop(driver);
43/// assert_eq!(out, Some(true));
44/// ```
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct LexicalRules {
47    lenient_bools: bool,
48    empty_is_null: bool,
49}
50
51impl LexicalRules {
52    /// The rules for text that happens to be text, like the keys of JSON
53    /// objects.  This is the default.
54    ///
55    /// * booleans are `true` and `false`
56    /// * integers and floats are parsed with [`str::parse`]
57    /// * empty text is not a missing value
58    pub const STRICT: LexicalRules = LexicalRules {
59        lenient_bools: false,
60        empty_is_null: false,
61    };
62
63    /// The rules for formats where everything is text, like query strings
64    /// and environment variables.
65    ///
66    /// * booleans are `true`, `yes`, `on` and `1` and `false`, `no`, `off`
67    ///   and `0` (ignoring ASCII case)
68    /// * integers and floats are parsed with [`str::parse`]
69    /// * empty text is a missing value for types that do not accept it:
70    ///   `None` for an `Option<u32>`, `Some("")` for an `Option<String>`
71    ///   and `()`
72    ///
73    /// These formats typically also allow keys to repeat, a key given once
74    /// can then stand for a sequence of one value.  That is not a rule of
75    /// the text but of the maps they emit (see
76    /// [`ContainerShape::set_multimap`](crate::ContainerShape::set_multimap)).
77    pub const LENIENT: LexicalRules = LexicalRules {
78        lenient_bools: true,
79        empty_is_null: true,
80    };
81
82    /// Returns the rules of a deserialization.
83    #[inline]
84    pub fn of(state: &State) -> LexicalRules {
85        state.get::<LexicalRules>().copied().unwrap_or_default()
86    }
87
88    /// Sets the rules of a deserialization.
89    #[inline]
90    pub fn set(self, state: &mut State) {
91        *state.get_mut::<LexicalRules>() = self;
92    }
93
94    /// Sets the rules unless the state or the context has rules.
95    ///
96    /// Formats use this for their default (see [`State::set_default`]).
97    #[inline]
98    pub fn set_default(self, state: &mut State) {
99        state.set_default(self);
100    }
101
102    /// Sets if booleans are also `yes`, `on` and `1` and `no`, `off` and
103    /// `0` (ignoring ASCII case).
104    pub const fn set_lenient_bools(&mut self, yes: bool) {
105        self.lenient_bools = yes;
106    }
107
108    /// Returns `true` if booleans are also `yes`, `on` and `1` and `no`,
109    /// `off` and `0` (see [`set_lenient_bools`](Self::set_lenient_bools)).
110    pub const fn lenient_bools(&self) -> bool {
111        self.lenient_bools
112    }
113
114    /// Sets if empty text is a missing value for types that do not accept
115    /// it.
116    pub const fn set_empty_is_null(&mut self, yes: bool) {
117        self.empty_is_null = yes;
118    }
119
120    /// Returns `true` if empty text is a missing value for types that do
121    /// not accept it (see [`set_empty_is_null`](Self::set_empty_is_null)).
122    pub const fn empty_is_null(&self) -> bool {
123        self.empty_is_null
124    }
125}
126
127impl Default for LexicalRules {
128    fn default() -> LexicalRules {
129        LexicalRules::STRICT
130    }
131}
132
133/// The key under which maps hold their own content.
134///
135/// In some formats a value is text or a map that holds the text together
136/// with more entries: the elements of XML are their text (`<count>3</count>`)
137/// or, if they have attributes, a map (`<count unit="m">3</count>` is
138/// `{"@unit": "m", "$text": "3"}`).  Which of the two a value is depends on
139/// the input, not on the type it's deserialized into.  Formats like this
140/// set the key of the content in the [`State`] (see [`set`](Self::set)),
141/// and values are then passed on in the form the type accepts:
142///
143/// * A type that rejects a map (like `u32`) receives the value of the key
144///   of the content, the other entries are skipped.  If the map has no
145///   such key it receives empty text (`""` as
146///   [lexical atom](crate::Atom::Lexical)).
147/// * A type that rejects [lexical atoms](crate::Atom::Lexical) but accepts
148///   maps (like a struct) receives a map with the text under the key of the
149///   content (no entry for empty text).
150///
151/// Both only happen after a type rejected the value, so they cost nothing
152/// otherwise.  Without the key (the default), values are passed on as
153/// they are.
154///
155/// ```
156/// use deser::de::{ContentKey, DeserializeDriver};
157/// use deser::{Atom, Deserialize, Event};
158///
159/// #[derive(Deserialize, Debug, PartialEq)]
160/// struct Price {
161///     #[deser(rename = "@currency")]
162///     currency: Option<String>,
163///     #[deser(rename = "$text")]
164///     amount: u32,
165/// }
166///
167/// let mut out = None::<(u32, Price)>;
168/// let mut driver = DeserializeDriver::new(&mut out);
169/// ContentKey("$text").set(driver.state_mut());
170/// driver.emit(Event::seq_start()).unwrap();
171/// // a map for a `u32`
172/// driver.emit(Event::map_start()).unwrap();
173/// for text in ["@unit", "m", "$text", "3"] {
174///     driver.emit(Atom::Lexical(text.into())).unwrap();
175/// }
176/// driver.emit(Event::MapEnd).unwrap();
177/// // text for a struct
178/// driver.emit(Atom::Lexical("12".into())).unwrap();
179/// driver.emit(Event::SeqEnd).unwrap();
180/// drop(driver);
181/// assert_eq!(out, Some((3, Price { currency: None, amount: 12 })));
182/// ```
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub struct ContentKey(pub &'static str);
185
186impl ContentKey {
187    /// Returns the key of the content of a deserialization if there is one.
188    #[inline]
189    pub fn of(state: &State) -> Option<&'static str> {
190        Some(state.content_key).filter(|key| !key.is_empty())
191    }
192
193    /// Sets the key of the content of a deserialization.
194    ///
195    /// The empty key means that there is no key of the content (the
196    /// default).
197    #[inline]
198    pub fn set(self, state: &mut State) {
199        state.content_key = self.0;
200    }
201}
202
203/// Parses the lexical form of a boolean with the rules of the state.
204pub(crate) fn parse_bool(value: &str, state: &State) -> Result<bool, Error> {
205    parse_bool_with(value, LexicalRules::of(state).lenient_bools, state)
206}
207
208/// Parses the lexical form of a boolean.
209///
210/// If `lenient` is set, the spellings of booleans in query strings,
211/// environment variables and command lines are accepted, ignoring ASCII
212/// case.
213pub(crate) fn parse_bool_with(value: &str, lenient: bool, state: &State) -> Result<bool, Error> {
214    // the common spellings first
215    match value {
216        "true" => return Ok(true),
217        "false" => return Ok(false),
218        _ => {}
219    }
220    if lenient {
221        const TRUE: [&str; 4] = ["true", "yes", "on", "1"];
222        const FALSE: [&str; 4] = ["false", "no", "off", "0"];
223        if TRUE.iter().any(|x| x.eq_ignore_ascii_case(value)) {
224            Ok(true)
225        } else if FALSE.iter().any(|x| x.eq_ignore_ascii_case(value)) {
226            Ok(false)
227        } else {
228            Err(invalid(
229                value,
230                "bool (true, yes, on, 1, false, no, off or 0)",
231                state,
232            ))
233        }
234    } else {
235        Err(invalid(value, "bool (true or false)", state))
236    }
237}
238
239/// Returns `true` if the text of a lexical atom is empty and empty text is
240/// a missing value.
241#[inline]
242pub(crate) fn is_empty_null(value: &str, state: &State) -> bool {
243    value.is_empty() && LexicalRules::of(state).empty_is_null
244}
245
246/// Converts the error of parsing an integer.
247#[cold]
248pub(crate) fn int_error(value: &str, err: ParseIntError, expecting: &str, state: &State) -> Error {
249    let kind = match err.kind() {
250        IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => ErrorKind::OutOfRange,
251        _ => ErrorKind::InvalidValue,
252    };
253    if state.discards_errors {
254        return discarded_error(kind);
255    }
256    Error::new(kind, invalid_message(value, expecting))
257}
258
259/// Creates the error for a lexical atom that cannot be parsed.
260#[cold]
261pub(crate) fn invalid(value: &str, expecting: &str, state: &State) -> Error {
262    if state.discards_errors {
263        return discarded_error(ErrorKind::InvalidValue);
264    }
265    Error::new(ErrorKind::InvalidValue, invalid_message(value, expecting))
266}
267
268fn invalid_message(value: &str, expecting: &str) -> String {
269    let mut msg = String::from("invalid value ");
270    match value.char_indices().nth(MAX_QUOTED) {
271        Some((end, _)) => write!(msg, "{:?}...", &value[..end]).unwrap(),
272        None => write!(msg, "{:?}", value).unwrap(),
273    }
274    write!(msg, ", expected {}", expecting).unwrap();
275    msg
276}
277
278/// Creates the error for a number that does not fit into the type.
279#[cold]
280pub(crate) fn out_of_range(
281    value: &dyn core::fmt::Display,
282    expecting: &str,
283    state: &State,
284) -> Error {
285    if state.discards_errors {
286        return discarded_error(ErrorKind::OutOfRange);
287    }
288    Error::new(
289        ErrorKind::OutOfRange,
290        format!("invalid value {}, expected {}", value, expecting),
291    )
292}
293
294#[test]
295fn test_parse_bool() {
296    let state = State::new();
297    for value in ["true", "TRUE", "Yes", "on", "1"] {
298        assert!(parse_bool_with(value, true, &state).unwrap(), "{}", value);
299    }
300    for value in ["false", "False", "NO", "off", "0"] {
301        assert!(!parse_bool_with(value, true, &state).unwrap(), "{}", value);
302    }
303    for value in ["", "2", "y", "n", "t", "truee", " true"] {
304        assert!(parse_bool_with(value, true, &state).is_err(), "{}", value);
305    }
306    assert!(parse_bool(" true", &state).is_err());
307    assert!(parse_bool("true", &state).unwrap());
308    assert!(!parse_bool("false", &state).unwrap());
309    for value in ["TRUE", "yes", "on", "1", "0", "off"] {
310        assert!(parse_bool(value, &state).is_err(), "{}", value);
311    }
312}
313
314#[test]
315fn test_invalid_truncates() {
316    let err = invalid(&"x".repeat(100), "u32", &State::new());
317    assert_eq!(
318        err.message(),
319        format!("invalid value {:?}..., expected u32", "x".repeat(64))
320    );
321}