use super::{Head, Syntax};
use structured_asn1::memory::{MemoryError, Work};
struct Pattern<'a> {
media_type: &'a [u8],
subtype: &'a [u8],
matches: bool,
}
impl Pattern<'_> {
fn compare(remaining: &mut &[u8], matches: &mut bool, byte: u8) {
if !*matches {
return;
}
if remaining.is_empty() {
*matches = false;
return;
}
if !remaining[0].eq_ignore_ascii_case(&byte) {
*matches = false;
}
*remaining = &remaining[1..];
}
}
impl<const N: usize> Head for [Pattern<'_>; N] {
fn cost(&self) -> Result<u64, MemoryError> {
u64::try_from(N)
.ok()
.and_then(|count| count.checked_mul(2))
.ok_or(MemoryError::WorkExceeded)
}
fn type_byte(&mut self, byte: u8) {
for pattern in self {
Pattern::compare(&mut pattern.media_type, &mut pattern.matches, byte);
}
}
fn subtype_byte(&mut self, byte: u8) {
for pattern in self {
Pattern::compare(&mut pattern.subtype, &mut pattern.matches, byte);
}
}
}
pub struct Essences<'a, const N: usize, const XML_NORMALIZED: bool = false> {
syntax: Syntax<XML_NORMALIZED>,
patterns: [Pattern<'a>; N],
}
impl<'a, const N: usize, const XML_NORMALIZED: bool> Essences<'a, N, XML_NORMALIZED> {
#[must_use]
pub fn new(patterns: [(&'a str, &'a str); N]) -> Self {
Self {
syntax: Syntax::new(),
patterns: patterns.map(|(media_type, subtype)| Pattern {
media_type: media_type.as_bytes(),
subtype: subtype.as_bytes(),
matches: true,
}),
}
}
pub fn feed<W: Work>(
&mut self,
bytes: impl AsRef<[u8]>,
work: &W,
) -> Result<bool, MemoryError> {
self.syntax.scan(bytes.as_ref(), work, &mut self.patterns)
}
#[must_use]
pub fn complete(&self) -> bool {
self.syntax.complete()
}
#[must_use]
pub fn matches(&self, index: usize) -> Option<bool> {
self.patterns.get(index).map(|pattern| {
self.complete()
&& pattern.matches
&& pattern.media_type.is_empty()
&& pattern.subtype.is_empty()
})
}
}