pub(crate) fn is_json_ws(c: char) -> bool {
matches!(c, ' ' | '\n' | '\r' | '\t')
}
pub(crate) fn is_special_ws(c: char) -> bool {
matches!(
c,
'\u{00A0}' | '\u{180E}' | '\u{2000}'
..='\u{200B}' | '\u{202F}' | '\u{205F}' | '\u{3000}' | '\u{FEFF}'
)
}
pub(crate) fn is_ws(c: char) -> bool {
is_json_ws(c) || is_special_ws(c)
}
pub(crate) fn is_double_quote(c: char) -> bool {
matches!(c, '"' | '\u{201C}' | '\u{201D}' | '\u{201E}' | '\u{201F}')
}
pub(crate) fn is_single_quote(c: char) -> bool {
matches!(
c,
'\'' | '\u{2018}' | '\u{2019}' | '\u{201A}' | '\u{201B}' | '\u{0060}' | '\u{00B4}'
)
}
pub(crate) fn is_quote(c: char) -> bool {
is_double_quote(c) || is_single_quote(c)
}
pub(crate) fn is_delimiter(c: char) -> bool {
matches!(
c,
',' | ':' | '[' | ']' | '/' | '{' | '}' | '(' | ')' | '\n' | '+'
)
}
pub(crate) fn is_unquoted_delimiter(c: char) -> bool {
matches!(c, ',' | '[' | ']' | '/' | '{' | '}' | '\n' | '+')
}
pub(crate) fn is_func_name_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_' || c == '$'
}
pub(crate) fn is_url_char(c: char) -> bool {
c.is_ascii_alphanumeric()
|| matches!(
c,
'-' | '.'
| '_'
| '~'
| ':'
| '/'
| '?'
| '#'
| '@'
| '!'
| '$'
| '&'
| '\''
| '('
| ')'
| '*'
| '+'
| ';'
| '='
)
}
pub(crate) fn is_url_scheme(span: &str) -> bool {
matches!(
span,
"http:" | "https:" | "ftp:" | "mailto:" | "file:" | "data:" | "irc:"
)
}
pub(crate) fn hex_value(c: char) -> Option<u32> {
c.to_digit(16)
}
pub(crate) fn simple_escape(c: char) -> Option<char> {
Some(match c {
'"' | '\\' | '/' | '\'' => c,
'b' => '\u{08}',
'f' => '\u{0C}',
'n' => '\n',
'r' => '\r',
't' => '\t',
_ => return None,
})
}
pub(crate) fn decode_html_entity(fragment: &str) -> Option<(char, usize)> {
if !fragment.starts_with('&') {
return None;
}
let semi = fragment.find(';')?;
let entity = &fragment[..=semi];
let named = match entity {
""" => Some('"'),
"&" => Some('&'),
"<" => Some('<'),
">" => Some('>'),
"'" => Some('\''),
_ => None,
};
if let Some(c) = named {
return Some((c, entity.len()));
}
let body = fragment.get(1..semi)?;
let body = body.strip_prefix('#')?;
let (radix, digits) = match body.strip_prefix(['x', 'X']) {
Some(hex) => (16, hex),
None => (10, body),
};
if digits.is_empty() {
return None;
}
let code = u32::from_str_radix(digits, radix).ok()?;
let c = char::from_u32(code)?;
Some((c, entity.len()))
}