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#[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
124impl 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 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 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 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}