use alloc::format;
use alloc::string::String;
use core::fmt::Write;
use core::num::{IntErrorKind, ParseIntError};
use crate::State;
use crate::error::{Error, ErrorKind, discarded_error};
const MAX_QUOTED: usize = 64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LexicalRules {
lenient_bools: bool,
empty_is_null: bool,
}
impl LexicalRules {
pub const STRICT: LexicalRules = LexicalRules {
lenient_bools: false,
empty_is_null: false,
};
pub const LENIENT: LexicalRules = LexicalRules {
lenient_bools: true,
empty_is_null: true,
};
#[inline]
pub fn of(state: &State) -> LexicalRules {
state.get::<LexicalRules>().copied().unwrap_or_default()
}
#[inline]
pub fn set(self, state: &mut State) {
*state.get_mut::<LexicalRules>() = self;
}
#[inline]
pub fn set_default(self, state: &mut State) {
state.set_default(self);
}
pub const fn set_lenient_bools(&mut self, yes: bool) {
self.lenient_bools = yes;
}
pub const fn lenient_bools(&self) -> bool {
self.lenient_bools
}
pub const fn set_empty_is_null(&mut self, yes: bool) {
self.empty_is_null = yes;
}
pub const fn empty_is_null(&self) -> bool {
self.empty_is_null
}
}
impl Default for LexicalRules {
fn default() -> LexicalRules {
LexicalRules::STRICT
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ContentKey(pub &'static str);
impl ContentKey {
#[inline]
pub fn of(state: &State) -> Option<&'static str> {
Some(state.content_key).filter(|key| !key.is_empty())
}
#[inline]
pub fn set(self, state: &mut State) {
state.content_key = self.0;
}
}
pub(crate) fn parse_bool(value: &str, state: &State) -> Result<bool, Error> {
parse_bool_with(value, LexicalRules::of(state).lenient_bools, state)
}
pub(crate) fn parse_bool_with(value: &str, lenient: bool, state: &State) -> Result<bool, Error> {
match value {
"true" => return Ok(true),
"false" => return Ok(false),
_ => {}
}
if lenient {
const TRUE: [&str; 4] = ["true", "yes", "on", "1"];
const FALSE: [&str; 4] = ["false", "no", "off", "0"];
if TRUE.iter().any(|x| x.eq_ignore_ascii_case(value)) {
Ok(true)
} else if FALSE.iter().any(|x| x.eq_ignore_ascii_case(value)) {
Ok(false)
} else {
Err(invalid(
value,
"bool (true, yes, on, 1, false, no, off or 0)",
state,
))
}
} else {
Err(invalid(value, "bool (true or false)", state))
}
}
#[inline]
pub(crate) fn is_empty_null(value: &str, state: &State) -> bool {
value.is_empty() && LexicalRules::of(state).empty_is_null
}
#[cold]
pub(crate) fn int_error(value: &str, err: ParseIntError, expecting: &str, state: &State) -> Error {
let kind = match err.kind() {
IntErrorKind::PosOverflow | IntErrorKind::NegOverflow => ErrorKind::OutOfRange,
_ => ErrorKind::InvalidValue,
};
if state.discards_errors {
return discarded_error(kind);
}
Error::new(kind, invalid_message(value, expecting))
}
#[cold]
pub(crate) fn invalid(value: &str, expecting: &str, state: &State) -> Error {
if state.discards_errors {
return discarded_error(ErrorKind::InvalidValue);
}
Error::new(ErrorKind::InvalidValue, invalid_message(value, expecting))
}
fn invalid_message(value: &str, expecting: &str) -> String {
let mut msg = String::from("invalid value ");
match value.char_indices().nth(MAX_QUOTED) {
Some((end, _)) => write!(msg, "{:?}...", &value[..end]).unwrap(),
None => write!(msg, "{:?}", value).unwrap(),
}
write!(msg, ", expected {}", expecting).unwrap();
msg
}
#[cold]
pub(crate) fn out_of_range(
value: &dyn core::fmt::Display,
expecting: &str,
state: &State,
) -> Error {
if state.discards_errors {
return discarded_error(ErrorKind::OutOfRange);
}
Error::new(
ErrorKind::OutOfRange,
format!("invalid value {}, expected {}", value, expecting),
)
}
#[test]
fn test_parse_bool() {
let state = State::new();
for value in ["true", "TRUE", "Yes", "on", "1"] {
assert!(parse_bool_with(value, true, &state).unwrap(), "{}", value);
}
for value in ["false", "False", "NO", "off", "0"] {
assert!(!parse_bool_with(value, true, &state).unwrap(), "{}", value);
}
for value in ["", "2", "y", "n", "t", "truee", " true"] {
assert!(parse_bool_with(value, true, &state).is_err(), "{}", value);
}
assert!(parse_bool(" true", &state).is_err());
assert!(parse_bool("true", &state).unwrap());
assert!(!parse_bool("false", &state).unwrap());
for value in ["TRUE", "yes", "on", "1", "0", "off"] {
assert!(parse_bool(value, &state).is_err(), "{}", value);
}
}
#[test]
fn test_invalid_truncates() {
let err = invalid(&"x".repeat(100), "u32", &State::new());
assert_eq!(
err.message(),
format!("invalid value {:?}..., expected u32", "x".repeat(64))
);
}