use encoding_rs::Encoding;
use std::str::FromStr;
use super::error::ParseError;
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub enum ParseMode {
#[default]
Strict,
Permissive,
}
impl ParseMode {
pub const fn as_str(self) -> &'static str {
match self {
Self::Strict => "strict",
Self::Permissive => "permissive",
}
}
pub(crate) const fn is_permissive(self) -> bool {
matches!(self, Self::Permissive)
}
}
impl FromStr for ParseMode {
type Err = ();
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"strict" => Ok(Self::Strict),
"permissive" => Ok(Self::Permissive),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub enum StringEncoding {
#[default]
Utf8,
EncodingRs(&'static Encoding),
}
impl PartialEq for StringEncoding {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Utf8, Self::Utf8) => true,
(Self::EncodingRs(a), Self::EncodingRs(b)) => std::ptr::eq(*a, *b),
_ => false,
}
}
}
impl Eq for StringEncoding {}
impl StringEncoding {
pub fn as_str(self) -> &'static str {
match self {
Self::Utf8 => "utf-8",
Self::EncodingRs(enc) => enc.name(),
}
}
pub(crate) fn decode(
self,
offset: usize,
bytes: &[u8],
mode: StringDecodeMode,
) -> Result<String, ParseError> {
match self {
Self::Utf8 => match mode {
StringDecodeMode::Strict => std::str::from_utf8(bytes)
.map(str::to_owned)
.map_err(|_| ParseError::StringDecode {
offset,
encoding: self.as_str(),
}),
StringDecodeMode::Lossy => Ok(String::from_utf8_lossy(bytes).into_owned()),
},
Self::EncodingRs(enc) => {
let (value, _, had_errors) = enc.decode(bytes);
if had_errors && matches!(mode, StringDecodeMode::Strict) {
return Err(ParseError::StringDecode {
offset,
encoding: self.as_str(),
});
}
Ok(value.into_owned())
}
}
}
}
impl FromStr for StringEncoding {
type Err = ();
fn from_str(value: &str) -> Result<Self, Self::Err> {
let enc = Encoding::for_label(value.as_bytes()).ok_or(())?;
if std::ptr::eq(enc, encoding_rs::UTF_8) {
Ok(Self::Utf8)
} else {
Ok(Self::EncodingRs(enc))
}
}
}
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub enum StringDecodeMode {
#[default]
Strict,
Lossy,
}
impl StringDecodeMode {
pub const fn as_str(self) -> &'static str {
match self {
Self::Strict => "strict",
Self::Lossy => "lossy",
}
}
}
impl FromStr for StringDecodeMode {
type Err = ();
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"strict" => Ok(Self::Strict),
"lossy" => Ok(Self::Lossy),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub struct ParseOptions {
pub mode: ParseMode,
pub string_encoding: StringEncoding,
pub string_decode_mode: StringDecodeMode,
}