erg_common/
str.rs

1use std::borrow::{Borrow, Cow};
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::ops::{Add, Deref};
5
6#[cfg(feature = "pylib")]
7use pyo3::prelude::PyAnyMethods;
8#[cfg(feature = "pylib")]
9use pyo3::{FromPyObject, IntoPyObject, PyAny};
10
11pub type ArcStr = std::sync::Arc<str>;
12
13/// Used to hold an immutable string.
14///
15/// It can construct as a const (by Str::ever).
16#[derive(Debug, Clone, Eq)]
17pub enum Str {
18    Rc(ArcStr),
19    Static(&'static str),
20}
21
22#[cfg(feature = "pylib")]
23impl FromPyObject<'_> for Str {
24    fn extract_bound(ob: &pyo3::Bound<'_, PyAny>) -> pyo3::PyResult<Self> {
25        let s = ob.extract::<String>()?;
26        Ok(Str::Rc(s.into()))
27    }
28}
29
30#[cfg(feature = "pylib")]
31impl<'py> IntoPyObject<'py> for Str {
32    type Target = pyo3::types::PyString;
33    type Output = pyo3::Bound<'py, Self::Target>;
34    type Error = std::convert::Infallible;
35    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
36        (&self[..]).into_pyobject(py)
37    }
38}
39
40impl PartialEq for Str {
41    #[inline]
42    fn eq(&self, other: &Str) -> bool {
43        self[..] == other[..]
44    }
45}
46
47impl PartialEq<Str> for &mut Str {
48    #[inline]
49    fn eq(&self, other: &Str) -> bool {
50        self[..] == other[..]
51    }
52}
53
54impl PartialEq<str> for Str {
55    #[inline]
56    fn eq(&self, other: &str) -> bool {
57        self[..] == other[..]
58    }
59}
60
61impl PartialEq<&str> for Str {
62    #[inline]
63    fn eq(&self, other: &&str) -> bool {
64        self[..] == other[..]
65    }
66}
67
68impl PartialEq<String> for Str {
69    #[inline]
70    fn eq(&self, other: &String) -> bool {
71        self[..] == other[..]
72    }
73}
74
75impl Add<&str> for Str {
76    type Output = Str;
77    #[inline]
78    fn add(self, other: &str) -> Str {
79        Str::from(&format!("{self}{other}"))
80    }
81}
82
83impl Hash for Str {
84    fn hash<H: Hasher>(&self, state: &mut H) {
85        match self {
86            Str::Rc(s) => s[..].hash(state),
87            Str::Static(s) => (*s).hash(state),
88        }
89    }
90}
91
92impl fmt::Display for Str {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        match self {
95            Str::Rc(s) => write!(f, "{s}"),
96            Str::Static(s) => write!(f, "{s}"),
97        }
98    }
99}
100
101impl From<&Str> for String {
102    #[inline]
103    fn from(s: &Str) -> Self {
104        s.to_string()
105    }
106}
107
108impl From<Str> for String {
109    #[inline]
110    fn from(s: Str) -> Self {
111        s.to_string()
112    }
113}
114
115impl From<Str> for Cow<'_, str> {
116    fn from(s: Str) -> Self {
117        match s {
118            Str::Static(s) => Cow::Borrowed(s),
119            Str::Rc(s) => Cow::Owned(s.to_string()),
120        }
121    }
122}
123
124// &'static str -> &strになってしまわないように
125// あえて`impl<S: Into<Str>> From<S> for Str { ... }`はしない
126impl From<&'static str> for Str {
127    #[inline]
128    fn from(s: &'static str) -> Self {
129        Str::ever(s)
130    }
131}
132
133impl From<&String> for Str {
134    #[inline]
135    fn from(s: &String) -> Self {
136        Str::Rc((s[..]).into())
137    }
138}
139
140impl From<String> for Str {
141    #[inline]
142    fn from(s: String) -> Self {
143        Str::Rc((s[..]).into())
144    }
145}
146
147impl From<&ArcStr> for Str {
148    #[inline]
149    fn from(s: &ArcStr) -> Self {
150        Str::Rc(s.clone())
151    }
152}
153
154impl From<ArcStr> for Str {
155    #[inline]
156    fn from(s: ArcStr) -> Self {
157        Str::Rc(s)
158    }
159}
160
161impl From<&Str> for Str {
162    #[inline]
163    fn from(s: &Str) -> Self {
164        match s {
165            Str::Rc(s) => Str::Rc(s.clone()),
166            Str::Static(s) => Str::Static(s),
167        }
168    }
169}
170
171impl From<Cow<'_, str>> for Str {
172    #[inline]
173    fn from(s: Cow<'_, str>) -> Self {
174        match s {
175            Cow::Borrowed(s) => Str::rc(s),
176            Cow::Owned(s) => Str::Rc(s.into()),
177        }
178    }
179}
180
181impl Deref for Str {
182    type Target = str;
183    fn deref(&self) -> &Self::Target {
184        self.borrow()
185    }
186}
187
188impl Borrow<str> for Str {
189    #[inline]
190    fn borrow(&self) -> &str {
191        match self {
192            Str::Rc(s) => &s[..],
193            Str::Static(s) => s,
194        }
195    }
196}
197
198impl AsRef<str> for Str {
199    fn as_ref(&self) -> &str {
200        self.borrow()
201    }
202}
203
204impl Str {
205    pub const fn ever(s: &'static str) -> Self {
206        Str::Static(s)
207    }
208
209    pub fn rc(s: &str) -> Self {
210        Str::Rc(s.into())
211    }
212
213    pub fn leak(self) -> &'static str {
214        match self {
215            Str::Rc(s) => Box::leak(s.into()),
216            Str::Static(s) => s,
217        }
218    }
219
220    pub fn into_rc(self) -> ArcStr {
221        match self {
222            Str::Rc(s) => s,
223            Str::Static(s) => ArcStr::from(s),
224        }
225    }
226
227    pub fn is_uppercase(&self) -> bool {
228        self.chars()
229            .next()
230            .map(|c| c.is_uppercase())
231            .unwrap_or(false)
232    }
233
234    /// split string with multiple separators
235    /// ```rust
236    /// # use erg_common::str::Str;
237    /// let s = Str::rc("a.b::c");
238    /// assert_eq!(s.split_with(&[".", "::"]), vec!["a", "b", "c"]);
239    /// let s = Str::rc("ああ.いい::うう");
240    /// assert_eq!(s.split_with(&[".", "::"]), vec!["ああ", "いい", "うう"]);
241    /// let s = Str::rc("abc");
242    /// assert_eq!(s.split_with(&[".", "::"]), vec!["abc"]);
243    /// ```
244    pub fn split_with(&self, seps: &[&str]) -> Vec<&str> {
245        let mut result = vec![];
246        let mut last_offset = 0;
247        for (offset, _c) in self.char_indices() {
248            for sep in seps {
249                if self[offset..].starts_with(sep) {
250                    result.push(&self[last_offset..offset]);
251                    last_offset = offset + sep.len();
252                }
253            }
254        }
255        result.push(&self[last_offset..]);
256        result
257    }
258
259    pub fn reversed(&self) -> Str {
260        Str::rc(&self.chars().rev().collect::<String>())
261    }
262
263    /// Note that replacements may be chained because it attempt to rewrite in sequence
264    pub fn multi_replace(&self, paths: &[(&str, &str)]) -> Self {
265        let mut self_ = self.to_string();
266        for (from, to) in paths {
267            self_ = self_.replace(from, to);
268        }
269        Str::rc(&self_)
270    }
271
272    pub fn is_snake_case(&self) -> bool {
273        self.chars().all(|c| !c.is_uppercase())
274    }
275
276    pub fn to_snake_case(&self) -> Str {
277        let mut ret = String::new();
278        let mut prev = '_';
279        for c in self.chars() {
280            if c.is_ascii_uppercase() {
281                if prev != '_' {
282                    ret.push('_');
283                }
284                ret.push(c.to_ascii_lowercase());
285            } else {
286                ret.push(c);
287            }
288            prev = c;
289        }
290        Str::rc(&ret)
291    }
292
293    pub fn find_sub<'a>(&self, pats: &[&'a str]) -> Option<&'a str> {
294        pats.iter().find(|&&pat| self.contains(pat)).copied()
295    }
296
297    /// ```
298    /// # use erg_common::str::Str;
299    /// let s = Str::rc("\n");
300    /// assert_eq!(&s.escape()[..], "\\n");
301    /// let s = Str::rc("\\");
302    /// assert_eq!(&s.escape()[..], "\\\\");
303    /// ```
304    pub fn escape(&self) -> Str {
305        self.multi_replace(&[
306            ("\\", "\\\\"),
307            ("\0", "\\0"),
308            ("\r", "\\r"),
309            ("\n", "\\n"),
310            ("\"", "\\\""),
311            ("\'", "\\'"),
312        ])
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use super::*;
319
320    #[test]
321    fn test_split_with() {
322        assert_eq!(
323            Str::ever("aa::bb.cc").split_with(&[".", "::"]),
324            vec!["aa", "bb", "cc"]
325        );
326        assert_eq!(
327            Str::ever("aa::bb.cc").split_with(&["::", "."]),
328            vec!["aa", "bb", "cc"]
329        );
330        assert_eq!(
331            Str::ever("aaxxbbyycc").split_with(&["xx", "yy"]),
332            vec!["aa", "bb", "cc"]
333        );
334        assert_ne!(
335            Str::ever("aaxxbbyycc").split_with(&["xx", "yy"]),
336            vec!["aa", "bb", "ff"]
337        );
338    }
339}