#[derive(Debug, Clone, Copy)]
#[allow(dead_code)] pub struct NamespacePrefix {
prefix: &'static [u8],
prefix_len: usize,
}
impl NamespacePrefix {
#[must_use]
#[allow(dead_code)] pub const fn new(prefix: &'static str) -> Self {
let prefix_bytes = prefix.as_bytes();
Self {
prefix: prefix_bytes,
prefix_len: prefix_bytes.len(),
}
}
#[inline]
#[must_use]
#[allow(dead_code)] pub fn matches<'a>(&self, tag_name: &'a [u8]) -> Option<&'a str> {
if tag_name.starts_with(self.prefix) {
std::str::from_utf8(&tag_name[self.prefix_len..]).ok()
} else {
None
}
}
#[inline]
#[must_use]
#[allow(dead_code)] pub const fn prefix(&self) -> &'static str {
#[allow(unsafe_code)]
unsafe {
std::str::from_utf8_unchecked(self.prefix)
}
}
}
pub mod namespaces {
use super::NamespacePrefix;
#[allow(dead_code)] pub const DC: NamespacePrefix = NamespacePrefix::new("dc:");
#[allow(dead_code)] pub const CONTENT: NamespacePrefix = NamespacePrefix::new("content:");
#[allow(dead_code)] pub const MEDIA: NamespacePrefix = NamespacePrefix::new("media:");
#[allow(dead_code)] pub const ITUNES: NamespacePrefix = NamespacePrefix::new("itunes:");
#[allow(dead_code)] pub const PODCAST: NamespacePrefix = NamespacePrefix::new("podcast:");
#[allow(dead_code)] pub const THR: NamespacePrefix = NamespacePrefix::new("thr:");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_namespace_prefix_matches() {
assert_eq!(namespaces::DC.matches(b"dc:creator"), Some("creator"));
assert_eq!(namespaces::DC.matches(b"dc:publisher"), Some("publisher"));
assert_eq!(
namespaces::CONTENT.matches(b"content:encoded"),
Some("encoded")
);
assert_eq!(
namespaces::MEDIA.matches(b"media:thumbnail"),
Some("thumbnail")
);
}
#[test]
fn test_namespace_prefix_no_match() {
assert_eq!(namespaces::DC.matches(b"content:encoded"), None);
assert_eq!(namespaces::CONTENT.matches(b"dc:creator"), None);
assert_eq!(namespaces::MEDIA.matches(b"itunes:author"), None);
}
#[test]
fn test_namespace_prefix_empty_element() {
assert_eq!(namespaces::DC.matches(b"dc:"), Some(""));
}
#[test]
fn test_namespace_prefix_invalid_utf8() {
let invalid = b"dc:\xFF\xFE";
assert_eq!(namespaces::DC.matches(invalid), None);
}
#[test]
fn test_namespace_prefix_getter() {
assert_eq!(namespaces::DC.prefix(), "dc:");
assert_eq!(namespaces::CONTENT.prefix(), "content:");
assert_eq!(namespaces::MEDIA.prefix(), "media:");
assert_eq!(namespaces::ITUNES.prefix(), "itunes:");
assert_eq!(namespaces::PODCAST.prefix(), "podcast:");
}
#[test]
fn test_custom_namespace() {
const CUSTOM: NamespacePrefix = NamespacePrefix::new("custom:");
assert_eq!(CUSTOM.matches(b"custom:field"), Some("field"));
assert_eq!(CUSTOM.matches(b"other:field"), None);
}
}