use core::cmp::Ordering;
use core::marker::PhantomData;
use crate::parser::char::{is_ascii_unreserved, is_unreserved, is_utf8_byte_continue};
use crate::spec::Spec;
use crate::parser::trusted::take_xdigits2;
#[derive(Debug, Clone)]
pub(crate) enum PctNormalizedFragment<'a> {
None,
Char(char),
PercentEncodingTriplets(&'a str),
}
impl<'a> PctNormalizedFragment<'a> {
#[inline]
#[must_use]
fn pct_enc_triplets(s: &'a str) -> Self {
Self::PercentEncodingTriplets(s)
}
}
impl<'a> Iterator for PctNormalizedFragment<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::None => None,
Self::Char(c) => {
let c = *c;
*self = Self::None;
Some(c)
}
Self::PercentEncodingTriplets(s) => {
if let Some((c, rest)) = take_first_char(s) {
*s = rest;
Some(c.to_ascii_uppercase())
} else {
*self = Self::None;
None
}
}
}
}
}
impl core::iter::FusedIterator for PctNormalizedFragment<'_> {}
#[derive(Debug, Clone)]
pub(crate) struct PctNormalizedFragments<'a, S> {
rest: &'a str,
_spec: PhantomData<fn() -> S>,
}
impl<'a, S: Spec> PctNormalizedFragments<'a, S> {
#[inline]
#[must_use]
pub(crate) fn new(s: &'a str) -> Self {
Self {
rest: s,
_spec: PhantomData,
}
}
}
impl<'a, S: Spec> Iterator for PctNormalizedFragments<'a, S> {
type Item = PctNormalizedFragment<'a>;
fn next(&mut self) -> Option<Self::Item> {
let (first_char, rest) = take_first_char(self.rest)?;
if first_char != '%' {
self.rest = rest;
return Some(PctNormalizedFragment::Char(first_char));
}
debug_assert_eq!(first_char, '%');
let (first_decoded_byte, rest) = take_xdigits2(rest);
if first_decoded_byte.is_ascii() {
let triplet = &self.rest[..3];
self.rest = rest;
if is_ascii_unreserved(first_decoded_byte) {
return Some(PctNormalizedFragment::Char(char::from(first_decoded_byte)));
} else {
return Some(PctNormalizedFragment::pct_enc_triplets(triplet));
}
}
let expected_char_len = match (first_decoded_byte & 0xf0).cmp(&0b1110_0000) {
Ordering::Less => 2,
Ordering::Equal => 3,
Ordering::Greater => 4,
};
let c_buf = &mut [first_decoded_byte, 0, 0, 0][..expected_char_len];
let mut suffix_of_the_decoded = rest;
for buf_dest in &mut c_buf[1..] {
let (byte, after_triplet) = match take_first_char(suffix_of_the_decoded) {
Some(('%', after_percent)) => take_xdigits2(after_percent),
_ => {
let skip_triplets_len = self.rest.len() - suffix_of_the_decoded.len();
let triplets = &self.rest[..skip_triplets_len];
self.rest = suffix_of_the_decoded;
return Some(PctNormalizedFragment::pct_enc_triplets(triplets));
}
};
if !is_utf8_byte_continue(byte) {
let skip_triplets_len = self.rest.len() - suffix_of_the_decoded.len();
let triplets = &self.rest[..skip_triplets_len];
self.rest = suffix_of_the_decoded;
return Some(PctNormalizedFragment::pct_enc_triplets(triplets));
}
*buf_dest = byte;
suffix_of_the_decoded = after_triplet;
}
let triplets = {
let skip_triplets_len = self.rest.len() - suffix_of_the_decoded.len();
&self.rest[..skip_triplets_len]
};
self.rest = suffix_of_the_decoded;
let decoded_s = match core::str::from_utf8(c_buf) {
Ok(s) => s,
Err(_) => {
return Some(PctNormalizedFragment::pct_enc_triplets(triplets));
}
};
let decoded_c = decoded_s
.chars()
.next()
.expect("[consistency] the buffer is non-empty");
if is_unreserved::<S>(decoded_c) {
Some(PctNormalizedFragment::Char(decoded_c))
} else {
Some(PctNormalizedFragment::pct_enc_triplets(triplets))
}
}
}
impl<S: Spec> core::iter::FusedIterator for PctNormalizedFragments<'_, S> {}
#[must_use]
fn take_first_char(s: &str) -> Option<(char, &str)> {
let mut chars = s.chars();
let c = chars.next()?;
let rest = chars.as_str();
Some((c, rest))
}