use std::{
fmt::{self, Display},
ops::{Add, AddAssign, Deref},
rc::Rc,
};
use super::Named;
#[derive(Debug, Clone, Default, Hash, PartialOrd, Ord, Eq)]
pub struct ImmutableString(Rc<String>);
impl ImmutableString {
pub fn new() -> Self {
Self(Rc::new(String::new()))
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn length(&self) -> usize {
self.0.len()
}
pub fn make_mut(&mut self) -> &mut String {
Rc::make_mut(&mut self.0)
}
pub fn into_owned(mut self) -> String {
self.make_mut();
Rc::try_unwrap(self.0).unwrap_or_else(|v| v.as_ref().to_string())
}
}
impl From<String> for ImmutableString {
fn from(str: String) -> Self {
Self(Rc::new(str))
}
}
impl From<&String> for ImmutableString {
fn from(str: &String) -> Self {
Self(Rc::new(str.to_string()))
}
}
impl From<&str> for ImmutableString {
fn from(str: &str) -> Self {
Self(Rc::new(str.to_string()))
}
}
impl From<&mut str> for ImmutableString {
fn from(str: &mut str) -> Self {
Self(Rc::new(str.to_string()))
}
}
impl From<Box<str>> for ImmutableString {
fn from(str: Box<str>) -> Self {
Self(Rc::new(str.to_string()))
}
}
impl Add<&str> for ImmutableString {
type Output = Self;
fn add(mut self, rhs: &str) -> Self::Output {
if rhs.is_empty() {
self
} else if self.is_empty() {
Self::from(rhs)
} else {
self.make_mut().push_str(rhs);
self
}
}
}
impl Add<&ImmutableString> for ImmutableString {
type Output = Self;
fn add(mut self, rhs: &ImmutableString) -> Self::Output {
if rhs.is_empty() {
self
} else if self.is_empty() {
rhs.clone()
} else {
self.make_mut().push_str(rhs.0.as_str());
self
}
}
}
impl Add<ImmutableString> for ImmutableString {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
if rhs.is_empty() {
self
} else if self.is_empty() {
rhs
} else {
self.make_mut().push_str(rhs.0.as_str());
self
}
}
}
impl AddAssign<&str> for ImmutableString {
fn add_assign(&mut self, rhs: &str) {
self.make_mut().push_str(rhs);
}
}
impl AddAssign<&ImmutableString> for ImmutableString {
fn add_assign(&mut self, rhs: &ImmutableString) {
self.make_mut().push_str(rhs);
}
}
impl PartialEq for ImmutableString {
fn eq(&self, other: &Self) -> bool {
self.0.len() == other.0.len() && self.0 == other.0
}
}
impl Deref for ImmutableString {
type Target = String;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl Display for ImmutableString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}
impl Named for ImmutableString {
const NAME: &'static str = "String";
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_add() {
let str = ImmutableString::from("Hello, ");
assert_eq!(str + "World!", ImmutableString::from("Hello, World!"));
}
#[test]
fn test_add_assign() {
let mut str = ImmutableString::from("ab");
str += "c";
assert_eq!(*str, "abc");
}
#[test]
fn test_deref() {
let str = ImmutableString::from("Hello");
assert_eq!(*str, "Hello");
}
#[test]
fn test_immutable_str() {
let s1: ImmutableString = "Hello, World".into();
let s2 = s1.clone();
let s3 = s1.clone();
assert_eq!(s1, s2);
let mut s: String = s1.into_owned();
s.push_str("!");
assert_eq!(s3, s2);
assert_eq!(s, "Hello, World!");
}
}