use alloc::borrow::ToOwned;
use alloc::string::String;
#[cfg(not(feature = "extra-platforms"))]
use alloc::sync::Arc;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::ops::Deref;
#[cfg(feature = "extra-platforms")]
use portable_atomic_util::Arc;
pub use rxml_validation::{CompactString, Error, Name, NameStr, NcName, NcNameStr};
use crate::error::{Error as XmlError, ErrorContext};
enum Inner<'x> {
Owned(Arc<String>),
Shared(&'x str),
}
pub struct Namespace<'x>(Inner<'x>);
const fn xml() -> Namespace<'static> {
Namespace(Inner::Shared(crate::XMLNS_XML))
}
const fn xmlns() -> Namespace<'static> {
Namespace(Inner::Shared(crate::XMLNS_XMLNS))
}
const fn none() -> Namespace<'static> {
Namespace(Inner::Shared(crate::parser::XMLNS_UNNAMESPACED))
}
impl Namespace<'static> {
pub const XML: Namespace<'static> = xml();
pub const XMLNS: Namespace<'static> = xmlns();
pub const NONE: Namespace<'static> = 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
}
}
impl<'x> Namespace<'x> {
pub const fn from_str(s: &'x str) -> Self {
Self(Inner::Shared(s))
}
pub fn borrow(&self) -> Namespace<'_> {
match self.0 {
Inner::Shared(v) => Namespace(Inner::Shared(v)),
Inner::Owned(ref v) => Namespace(Inner::Owned(Arc::clone(v))),
}
}
pub fn make_mut(&mut self) -> &mut String {
match self.0 {
Inner::Shared(v) => {
let mut tmp = Inner::Owned(Arc::new(v.to_owned()));
core::mem::swap(&mut self.0, &mut tmp);
let Inner::Owned(ref mut v) = self.0 else {
unreachable!()
};
Arc::make_mut(v)
}
Inner::Owned(ref mut v) => Arc::make_mut(v),
}
}
pub fn into_static(self) -> Namespace<'static> {
Namespace(match self.0 {
Inner::Shared(v) => match Namespace::try_share_static(v) {
Some(v) => return v,
None => Inner::Owned(Arc::new(v.to_owned())),
},
Inner::Owned(v) => Inner::Owned(v),
})
}
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(v) => v,
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(v) => Inner::Shared(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(_) => f.debug_tuple("Namespace<'x>"),
Inner::Owned(_) => f.debug_tuple("Namespace<'static>"),
};
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) => Arc::unwrap_or_clone(v),
Inner::Shared(v) => v.to_owned(),
}
}
}
impl From<Namespace<'_>> for Arc<String> {
fn from(other: Namespace) -> Self {
match other.0 {
Inner::Owned(v) => v,
Inner::Shared(v) => Arc::new(v.to_owned()),
}
}
}
impl From<Name> for Namespace<'static> {
fn from(other: Name) -> Self {
Self(Inner::Owned(Arc::new(other.into())))
}
}
impl From<NcName> for Namespace<'static> {
fn from(other: NcName) -> Self {
Self(Inner::Owned(Arc::new(other.into())))
}
}
impl<'x> From<&'x Name> for Namespace<'x> {
fn from(other: &'x Name) -> Self {
Self(Inner::Shared(other.as_ref()))
}
}
impl<'x> From<&'x NcName> for Namespace<'x> {
fn from(other: &'x NcName) -> Self {
Self(Inner::Shared(other.as_ref()))
}
}
impl<'x> From<&'x str> for Namespace<'x> {
fn from(other: &'x str) -> Self {
Self(Inner::Shared(other))
}
}
impl From<String> for Namespace<'static> {
fn from(other: String) -> Self {
Self(Inner::Owned(Arc::new(other)))
}
}
impl From<Arc<String>> for Namespace<'static> {
fn from(other: Arc<String>) -> Self {
Self(Inner::Owned(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");
}
#[test]
fn namespace_size_leq_string_size() {
let ns_sz = core::mem::size_of::<Namespace>();
let string_sz = core::mem::size_of::<String>();
assert!(ns_sz <= string_sz, "{ns_sz} <= {string_sz}");
}
#[test]
fn option_namespace_size_leq_option_string_size() {
let ns_sz = core::mem::size_of::<Option<Namespace>>();
let string_sz = core::mem::size_of::<Option<String>>();
assert!(ns_sz <= string_sz, "{ns_sz} <= {string_sz}");
}
}