kaon/common/
immutable_string.rs

1use std::{
2    fmt::{self, Display},
3    ops::{Add, AddAssign, Deref},
4    rc::Rc,
5};
6
7use super::Named;
8
9/// An immutable string type.
10///
11/// An [ImmutableString] wraps a `Rc<String>` (or `Arc<String>` when Send/Sync is added in the future)
12/// to ensure the string isn't cloned when unnessary.
13///
14/// # Example
15/// ``` rust
16/// use kaon::common::ImmutableString;
17///
18/// let s1: ImmutableString = "Hello".into();
19///
20/// // Cloning here doesn't actually do anything
21/// let s2 = s1.clone();
22/// let s3 = s1.clone();
23/// 
24/// // Consumes the ImmutableString and returns the underlying string
25/// let mut s: String = s1.into_owned();
26/// 
27/// // Changing the sting won't effect the clones
28/// s.push_str(", World!");
29/// 
30/// assert_ne!(s, s2.as_str());
31/// assert_eq!(s, "Hello, World!");
32///
33/// assert_eq!(s2, s3);
34/// ```
35#[derive(Debug, Clone, Default, Hash, PartialOrd, Ord, Eq)]
36pub struct ImmutableString(Rc<String>);
37
38impl ImmutableString {
39    /// Create a new [ImmutableString].
40    pub fn new() -> Self {
41        Self(Rc::new(String::new()))
42    }
43
44    /// Returns `true` if this [ImmutableString] has a length of zero, `false` otherwise.
45    pub fn is_empty(&self) -> bool {
46        self.0.is_empty()
47    }
48
49    /// Returns the length of this [ImmutableString].
50    pub fn length(&self) -> usize {
51        self.0.len()
52    }
53
54    /// Makes a mutable reference from this [ImmutableString].
55    pub fn make_mut(&mut self) -> &mut String {
56        Rc::make_mut(&mut self.0)
57    }
58
59    /// Consumes the [ImmutableString] and returns the inner string.
60    /// 
61    /// If there are more than one strong reference, the inner value is cloned instead.
62    pub fn into_owned(mut self) -> String {
63        self.make_mut();
64
65        Rc::try_unwrap(self.0).unwrap_or_else(|v| v.as_ref().to_string())
66    }
67}
68
69impl From<String> for ImmutableString {
70    fn from(str: String) -> Self {
71        Self(Rc::new(str))
72    }
73}
74
75impl From<&String> for ImmutableString {
76    fn from(str: &String) -> Self {
77        Self(Rc::new(str.to_string()))
78    }
79}
80
81impl From<&str> for ImmutableString {
82    fn from(str: &str) -> Self {
83        Self(Rc::new(str.to_string()))
84    }
85}
86
87impl From<&mut str> for ImmutableString {
88    fn from(str: &mut str) -> Self {
89        Self(Rc::new(str.to_string()))
90    }
91}
92
93impl From<Box<str>> for ImmutableString {
94    fn from(str: Box<str>) -> Self {
95        Self(Rc::new(str.to_string()))
96    }
97}
98
99impl Add<&str> for ImmutableString {
100    type Output = Self;
101
102    fn add(mut self, rhs: &str) -> Self::Output {
103        if rhs.is_empty() {
104            self
105        } else if self.is_empty() {
106            Self::from(rhs)
107        } else {
108            self.make_mut().push_str(rhs);
109
110            self
111        }
112    }
113}
114
115impl Add<&ImmutableString> for ImmutableString {
116    type Output = Self;
117
118    fn add(mut self, rhs: &ImmutableString) -> Self::Output {
119        if rhs.is_empty() {
120            self
121        } else if self.is_empty() {
122            rhs.clone()
123        } else {
124            self.make_mut().push_str(rhs.0.as_str());
125
126            self
127        }
128    }
129}
130
131impl Add<ImmutableString> for ImmutableString {
132    type Output = Self;
133
134    fn add(mut self, rhs: Self) -> Self::Output {
135        if rhs.is_empty() {
136            self
137        } else if self.is_empty() {
138            rhs
139        } else {
140            self.make_mut().push_str(rhs.0.as_str());
141
142            self
143        }
144    }
145}
146
147impl AddAssign<&str> for ImmutableString {
148    fn add_assign(&mut self, rhs: &str) {
149        self.make_mut().push_str(rhs);
150    }
151}
152
153impl AddAssign<&ImmutableString> for ImmutableString {
154    fn add_assign(&mut self, rhs: &ImmutableString) {
155        self.make_mut().push_str(rhs);
156    }
157}
158
159impl PartialEq for ImmutableString {
160    fn eq(&self, other: &Self) -> bool {
161        self.0.len() == other.0.len() && self.0 == other.0
162    }
163}
164
165impl Deref for ImmutableString {
166    type Target = String;
167
168    fn deref(&self) -> &Self::Target {
169        self.0.as_ref()
170    }
171}
172
173impl Display for ImmutableString {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        f.write_fmt(format_args!("{}", self.0))
176    }
177}
178
179impl Named for ImmutableString {
180    const NAME: &'static str = "String";    
181}
182
183#[cfg(test)]
184mod test {
185    use super::*;
186
187    #[test]
188    fn test_add() {
189        let str = ImmutableString::from("Hello, ");
190
191        assert_eq!(str + "World!", ImmutableString::from("Hello, World!"));
192    }
193
194    #[test]
195    fn test_add_assign() {
196        let mut str = ImmutableString::from("ab");
197        str += "c";
198
199        assert_eq!(*str, "abc");
200    }
201
202    #[test]
203    fn test_deref() {
204        let str = ImmutableString::from("Hello");
205
206        assert_eq!(*str, "Hello");
207    }
208
209    #[test]
210    fn test_immutable_str() {
211        let s1: ImmutableString = "Hello, World".into();
212
213        // Cloning here doesn't actually do anything.
214        let s2 = s1.clone();
215        let s3 = s1.clone();
216
217        assert_eq!(s1, s2);
218
219        let mut s: String = s1.into_owned();
220
221        s.push_str("!");
222
223        assert_eq!(s3, s2);
224
225        assert_eq!(s, "Hello, World!");
226    }
227}