//! The `ImmutableString` type. use crate::func::native::{shared_make_mut, shared_take}; use crate::{Shared, SmartString}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ borrow::Borrow, cmp::Ordering, fmt, hash::Hash, iter::FromIterator, ops::{Add, AddAssign, Deref, Sub, SubAssign}, str::FromStr, }; /// The system immutable string type. /// /// An [`ImmutableString`] wraps an `Rc` (or `Arc` under the `sync` feature) /// so that it can be simply shared and not cloned. /// /// # Example /// /// ``` /// use rhai::ImmutableString; /// /// let s1: ImmutableString = "hello".into(); /// /// // No actual cloning of the string is involved below. /// let s2 = s1.clone(); /// let s3 = s2.clone(); /// /// assert_eq!(s1, s2); /// /// // Clones the underlying string (because it is already shared) and extracts it. /// let mut s: String = s1.into_owned(); /// /// // Changing the clone has no impact on the previously shared version. /// s.push_str(", world!"); /// /// // The old version still exists. /// assert_eq!(s2, s3); /// assert_eq!(s2.as_str(), "hello"); /// /// // Not equals! /// assert_ne!(s2.as_str(), s.as_str()); /// assert_eq!(s, "hello, world!"); /// ``` #[derive(Clone, Eq, Ord, Hash, Default)] pub struct ImmutableString(Shared); impl Deref for ImmutableString { type Target = SmartString; #[inline(always)] fn deref(&self) -> &Self::Target { &self.0 } } impl AsRef for ImmutableString { #[inline(always)] fn as_ref(&self) -> &SmartString { &self.0 } } impl AsRef for ImmutableString { #[inline(always)] fn as_ref(&self) -> &str { &self.0 } } impl Borrow for ImmutableString { #[inline(always)] fn borrow(&self) -> &SmartString { &self.0 } } impl Borrow for ImmutableString { #[inline(always)] fn borrow(&self) -> &str { self.0.as_str() } } impl From<&str> for ImmutableString { #[inline(always)] fn from(value: &str) -> Self { let value: SmartString = value.into(); Self(value.into()) } } impl From> for ImmutableString { #[inline(always)] fn from(value: Box) -> Self { let value: SmartString = value.into(); Self(value.into()) } } impl From<&String> for ImmutableString { #[inline(always)] fn from(value: &String) -> Self { let value: SmartString = value.into(); Self(value.into()) } } impl From for ImmutableString { #[inline(always)] fn from(value: String) -> Self { let value: SmartString = value.into(); Self(value.into()) } } impl From<&SmartString> for ImmutableString { #[inline(always)] fn from(value: &SmartString) -> Self { Self(value.clone().into()) } } impl From for ImmutableString { #[inline(always)] fn from(value: SmartString) -> Self { Self(value.into()) } } impl From<&ImmutableString> for SmartString { #[inline(always)] fn from(value: &ImmutableString) -> Self { value.as_str().into() } } impl From for SmartString { #[inline(always)] fn from(mut value: ImmutableString) -> Self { std::mem::take(shared_make_mut(&mut value.0)) } } impl FromStr for ImmutableString { type Err = (); #[inline(always)] fn from_str(s: &str) -> Result { let s: SmartString = s.into(); Ok(Self(s.into())) } } impl FromIterator for ImmutableString { #[inline] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl<'a> FromIterator<&'a char> for ImmutableString { #[inline] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().cloned().collect::().into()) } } impl<'a> FromIterator<&'a str> for ImmutableString { #[inline] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl<'a> FromIterator for ImmutableString { #[inline] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl<'a> FromIterator for ImmutableString { #[inline] fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect::().into()) } } impl fmt::Display for ImmutableString { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.0.as_str(), f) } } impl fmt::Debug for ImmutableString { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.0.as_str(), f) } } impl Add for ImmutableString { type Output = Self; #[inline] 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 Add for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: Self) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.clone() } else { let mut s = self.clone(); s.make_mut().push_str(rhs.0.as_str()); s } } } impl AddAssign<&ImmutableString> for ImmutableString { #[inline] fn add_assign(&mut self, rhs: &ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0.clone(); } else { self.make_mut().push_str(rhs.0.as_str()); } } } } impl AddAssign for ImmutableString { #[inline] fn add_assign(&mut self, rhs: ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0; } else { self.make_mut().push_str(rhs.0.as_str()); } } } } impl Add<&str> for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: &str) -> Self::Output { if !rhs.is_empty() { self.make_mut().push_str(rhs); } self } } impl Add<&str> for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: &str) -> Self::Output { if rhs.is_empty() { self.clone() } else { let mut s = self.clone(); s.make_mut().push_str(rhs); s } } } impl AddAssign<&str> for ImmutableString { #[inline] fn add_assign(&mut self, rhs: &str) { if !rhs.is_empty() { self.make_mut().push_str(rhs); } } } impl Add for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: String) -> Self::Output { if rhs.is_empty() { self } else if self.is_empty() { rhs.into() } else { self.make_mut().push_str(&rhs); self } } } impl Add for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: String) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.into() } else { let mut s = self.clone(); s.make_mut().push_str(&rhs); s } } } impl AddAssign for ImmutableString { #[inline] fn add_assign(&mut self, rhs: String) { if !rhs.is_empty() { if self.is_empty() { let rhs: SmartString = rhs.into(); self.0 = rhs.into(); } else { self.make_mut().push_str(&rhs); } } } } impl Add for ImmutableString { type Output = Self; #[inline] fn add(mut self, rhs: char) -> Self::Output { self.make_mut().push(rhs); self } } impl Add for &ImmutableString { type Output = ImmutableString; #[inline] fn add(self, rhs: char) -> Self::Output { let mut s = self.clone(); s.make_mut().push(rhs); s } } impl AddAssign for ImmutableString { #[inline] fn add_assign(&mut self, rhs: char) { self.make_mut().push(rhs); } } impl Sub for ImmutableString { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self::Output { if rhs.is_empty() { self } else if self.is_empty() { rhs } else { self.replace(rhs.as_str(), "").into() } } } impl Sub for &ImmutableString { type Output = ImmutableString; #[inline] fn sub(self, rhs: Self) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.clone() } else { self.replace(rhs.as_str(), "").into() } } } impl SubAssign<&ImmutableString> for ImmutableString { #[inline] fn sub_assign(&mut self, rhs: &ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0.clone(); } else { let rhs: SmartString = self.replace(rhs.as_str(), "").into(); self.0 = rhs.into(); } } } } impl SubAssign for ImmutableString { #[inline] fn sub_assign(&mut self, rhs: ImmutableString) { if !rhs.is_empty() { if self.is_empty() { self.0 = rhs.0; } else { let rhs: SmartString = self.replace(rhs.as_str(), "").into(); self.0 = rhs.into(); } } } } impl Sub for ImmutableString { type Output = Self; #[inline] fn sub(self, rhs: String) -> Self::Output { if rhs.is_empty() { self } else if self.is_empty() { rhs.into() } else { self.replace(&rhs, "").into() } } } impl Sub for &ImmutableString { type Output = ImmutableString; #[inline] fn sub(self, rhs: String) -> Self::Output { if rhs.is_empty() { self.clone() } else if self.is_empty() { rhs.into() } else { self.replace(&rhs, "").into() } } } impl SubAssign for ImmutableString { #[inline] fn sub_assign(&mut self, rhs: String) { let rhs: SmartString = self.replace(&rhs, "").into(); self.0 = rhs.into(); } } impl Sub for ImmutableString { type Output = Self; #[inline(always)] fn sub(self, rhs: char) -> Self::Output { self.replace(rhs, "").into() } } impl Sub for &ImmutableString { type Output = ImmutableString; #[inline(always)] fn sub(self, rhs: char) -> Self::Output { self.replace(rhs, "").into() } } impl SubAssign for ImmutableString { #[inline] fn sub_assign(&mut self, rhs: char) { let rhs: SmartString = self.replace(rhs, "").into(); self.0 = rhs.into(); } } impl> PartialEq for ImmutableString { #[inline(always)] fn eq(&self, other: &S) -> bool { self.as_str().eq(other.as_ref()) } } impl PartialEq for str { #[inline(always)] fn eq(&self, other: &ImmutableString) -> bool { self.eq(other.as_str()) } } impl PartialEq for String { #[inline(always)] fn eq(&self, other: &ImmutableString) -> bool { self.eq(other.as_str()) } } impl> PartialOrd for ImmutableString { fn partial_cmp(&self, other: &S) -> Option { self.as_str().partial_cmp(other.as_ref()) } } impl PartialOrd for str { #[inline(always)] fn partial_cmp(&self, other: &ImmutableString) -> Option { self.partial_cmp(other.as_str()) } } impl PartialOrd for String { #[inline(always)] fn partial_cmp(&self, other: &ImmutableString) -> Option { self.as_str().partial_cmp(other.as_str()) } } impl ImmutableString { /// Create a new [`ImmutableString`]. #[inline(always)] pub fn new() -> Self { Self(SmartString::new_const().into()) } /// Consume the [`ImmutableString`] and convert it into a [`String`]. /// If there are other references to the same string, a cloned copy is returned. #[inline] pub fn into_owned(mut self) -> String { self.make_mut(); // Make sure it is unique reference shared_take(self.0).into() // Should succeed } /// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references). /// Then return a mutable reference to the [`SmartString`]. #[inline(always)] pub(crate) fn make_mut(&mut self) -> &mut SmartString { shared_make_mut(&mut self.0) } /// Returns `true` if the two [`ImmutableString`]'s point to the same allocation. /// /// # Example /// /// ``` /// use rhai::ImmutableString; /// /// let s1: ImmutableString = "hello".into(); /// let s2 = s1.clone(); /// let s3: ImmutableString = "hello".into(); /// /// assert_eq!(s1, s2); /// assert_eq!(s1, s3); /// assert_eq!(s2, s3); /// /// assert!(s1.ptr_eq(&s2)); /// assert!(!s1.ptr_eq(&s3)); /// assert!(!s2.ptr_eq(&s3)); /// ``` #[inline(always)] pub fn ptr_eq(&self, other: &Self) -> bool { Shared::ptr_eq(&self.0, &other.0) } }