use std::borrow::Borrow;
use std::hash::Hash;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub enum XString {
Str(&'static str),
Ref(Arc<str>),
}
impl XString {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
XString::Str(str) => str,
XString::Ref(arc) => arc.as_ref(),
}
}
}
impl AsRef<str> for XString {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Deref for XString {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl Borrow<str> for XString {
fn borrow(&self) -> &str {
self.as_str()
}
}
impl std::fmt::Display for XString {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}
impl Default for XString {
fn default() -> Self {
const { Self::Str("") }
}
}
impl PartialEq for XString {
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl Eq for XString {}
impl PartialOrd for XString {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(Ord::cmp(&self, &other))
}
}
impl Ord for XString {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
Ord::cmp(self.as_str(), other.as_str())
}
}
impl Hash for XString {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Hash::hash(self.as_str(), state);
}
}
impl From<String> for XString {
fn from(string: String) -> Self {
Self::Ref(string.into())
}
}
impl From<&'static str> for XString {
fn from(str: &'static str) -> Self {
Self::Str(str)
}
}
impl From<Arc<str>> for XString {
fn from(arc: Arc<str>) -> Self {
Self::Ref(arc)
}
}
impl<'t> From<&'t XString> for &'t str {
fn from(value: &'t XString) -> Self {
value.as_str()
}
}
impl From<bool> for XString {
fn from(t: bool) -> Self {
Self::Str(if t { "true" } else { "false" })
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::sync::Arc;
use super::XString;
#[test]
fn from_str() {
let xs: XString = "value".into();
assert_eq!(format!(" {xs:?} "), r#" Str("value") "#);
}
#[test]
fn from_arc() {
let s: Arc<str> = "value".into();
let xs: XString = s.into();
assert_eq!(format!(" {xs:?} "), r#" Ref("value") "#);
}
#[test]
fn eq() {
assert_eq!(XString::Str("A"), XString::Str("A"));
assert_eq!(XString::Str("A"), XString::Ref("A".into()));
assert_eq!(XString::Ref("A".into()), XString::Ref("A".into()));
}
#[test]
fn ne() {
assert_ne!(XString::Str("A"), XString::Str("B"));
assert_ne!(XString::Str("A"), XString::Ref("B".into()));
}
#[test]
fn ord() {
let mut strings = vec![
XString::Ref("b-arc".into()),
XString::Str("a-str"),
XString::Str("d-str"),
XString::Ref("c-arc".into()),
];
strings.sort();
assert_eq!(
vec![
XString::Str("a-str"),
XString::Ref("b-arc".into()),
XString::Ref("c-arc".into()),
XString::Str("d-str"),
],
strings
);
}
#[test]
fn hash() {
let strings = [
XString::Str("a-str"),
XString::Ref("b-arc".into()),
XString::Ref("c-arc".into()),
XString::Str("d-str"),
]
.into_iter()
.collect::<HashSet<_>>();
assert!(strings.contains("a-str"));
assert!(strings.contains("b-arc"));
assert!(strings.contains("c-arc"));
assert!(strings.contains("d-str"));
assert!(strings.contains(&XString::from("a-str")));
assert!(strings.contains(&XString::from("a-str".to_string())));
assert!(!strings.contains("x"));
assert!(!strings.contains(&XString::from("y")));
}
}