use std::array;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display};
use std::hash::{Hash, Hasher};
use std::ops::Range;
use std::str::{from_utf8_unchecked, FromStr};
mod builder;
mod encoding;
mod error;
mod path;
pub use builder::Builder;
use encoding::decode;
pub use error::{Error, Result};
use path::validate;
#[derive(Clone)]
pub struct Format<const N: usize> {
value: Box<[u8]>,
spans: [Range<u16>; N],
flags: u64,
}
impl<const N: usize> Format<N> {
#[must_use]
pub fn get(&self, index: usize) -> Cow<'_, str> {
let p = self.spans[index].start as usize;
let q = self.spans[index].end as usize;
if self.flags & (1 << index) == 0 {
unsafe { Cow::Borrowed(from_utf8_unchecked(&self.value[p..q])) }
} else {
decode(&self.value[p..q])
}
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
unsafe { from_utf8_unchecked(&self.value) }
}
}
impl<const N: usize> FromStr for Format<N> {
type Err = Error;
fn from_str(value: &str) -> Result<Self> {
let mut spans = array::from_fn(|_| 0u16..0u16);
let mut flags = 0;
let mut start = 0u16;
let mut index = 0;
let mut shift = 1;
for (i, char) in value.char_indices() {
match char {
':' => {
let end = u16::try_from(i).map_err(|_| Error::Overflow)?;
validate(&value[start.into()..end.into()])?;
spans[index] = start..end;
index += 1;
start = end + 1;
shift = 1 << index;
}
'%' if flags & shift == 0 => {
let bytes = value.as_bytes();
if let Some(&[b1, b2]) = bytes.get(i + 1..i + 3) {
if b1.is_ascii_hexdigit() && b2.is_ascii_hexdigit() {
flags |= shift;
}
}
}
_ => {}
}
}
let end = u16::try_from(value.len()).map_err(|_| Error::Overflow)?;
spans[index] = start..end;
if index == N - 1 {
Ok(Format {
value: value.as_bytes().into(),
spans,
flags,
})
} else {
Err(Error::Mismatch)
}
}
}
impl<const N: usize> Hash for Format<N> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<const N: usize> PartialEq for Format<N> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<const N: usize> Eq for Format<N> {}
impl<const N: usize> PartialOrd for Format<N> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> Ord for Format<N> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.value.cmp(&other.value)
}
}
impl<const N: usize> Display for Format<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl<const N: usize> Debug for Format<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Format")
.field("value", &self.as_str())
.field("spans", &self.spans)
.field("flags", &self.flags)
.finish()
}
}