use core::{
borrow::Borrow,
cmp::Ordering,
fmt::{self, Debug, Display, Formatter},
hash::{Hash, Hasher},
};
use crate::{
InvalidPctString,
util::{TryEncodedBytes, to_digit},
};
#[cfg(feature = "std")]
use crate::PctString;
pub struct PctStr([u8]);
impl PctStr {
pub fn new<S: AsRef<[u8]> + ?Sized>(input: &S) -> Result<&PctStr, InvalidPctString<&S>> {
let input_bytes = input.as_ref();
if Self::validate(input_bytes.iter().copied()) {
Ok(unsafe { Self::new_unchecked(input_bytes) })
} else {
Err(InvalidPctString(input))
}
}
pub unsafe fn new_unchecked<S: AsRef<[u8]> + ?Sized>(input: &S) -> &PctStr {
unsafe { core::mem::transmute::<&[u8], &PctStr>(input.as_ref()) }
}
pub fn validate(input: impl Iterator<Item = u8>) -> bool {
let chars = TryEncodedBytes::new(input);
utf8_decode::TryDecoder::new(chars).all(|r| r.is_ok())
}
#[inline]
pub fn len(&self) -> usize {
self.chars().count()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(&self.0)
}
}
#[inline]
pub fn chars(&self) -> Chars<'_> {
Chars::new(self.bytes())
}
#[inline]
pub fn bytes(&self) -> Bytes<'_> {
Bytes(self.0.iter())
}
#[cfg(feature = "std")]
pub fn decode(&self) -> String {
let mut decoded = String::with_capacity(self.len());
for c in self.chars() {
decoded.push(c)
}
decoded
}
}
impl PartialEq for PctStr {
#[inline]
fn eq(&self, other: &PctStr) -> bool {
let mut a = self.chars();
let mut b = other.chars();
loop {
match (a.next(), b.next()) {
(Some(a), Some(b)) if a != b => return false,
(Some(_), None) => return false,
(None, Some(_)) => return false,
(None, None) => break,
_ => (),
}
}
true
}
}
impl Eq for PctStr {}
impl PartialEq<str> for PctStr {
#[inline]
fn eq(&self, other: &str) -> bool {
let mut a = self.chars();
let mut b = other.chars();
loop {
match (a.next(), b.next()) {
(Some(a), Some(b)) if a != b => return false,
(Some(_), None) => return false,
(None, Some(_)) => return false,
(None, None) => break,
_ => (),
}
}
true
}
}
#[cfg(feature = "std")]
impl PartialEq<PctString> for PctStr {
#[inline]
fn eq(&self, other: &PctString) -> bool {
let mut a = self.chars();
let mut b = other.chars();
loop {
match (a.next(), b.next()) {
(Some(a), Some(b)) if a != b => return false,
(Some(_), None) => return false,
(None, Some(_)) => return false,
(None, None) => break,
_ => (),
}
}
true
}
}
impl PartialOrd for PctStr {
fn partial_cmp(&self, other: &PctStr) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for PctStr {
fn cmp(&self, other: &PctStr) -> Ordering {
let mut self_chars = self.chars();
let mut other_chars = other.chars();
loop {
match (self_chars.next(), other_chars.next()) {
(None, None) => return Ordering::Equal,
(None, Some(_)) => return Ordering::Less,
(Some(_), None) => return Ordering::Greater,
(Some(a), Some(b)) => match a.cmp(&b) {
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
Ordering::Equal => (),
},
}
}
}
}
#[cfg(feature = "std")]
impl PartialOrd<PctString> for PctStr {
fn partial_cmp(&self, other: &PctString) -> Option<Ordering> {
self.partial_cmp(other.as_pct_str())
}
}
impl Hash for PctStr {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
for c in self.chars() {
c.hash(hasher)
}
}
}
impl Display for PctStr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl Debug for PctStr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
#[cfg(feature = "std")]
impl ToOwned for PctStr {
type Owned = PctString;
fn to_owned(&self) -> Self::Owned {
unsafe { PctString::new_unchecked(self.0.to_owned()) }
}
}
impl Borrow<str> for PctStr {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for PctStr {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for PctStr {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
pub struct Bytes<'a>(core::slice::Iter<'a, u8>);
impl<'a> Iterator for Bytes<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
if let Some(next) = self.0.next().copied() {
match next {
b'%' => {
let a = self.0.next().copied().unwrap();
let a = to_digit(a).unwrap();
let b = self.0.next().copied().unwrap();
let b = to_digit(b).unwrap();
let byte = a << 4 | b;
Some(byte)
}
_ => Some(next),
}
} else {
None
}
}
}
impl<'a> core::iter::FusedIterator for Bytes<'a> {}
pub struct Chars<'a> {
inner: utf8_decode::Decoder<Bytes<'a>>,
}
impl<'a> Chars<'a> {
fn new(bytes: Bytes<'a>) -> Self {
Self {
inner: utf8_decode::Decoder::new(bytes),
}
}
}
impl<'a> Iterator for Chars<'a> {
type Item = char;
fn next(&mut self) -> Option<char> {
self.inner.next().map(|x| x.unwrap())
}
}
impl<'a> core::iter::FusedIterator for Chars<'a> {}