use crate::item::Node;
use crate::namespace::NamespaceMap;
use crate::parser::xml::qname::eqname;
use crate::parser::ParserState;
use crate::trees::nullo::Nullo;
use crate::value::Value;
use crate::xdmerror::{Error, ErrorKind};
use core::hash::{Hash, Hasher};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::rc::Rc;
#[derive(Clone)]
pub struct QualifiedName {
nsuri: Option<Rc<Value>>,
prefix: Option<Rc<Value>>,
localname: Rc<Value>,
}
impl QualifiedName {
pub fn new(
nsuri: Option<String>,
prefix: Option<String>,
localname: impl Into<String>,
) -> QualifiedName {
QualifiedName {
nsuri: nsuri.map(|s| Rc::new(Value::from(s))),
prefix: prefix.map(|s| Rc::new(Value::from(s))),
localname: Rc::new(Value::from(localname.into())),
}
}
pub fn new_from_values(
nsuri: Option<Rc<Value>>,
prefix: Option<Rc<Value>>,
localname: Rc<Value>,
) -> QualifiedName {
QualifiedName {
nsuri,
prefix,
localname,
}
}
pub fn as_ref(&self) -> &Self {
self
}
pub fn namespace_uri(&self) -> Option<Rc<Value>> {
self.nsuri.clone()
}
pub fn namespace_uri_to_string(&self) -> Option<String> {
self.nsuri.as_ref().map(|x| x.to_string())
}
pub fn prefix(&self) -> Option<Rc<Value>> {
self.prefix.clone()
}
pub fn prefix_to_string(&self) -> Option<String> {
self.prefix.as_ref().map(|x| x.to_string())
}
pub fn localname(&self) -> Rc<Value> {
self.localname.clone()
}
pub fn localname_to_string(&self) -> String {
self.localname.to_string()
}
pub fn resolve<F>(&mut self, mapper: F) -> Result<(), Error>
where
F: Fn(Option<Rc<Value>>) -> Result<Rc<Value>, Error>,
{
match (&self.prefix, &self.nsuri) {
(Some(p), None) => {
self.nsuri = Some(mapper(Some(p.clone()))?.clone());
Ok(())
}
_ => Ok(()),
}
}
}
impl fmt::Display for QualifiedName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut result = String::new();
let _ = self.prefix.as_ref().map_or((), |p| {
result.push_str(p.to_string().as_str());
result.push(':');
});
result.push_str(self.localname.to_string().as_str());
f.write_str(result.as_str())
}
}
impl Debug for QualifiedName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let _ = f.write_str("namespace ");
let _ = f.write_str(
self.nsuri
.as_ref()
.map_or("--none--".to_string(), |ns| ns.to_string())
.as_str(),
);
let _ = f.write_str(" prefix ");
let _ = f.write_str(
self.prefix
.as_ref()
.map_or("--none--".to_string(), |p| p.to_string())
.as_str(),
);
let _ = f.write_str(" local part \"");
let _ = f.write_str(self.localname.to_string().as_str());
f.write_str("\"")
}
}
pub type QHash<T> = HashMap<QualifiedName, T>;
impl PartialEq for QualifiedName {
fn eq(&self, other: &QualifiedName) -> bool {
self.nsuri.as_ref().map_or_else(
|| {
other
.nsuri
.as_ref()
.map_or_else(|| self.localname.eq(&other.localname), |_| false)
},
|ns| {
other.nsuri.as_ref().map_or_else(
|| false,
|ons| ns.eq(ons) && self.localname.eq(&other.localname),
)
},
)
}
}
impl PartialOrd for QualifiedName {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (&self.nsuri, &other.nsuri) {
(None, None) => self.localname.partial_cmp(&other.localname),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(Some(n), Some(m)) => {
if n == m {
self.localname.partial_cmp(&other.localname)
} else {
n.partial_cmp(m)
}
}
}
}
}
impl Ord for QualifiedName {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
impl Eq for QualifiedName {}
impl Hash for QualifiedName {
fn hash<H: Hasher>(&self, state: &mut H) {
if let Some(ns) = self.nsuri.as_ref() {
ns.hash(state)
}
self.localname.hash(state);
}
}
impl TryFrom<&str> for QualifiedName {
type Error = Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let state: ParserState<Nullo> = ParserState::new(None, None, None);
match eqname()((s, state)) {
Ok((_, qn)) => Ok(qn),
Err(_) => Err(Error::new(
ErrorKind::ParseError,
String::from("unable to parse qualified name"),
)),
}
}
}
impl TryFrom<(&str, Rc<NamespaceMap>)> for QualifiedName {
type Error = Error;
fn try_from(s: (&str, Rc<NamespaceMap>)) -> Result<Self, Self::Error> {
let state: ParserState<Nullo> = ParserState::new(None, None, None);
match eqname()((s.0, state)) {
Ok((_, qn)) => {
if qn.prefix().is_some() && qn.namespace_uri().is_none() {
match s.1.get(&qn.prefix()) {
Some(ns) => Ok(QualifiedName::new_from_values(
Some(ns),
qn.prefix(),
qn.localname().clone(),
)),
_ => Err(Error::new(
ErrorKind::Unknown,
format!(
"unable to match prefix \"{}\"",
qn.prefix_to_string().unwrap()
),
)),
}
} else {
Ok(qn)
}
}
Err(_) => Err(Error::new(
ErrorKind::ParseError,
String::from("unable to parse qualified name"),
)),
}
}
}
impl<N: Node> TryFrom<(&str, N)> for QualifiedName {
type Error = Error;
fn try_from(s: (&str, N)) -> Result<Self, Self::Error> {
let state: ParserState<Nullo> = ParserState::new(None, None, None);
match eqname()((s.0, state)) {
Ok((_, qn)) => {
if qn.prefix().is_some() && qn.namespace_uri().is_none() {
s.1.namespace_iter()
.find(|ns| ns.name().localname() == qn.prefix().unwrap())
.map_or(
Err(Error::new(
ErrorKind::DynamicAbsent,
format!(
"no namespace matching prefix \"{}\"",
qn.prefix_to_string().unwrap()
),
)),
|ns| {
Ok(QualifiedName::new_from_values(
Some(ns.value()),
qn.prefix(),
qn.localname(),
))
},
)
} else {
Ok(qn)
}
}
Err(_) => Err(Error::new(
ErrorKind::ParseError,
String::from("unable to parse qualified name"),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unqualified_raw() {
assert_eq!(QualifiedName::new(None, None, "foo").to_string(), "foo")
}
#[test]
fn unqualified_rc() {
assert_eq!(
QualifiedName::new_from_values(None, None, Rc::new(Value::from("foo"))).to_string(),
"foo"
)
}
#[test]
fn qualified_raw() {
assert_eq!(
QualifiedName::new(
Some("http://example.org/whatsinaname/".to_string()),
Some("x".to_string()),
"foo".to_string()
)
.to_string(),
"x:foo"
)
}
#[test]
fn qualified_rc() {
assert_eq!(
QualifiedName::new_from_values(
Some(Rc::new(Value::from("http://example.org/whatsinaname/"))),
Some(Rc::new(Value::from("x"))),
Rc::new(Value::from("foo"))
)
.to_string(),
"x:foo"
)
}
#[test]
fn eqname() {
let e = QualifiedName::try_from("Q{http://example.org/bar}foo")
.expect("unable to parse EQName");
assert_eq!(e.localname_to_string(), "foo");
assert_eq!(
e.namespace_uri_to_string(),
Some(String::from("http://example.org/bar"))
);
assert_eq!(e.prefix_to_string(), None)
}
#[test]
fn hashmap() {
let mut h = QHash::<String>::new();
h.insert(
QualifiedName::new(None, None, "foo"),
String::from("this is unprefixed foo"),
);
h.insert(
QualifiedName::new(
Some("http://example.org/whatsinaname/".to_string()),
Some("x".to_string()),
"foo",
),
"this is x:foo".to_string(),
);
h.insert(
QualifiedName::new(
Some("http://example.org/whatsinaname/".to_string()),
Some("y".to_string()),
"bar",
),
"this is y:bar".to_string(),
);
assert_eq!(h.len(), 3);
assert_eq!(
h.get(&QualifiedName {
nsuri: Some(Rc::new(Value::from("http://example.org/whatsinaname/"))),
prefix: Some(Rc::new(Value::from("x"))),
localname: Rc::new(Value::from("foo")),
}),
Some(&"this is x:foo".to_string())
);
assert_eq!(
h.get(&QualifiedName {
nsuri: None,
prefix: None,
localname: Rc::new(Value::from("foo")),
}),
Some(&"this is unprefixed foo".to_string())
);
}
}