use crate::parser::ParseError;
use crate::parser::SubtagIterator;
use crate::shortvec::{ShortBoxSlice, ShortBoxSliceIntoIter};
use crate::subtags::{subtag, Subtag};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::str::FromStr;
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord, Default)]
pub struct Value(ShortBoxSlice<Subtag>);
const TRUE_VALUE: Subtag = subtag!("true");
impl Value {
#[inline]
pub fn try_from_str(s: &str) -> Result<Self, ParseError> {
Self::try_from_utf8(s.as_bytes())
}
pub fn try_from_utf8(code_units: &[u8]) -> Result<Self, ParseError> {
let mut v = ShortBoxSlice::new();
if !code_units.is_empty() {
for chunk in SubtagIterator::new(code_units) {
let subtag = Subtag::try_from_utf8(chunk)?;
if subtag != TRUE_VALUE {
#[cfg(feature = "alloc")]
v.push(subtag);
#[cfg(not(feature = "alloc"))]
if v.is_empty() {
v = ShortBoxSlice::new_single(subtag);
} else if let &[prev] = &*v {
v = ShortBoxSlice::new_double(prev, subtag);
} else {
return Err(ParseError::InvalidSubtag);
}
}
}
}
Ok(Self(v))
}
pub const fn as_single_subtag(&self) -> Option<&Subtag> {
self.0.single()
}
pub fn into_single_subtag(self) -> Option<Subtag> {
self.0.into_single()
}
#[doc(hidden)]
pub fn as_subtags_slice(&self) -> &[Subtag] {
&self.0
}
#[cfg(feature = "alloc")]
pub fn push_subtag(&mut self, subtag: Subtag) {
if subtag != TRUE_VALUE {
self.0.push(subtag);
}
}
pub fn subtag_count(&self) -> usize {
self.0.len()
}
pub const fn new_empty() -> Self {
Self(ShortBoxSlice::new())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn remove_subtag(&mut self, idx: usize) -> Option<Subtag> {
if self.0.len() < idx {
None
} else {
let item = self.0.remove(idx);
Some(item)
}
}
pub fn get_subtag(&self, idx: usize) -> Option<&Subtag> {
self.0.get(idx)
}
#[doc(hidden)]
pub const fn from_subtag(subtag: Option<Subtag>) -> Self {
match subtag {
None | Some(TRUE_VALUE) => Self(ShortBoxSlice::new()),
Some(val) => Self(ShortBoxSlice::new_single(val)),
}
}
#[doc(hidden)]
pub fn from_two_subtags(f: Subtag, s: Subtag) -> Self {
Self(ShortBoxSlice::new_double(f, s))
}
#[cfg(feature = "alloc")]
pub fn from_vec_unchecked(input: Vec<Subtag>) -> Self {
Self(input.into())
}
#[allow(dead_code)]
pub(crate) fn from_short_slice_unchecked(input: ShortBoxSlice<Subtag>) -> Self {
Self(input)
}
pub(crate) const fn parse_subtag_from_utf8(t: &[u8]) -> Result<Option<Subtag>, ParseError> {
match Subtag::try_from_utf8(t) {
Ok(TRUE_VALUE) => Ok(None),
Ok(s) => Ok(Some(s)),
Err(_) => Err(ParseError::InvalidSubtag),
}
}
pub(crate) fn for_each_subtag_str<E, F>(&self, f: &mut F) -> Result<(), E>
where
F: FnMut(&str) -> Result<(), E>,
{
self.0.iter().map(Subtag::as_str).try_for_each(f)
}
}
impl IntoIterator for Value {
type Item = Subtag;
type IntoIter = ShortBoxSliceIntoIter<Subtag>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[cfg(feature = "alloc")]
impl FromIterator<Subtag> for Value {
fn from_iter<T: IntoIterator<Item = Subtag>>(iter: T) -> Self {
Self(ShortBoxSlice::from_iter(iter))
}
}
#[cfg(feature = "alloc")]
impl Extend<Subtag> for Value {
fn extend<T: IntoIterator<Item = Subtag>>(&mut self, iter: T) {
for i in iter {
self.0.push(i);
}
}
}
#[cfg(feature = "alloc")]
impl FromStr for Value {
type Err = ParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
impl PartialEq<&str> for Value {
fn eq(&self, other: &&str) -> bool {
writeable::cmp_utf8(self, other.as_bytes()).is_eq()
}
}
impl_writeable_for_subtag_list!(Value, "islamic", "civil");
#[macro_export]
#[doc(hidden)] macro_rules! extensions_unicode_value {
($value:literal) => {
const {
$crate::extensions::unicode::Value::from_subtag(
match $crate::subtags::Subtag::try_from_utf8($value.as_bytes()) {
Ok(r) => Some(r),
_ => panic!(concat!("Invalid Unicode extension value: ", $value)),
},
)
}
};
}
#[doc(inline)]
pub use extensions_unicode_value as value;