use std::fmt;
use std::str::FromStr;
use crate::xml::namespace::NS_NO_PREFIX;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Name<'a> {
pub local_name: &'a str,
pub namespace: Option<&'a str>,
pub prefix: Option<&'a str>,
}
impl<'a> From<&'a str> for Name<'a> {
fn from(s: &'a str) -> Name<'a> {
let mut parts = s.splitn(2, ':').fuse();
match (parts.next(), parts.next()) {
(Some(name), None) => Name::local(name),
(Some(prefix), Some(name)) => Name::prefixed(name, prefix),
_ => unreachable!(),
}
}
}
impl<'a> From<(&'a str, &'a str)> for Name<'a> {
fn from((prefix, name): (&'a str, &'a str)) -> Name<'a> {
Name::prefixed(name, prefix)
}
}
impl<'a> fmt::Display for Name<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(namespace) = self.namespace {
write!(f, "{{{}}}", namespace)?;
}
if let Some(prefix) = self.prefix {
write!(f, "{}:", prefix)?;
}
write!(f, "{}", self.local_name)
}
}
impl<'a> Name<'a> {
pub fn to_owned(&self) -> OwnedName {
OwnedName {
local_name: self.local_name.into(),
namespace: self.namespace.map(|s| s.into()),
prefix: self.prefix.map(|s| s.into()),
}
}
#[inline]
pub fn local(local_name: &str) -> Name<'_> {
Name {
local_name,
prefix: None,
namespace: None,
}
}
#[inline]
pub fn prefixed(local_name: &'a str, prefix: &'a str) -> Name<'a> {
Name {
local_name,
namespace: None,
prefix: Some(prefix),
}
}
#[inline]
#[cfg(test)]
pub fn qualified(local_name: &'a str, namespace: &'a str, prefix: Option<&'a str>) -> Name<'a> {
Name {
local_name,
namespace: Some(namespace),
prefix,
}
}
#[inline]
pub fn repr_display(&self) -> ReprDisplay<'_, '_> {
ReprDisplay(self)
}
#[inline]
pub fn prefix_repr(&self) -> &str {
self.prefix.unwrap_or(NS_NO_PREFIX)
}
}
pub struct ReprDisplay<'a, 'b>(&'a Name<'b>);
impl<'a, 'b: 'a> fmt::Display for ReprDisplay<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0.prefix {
Some(prefix) => write!(f, "{}:{}", prefix, self.0.local_name),
None => write!(f, "{}", self.0.local_name),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct OwnedName {
pub local_name: String,
pub namespace: Option<String>,
pub prefix: Option<String>,
}
impl fmt::Display for OwnedName {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.borrow(), f)
}
}
impl OwnedName {
pub fn borrow(&self) -> Name<'_> {
Name {
local_name: &*self.local_name,
namespace: self.namespace.as_deref(),
prefix: self.prefix.as_deref(),
}
}
#[inline]
#[cfg(test)]
pub fn local<S>(local_name: S) -> OwnedName
where
S: Into<String>,
{
OwnedName {
local_name: local_name.into(),
namespace: None,
prefix: None,
}
}
#[inline]
pub fn prefix_ref(&self) -> Option<&str> {
self.prefix.as_deref()
}
}
impl<'a> From<Name<'a>> for OwnedName {
#[inline]
fn from(n: Name<'a>) -> OwnedName {
n.to_owned()
}
}
impl FromStr for OwnedName {
type Err = ();
fn from_str(s: &str) -> Result<OwnedName, ()> {
let mut it = s.split(':');
let r = match (it.next(), it.next(), it.next()) {
(Some(prefix), Some(local_name), None)
if !prefix.is_empty() && !local_name.is_empty() =>
{
Some((local_name.into(), Some(prefix.into())))
}
(Some(local_name), None, None) if !local_name.is_empty() => {
Some((local_name.into(), None))
}
(_, _, _) => None,
};
r.map(|(local_name, prefix)| OwnedName {
local_name,
namespace: None,
prefix,
})
.ok_or(())
}
}
#[cfg(test)]
mod tests {
use super::{Name, OwnedName};
#[test]
fn test_name_from() {
let n1: Name = "p:some-name".into();
let n2: Name = ("p", "some-name").into();
assert_eq!(n1, n2);
assert_eq!(n1.local_name, "some-name");
assert_eq!(n1.prefix, Some("p"));
assert!(n1.namespace.is_none());
}
#[test]
fn test_owned_name_from_str() {
assert_eq!(
"prefix:name".parse(),
Ok(OwnedName {
local_name: "name".into(),
namespace: None,
prefix: Some("prefix".into())
})
);
assert_eq!(
"name".parse(),
Ok(OwnedName {
local_name: "name".into(),
namespace: None,
prefix: None
})
);
assert_eq!("".parse(), Err::<OwnedName, ()>(()));
assert_eq!(":".parse(), Err::<OwnedName, ()>(()));
assert_eq!(":a".parse(), Err::<OwnedName, ()>(()));
assert_eq!("a:".parse(), Err::<OwnedName, ()>(()));
assert_eq!("a:b:c".parse(), Err::<OwnedName, ()>(()));
}
}