use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::sync::Arc;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::ops::Deref;
pub use rxml_validation::{CompactString, Error, Name, NameStr, NcName, NcNameStr};
use crate::error::{Error as XmlError, ErrorContext};
enum Shared {
Static(&'static str),
Ptr(Arc<String>),
}
enum Inner {
Owned(String),
Shared(Shared),
}
pub struct Namespace(Inner);
const fn xml() -> Namespace {
Namespace(Inner::Shared(Shared::Static(crate::XMLNS_XML)))
}
const fn xmlns() -> Namespace {
Namespace(Inner::Shared(Shared::Static(crate::XMLNS_XMLNS)))
}
const fn none() -> Namespace {
Namespace(Inner::Shared(Shared::Static(
crate::parser::XMLNS_UNNAMESPACED,
)))
}
impl Namespace {
pub const XML: Namespace = xml();
pub const XMLNS: Namespace = xmlns();
pub const NONE: Namespace = none();
#[inline(always)]
pub fn xml() -> &'static Self {
static RESULT: Namespace = Namespace::XML;
&RESULT
}
#[inline(always)]
pub fn xmlns() -> &'static Self {
static RESULT: Namespace = Namespace::XMLNS;
&RESULT
}
#[inline(always)]
pub fn none() -> &'static Self {
static RESULT: Namespace = Namespace::NONE;
&RESULT
}
pub fn try_share_static(s: &str) -> Option<Self> {
if s.is_empty() {
return Some(Self::NONE);
}
if s == crate::XMLNS_XML {
return Some(Self::XML);
}
if s == crate::XMLNS_XMLNS {
return Some(Self::XMLNS);
}
None
}
pub const fn from_str(s: &'static str) -> Self {
Self(Inner::Shared(Shared::Static(s)))
}
pub fn make_mut(&mut self) -> &mut String {
match self.0 {
Inner::Shared(Shared::Static(v)) => {
let mut tmp = Inner::Owned(v.to_owned());
core::mem::swap(&mut self.0, &mut tmp);
let Inner::Owned(ref mut v) = self.0 else {
unreachable!()
};
v
}
Inner::Shared(Shared::Ptr(ref mut v)) => Arc::make_mut(v),
Inner::Owned(ref mut v) => v,
}
}
pub fn make_shared(&mut self) {
if let Inner::Owned(ref mut v) = self.0 {
if let Some(result) = Self::try_share_static(v) {
*self = result;
return;
}
let mut tmp = String::new();
core::mem::swap(&mut tmp, v);
self.0 = Inner::Shared(Shared::Ptr(Arc::new(tmp)));
}
}
pub fn share(&mut self) -> Self {
self.make_shared();
self.clone()
}
pub fn shared(mut self) -> Self {
self.make_shared();
self.clone()
}
pub fn clone_shared(&self) -> Self {
self.clone().shared()
}
pub fn as_namespace_name(&self) -> Option<&str> {
let s = self.deref();
if s.is_empty() {
None
} else {
Some(s)
}
}
pub fn is_none(&self) -> bool {
self.deref().is_empty()
}
pub fn is_some(&self) -> bool {
!self.deref().is_empty()
}
pub fn as_str(&self) -> &str {
self.deref()
}
}
impl Deref for Namespace {
type Target = str;
fn deref(&self) -> &Self::Target {
match self.0 {
Inner::Shared(Shared::Static(v)) => v,
Inner::Shared(Shared::Ptr(ref v)) => v.deref().deref(),
Inner::Owned(ref v) => v.deref(),
}
}
}
impl AsRef<str> for Namespace {
fn as_ref(&self) -> &str {
self.deref()
}
}
impl Borrow<str> for Namespace {
fn borrow(&self) -> &str {
self.deref()
}
}
impl Clone for Namespace {
fn clone(&self) -> Self {
Self(match self.0 {
Inner::Shared(Shared::Static(v)) => Inner::Shared(Shared::Static(v)),
Inner::Shared(Shared::Ptr(ref v)) => Inner::Shared(Shared::Ptr(Arc::clone(v))),
Inner::Owned(ref v) => Inner::Owned(v.clone()),
})
}
}
impl fmt::Debug for Namespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut wrapper = match self.0 {
Inner::Shared(Shared::Static(_)) => f.debug_tuple("Namespace<{Static}>"),
Inner::Shared(Shared::Ptr(_)) => f.debug_tuple("Namespace<{Ptr}>"),
Inner::Owned(_) => f.debug_tuple("Namespace<{Owned}>"),
};
wrapper.field(&self.deref()).finish()
}
}
impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<str as fmt::Display>::fmt(self.deref(), f)
}
}
impl Eq for Namespace {}
impl From<Namespace> for String {
fn from(other: Namespace) -> Self {
match other.0 {
Inner::Owned(v) => v,
Inner::Shared(Shared::Static(v)) => v.to_owned(),
Inner::Shared(Shared::Ptr(v)) => Arc::unwrap_or_clone(v),
}
}
}
impl From<Namespace> for Arc<String> {
fn from(other: Namespace) -> Self {
match other.0 {
Inner::Owned(v) => Arc::new(v),
Inner::Shared(Shared::Static(v)) => Arc::new(v.to_owned()),
Inner::Shared(Shared::Ptr(v)) => v,
}
}
}
impl From<Name> for Namespace {
fn from(other: Name) -> Self {
let v: String = other.into();
debug_assert!(!v.is_empty()); Self(Inner::Owned(v))
}
}
impl From<NcName> for Namespace {
fn from(other: NcName) -> Self {
let v: String = other.into();
debug_assert!(!v.is_empty()); Self(Inner::Owned(v))
}
}
impl From<&'static Name> for Namespace {
fn from(other: &'static Name) -> Self {
let v: &'static str = other.as_ref();
debug_assert!(!v.is_empty()); Self(Inner::Shared(Shared::Static(v)))
}
}
impl From<&'static NcName> for Namespace {
fn from(other: &'static NcName) -> Self {
let v: &'static str = other.as_ref();
debug_assert!(!v.is_empty()); Self(Inner::Shared(Shared::Static(v)))
}
}
impl From<&'static str> for Namespace {
fn from(other: &'static str) -> Self {
Self(Inner::Shared(Shared::Static(other)))
}
}
impl From<String> for Namespace {
fn from(other: String) -> Self {
if let Some(result) = Self::try_share_static(&other) {
return result;
}
Self(Inner::Owned(other))
}
}
impl From<Arc<String>> for Namespace {
fn from(other: Arc<String>) -> Self {
if let Some(result) = Self::try_share_static(&other) {
return result;
}
Self(Inner::Shared(Shared::Ptr(other)))
}
}
impl Hash for Namespace {
fn hash<H: Hasher>(&self, h: &mut H) {
self.deref().hash(h)
}
}
impl Ord for Namespace {
fn cmp(&self, other: &Namespace) -> Ordering {
self.deref().cmp(other.deref())
}
}
impl PartialEq for Namespace {
fn eq(&self, other: &Namespace) -> bool {
self.deref() == other.deref()
}
}
impl PartialEq<&str> for Namespace {
fn eq(&self, other: &&str) -> bool {
self.deref() == *other
}
}
impl PartialEq<Namespace> for &str {
fn eq(&self, other: &Namespace) -> bool {
*self == other.deref()
}
}
impl PartialEq<str> for Namespace {
fn eq(&self, other: &str) -> bool {
self.deref() == other
}
}
impl PartialEq<Namespace> for str {
fn eq(&self, other: &Namespace) -> bool {
self == other.deref()
}
}
impl PartialOrd for Namespace {
fn partial_cmp(&self, other: &Namespace) -> Option<Ordering> {
Some(self.cmp(other))
}
}
pub fn validate_cdata(s: &str) -> Result<(), XmlError> {
rxml_validation::validate_cdata(s).map_err(|e| XmlError::from_validation(e, None))
}
pub fn validate_name(s: &str) -> Result<(), XmlError> {
rxml_validation::validate_name(s)
.map_err(|e| XmlError::from_validation(e, Some(ErrorContext::Name)))
}
pub fn validate_ncname(s: &str) -> Result<(), XmlError> {
match rxml_validation::validate_ncname(s)
.map_err(|e| XmlError::from_validation(e, Some(ErrorContext::Name)))
{
Err(XmlError::UnexpectedChar(ctx, ':', _)) => Err(XmlError::MultiColonName(ctx)),
Err(XmlError::InvalidSyntax(_)) => Err(XmlError::EmptyNamePart(None)),
other => other,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_slice_namespace_name() {
let nsn = Namespace::xml();
assert_eq!(&nsn[..4], "http");
}
}