arcstr/substr.rs
1#![allow(
2 // We follow libstd's lead and prefer to define both.
3 clippy::partialeq_ne_impl,
4 // This is a really annoying clippy lint, since it's required for so many cases...
5 clippy::cast_ptr_alignment,
6 // For macros
7 clippy::redundant_slicing,
8)]
9#![cfg_attr(feature = "substr-usize-indices", allow(clippy::unnecessary_cast))]
10use crate::ArcStr;
11use core::ops::{Range, RangeBounds};
12
13#[cfg(feature = "substr-usize-indices")]
14type Idx = usize;
15
16#[cfg(not(feature = "substr-usize-indices"))]
17type Idx = u32;
18
19#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
20compile_error!(
21 "Non-32/64-bit pointers not supported right now due to insufficient \
22 testing on a platform like that. Please file a issue with the \
23 `arcstr` crate so we can talk about your use case if this is \
24 important to you."
25);
26
27/// A low-cost string type representing a view into an [`ArcStr`].
28///
29/// Conceptually this is `(ArcStr, Range<usize>)` with ergonomic helpers. In
30/// implementation, the only difference between it and that is that the index
31/// type is `u32` unless the `substr-usize-indices` feature is enabled, which
32/// makes them use `usize`.
33///
34/// # Examples
35///
36/// ```
37/// use arcstr::{ArcStr, Substr};
38/// let parent = ArcStr::from("foo bar");
39/// // The main way to create a Substr is with `ArcStr::substr`.
40/// let substr: Substr = parent.substr(3..);
41/// assert_eq!(substr, " bar");
42/// // You can use `substr_using` to turn a function which is
43/// // `&str => &str` into a function over `Substr => Substr`.
44/// // See also `substr_from`, `try_substr_{from,using}`, and
45/// // the functions with the same name on `ArcStr`.
46/// let trimmed = substr.substr_using(str::trim);
47/// assert_eq!(trimmed, "bar");
48/// ```
49///
50/// # Caveats
51///
52/// The main caveat is the bit about index types. The index type is u32 by
53/// default. You can turn on `substr-usize-indices` if you desire though. The
54/// feature doesn't change the public API at all, just makes it able to handle
55/// enormous strings without panicking. This seems very niche to me, though.
56#[derive(Clone)]
57#[repr(C)] // We mentioned ArcStr being good at FFI at some point so why not
58pub struct Substr(ArcStr, Idx, Idx);
59
60#[inline]
61#[cfg(all(target_pointer_width = "64", not(feature = "substr-usize-indices")))]
62#[allow(clippy::let_unit_value)]
63const fn to_idx_const(i: usize) -> Idx {
64 const DUMMY: [(); 1] = [()];
65 let _ = DUMMY[i >> 32];
66 i as Idx
67}
68#[inline]
69#[cfg(any(not(target_pointer_width = "64"), feature = "substr-usize-indices"))]
70const fn to_idx_const(i: usize) -> Idx {
71 i as Idx
72}
73
74#[inline]
75#[cfg(all(target_pointer_width = "64", not(feature = "substr-usize-indices")))]
76fn to_idx(i: usize) -> Idx {
77 if i > 0xffff_ffff {
78 index_overflow(i);
79 }
80 i as Idx
81}
82
83#[inline]
84#[cfg(any(not(target_pointer_width = "64"), feature = "substr-usize-indices"))]
85fn to_idx(i: usize) -> Idx {
86 i as Idx
87}
88
89#[cold]
90#[inline(never)]
91#[cfg(all(target_pointer_width = "64", not(feature = "substr-usize-indices")))]
92fn index_overflow(i: usize) -> ! {
93 panic!("The index {} is too large for arcstr::Substr (enable the `substr-usize-indices` feature in `arcstr` if you need this)", i);
94}
95#[cold]
96#[inline(never)]
97fn bad_substr_idx(s: &ArcStr, i: usize, e: usize) -> ! {
98 assert!(i <= e, "Bad substr range: start {} must be <= end {}", i, e);
99 let max = if cfg!(all(
100 target_pointer_width = "64",
101 not(feature = "substr-usize-indices")
102 )) {
103 u32::MAX as usize
104 } else {
105 usize::MAX
106 };
107 let len = s.len().min(max);
108 assert!(
109 e <= len,
110 "Bad substr range: end {} must be <= string length/index max size {}",
111 e,
112 len
113 );
114 assert!(
115 s.is_char_boundary(i) && s.is_char_boundary(e),
116 "Bad substr range: start and end must be on char boundaries"
117 );
118 unreachable!(
119 "[arcstr bug]: should have failed one of the above tests: \
120 please report me. debugging info: b={}, e={}, l={}, max={:#x}",
121 i,
122 e,
123 s.len(),
124 max
125 );
126}
127
128impl Substr {
129 /// Construct an empty substr.
130 ///
131 /// # Examples
132 /// ```
133 /// # use arcstr::Substr;
134 /// let s = Substr::new();
135 /// assert_eq!(s, "");
136 /// ```
137 #[inline]
138 pub const fn new() -> Self {
139 Substr(ArcStr::new(), 0, 0)
140 }
141
142 /// Construct a Substr over the entire ArcStr.
143 ///
144 /// This is also provided as `Substr::from(some_arcstr)`, and can be
145 /// accomplished with `a.substr(..)`, `a.into_substr(..)`, ...
146 ///
147 /// # Examples
148 /// ```
149 /// # use arcstr::{Substr, ArcStr};
150 /// let s = Substr::full(ArcStr::from("foo"));
151 /// assert_eq!(s, "foo");
152 /// assert_eq!(s.range(), 0..3);
153 /// ```
154 #[inline]
155 pub fn full(a: ArcStr) -> Self {
156 let l = to_idx(a.len());
157 Substr(a, 0, l)
158 }
159
160 #[inline]
161 pub(crate) fn from_parts(a: &ArcStr, range: impl RangeBounds<usize>) -> Self {
162 use core::ops::Bound;
163 let begin = match range.start_bound() {
164 Bound::Included(&n) => n,
165 Bound::Excluded(&n) => n + 1,
166 Bound::Unbounded => 0,
167 };
168
169 let end = match range.end_bound() {
170 Bound::Included(&n) => n + 1,
171 Bound::Excluded(&n) => n,
172 Bound::Unbounded => a.len(),
173 };
174 let _ = &a.as_str()[begin..end];
175
176 Self(ArcStr::clone(a), to_idx(begin), to_idx(end))
177 }
178
179 /// Extract a substr of this substr.
180 ///
181 /// If the result would be empty, a new strong reference to our parent is
182 /// not created.
183 ///
184 /// # Examples
185 /// ```
186 /// # use arcstr::Substr;
187 /// let s: Substr = arcstr::literal!("foobarbaz").substr(3..);
188 /// assert_eq!(s.as_str(), "barbaz");
189 ///
190 /// let s2 = s.substr(1..5);
191 /// assert_eq!(s2, "arba");
192 /// ```
193 /// # Panics
194 /// If any of the following are untrue, we panic
195 /// - `range.start() <= range.end()`
196 /// - `range.end() <= self.len()`
197 /// - `self.is_char_boundary(start) && self.is_char_boundary(end)`
198 /// - These can be conveniently verified in advance using
199 /// `self.get(start..end).is_some()` if needed.
200 #[inline]
201 pub fn substr(&self, range: impl RangeBounds<usize>) -> Self {
202 use core::ops::Bound;
203 let my_end = self.2 as usize;
204
205 let begin = match range.start_bound() {
206 Bound::Included(&n) => n,
207 Bound::Excluded(&n) => n + 1,
208 Bound::Unbounded => 0,
209 };
210
211 let end = match range.end_bound() {
212 Bound::Included(&n) => n + 1,
213 Bound::Excluded(&n) => n,
214 Bound::Unbounded => self.len(),
215 };
216 let new_begin = self.1 as usize + begin;
217 let new_end = self.1 as usize + end;
218 // let _ = &self.0.as_str()[new_begin..new_end];
219 if begin > end
220 || end > my_end
221 || !self.0.is_char_boundary(new_begin)
222 || !self.0.is_char_boundary(new_end)
223 {
224 bad_substr_idx(&self.0, new_begin, new_end);
225 }
226 debug_assert!(self.0.get(new_begin..new_end).is_some());
227
228 debug_assert!(new_begin <= (Idx::MAX as usize) && new_end <= (Idx::MAX as usize));
229
230 Self(ArcStr::clone(&self.0), new_begin as Idx, new_end as Idx)
231 }
232
233 /// Extract a string slice containing our data.
234 ///
235 /// Note: This is an equivalent to our `Deref` implementation, but can be
236 /// more readable than `&*s` in the cases where a manual invocation of
237 /// `Deref` would be required.
238 ///
239 /// # Examples
240 /// ```
241 /// # use arcstr::Substr;
242 /// let s: Substr = arcstr::literal!("foobar").substr(3..);
243 /// assert_eq!(s.as_str(), "bar");
244 /// ```
245 #[inline]
246 pub fn as_str(&self) -> &str {
247 self
248 }
249
250 /// Returns the length of this `Substr` in bytes.
251 ///
252 /// # Examples
253 ///
254 /// ```
255 /// # use arcstr::{ArcStr, Substr};
256 /// let a: Substr = ArcStr::from("foo").substr(1..);
257 /// assert_eq!(a.len(), 2);
258 /// ```
259 #[inline]
260 pub fn len(&self) -> usize {
261 debug_assert!(self.2 >= self.1);
262 (self.2 - self.1) as usize
263 }
264
265 /// Returns true if this `Substr` is empty.
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// # use arcstr::Substr;
271 /// assert!(arcstr::literal!("abc").substr(3..).is_empty());
272 /// assert!(!arcstr::literal!("abc").substr(2..).is_empty());
273 /// assert!(Substr::new().is_empty());
274 /// ```
275 #[inline]
276 pub fn is_empty(&self) -> bool {
277 self.2 == self.1
278 }
279
280 /// Convert us to a `std::string::String`.
281 ///
282 /// This is provided as an inherent method to avoid needing to route through
283 /// the `Display` machinery, but is equivalent to `ToString::to_string`.
284 ///
285 /// # Examples
286 ///
287 /// ```
288 /// # use arcstr::Substr;
289 /// let s: Substr = arcstr::literal!("12345").substr(1..4);
290 /// assert_eq!(s.to_string(), "234");
291 /// ```
292 #[inline]
293 #[allow(clippy::inherent_to_string_shadow_display)]
294 pub fn to_string(&self) -> alloc::string::String {
295 #[cfg(not(feature = "std"))]
296 use alloc::borrow::ToOwned;
297 self.as_str().to_owned()
298 }
299
300 /// Unchecked function to construct a [`Substr`] from an [`ArcStr`] and a
301 /// byte range. Direct usage of this function is largely discouraged in
302 /// favor of [`ArcStr::substr`][crate::ArcStr::substr], or the
303 /// [`literal_substr!`](crate::literal_substr) macro, which currently is
304 /// implemented using a call to this function (however, can guarantee safe
305 /// usage).
306 ///
307 /// This is unsafe because currently `ArcStr` cannot provide a `&str` in a
308 /// `const fn`. If that changes then we will likely deprecate this function,
309 /// and provide a `pub const fn from_parts` with equivalent functionality.
310 ///
311 /// In the distant future, it would be nice if this accepted other kinds of
312 /// ranges too.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// use arcstr::{ArcStr, Substr};
318 /// const FOOBAR: ArcStr = arcstr::literal!("foobar");
319 /// const OBA: Substr = unsafe { Substr::from_parts_unchecked(FOOBAR, 2..5) };
320 /// assert_eq!(OBA, "oba");
321 /// ```
322 // TODO: can I do a compile_fail test that only is a failure under a certain feature?
323 ///
324 /// # Safety
325 /// You promise that `range` is in bounds for `s`, and that the start and
326 /// end are both on character boundaries. Note that we do check that the
327 /// `usize` indices fit into `u32` if thats our configured index type, so
328 /// `_unchecked` is not *entirely* a lie.
329 ///
330 /// # Panics
331 /// If the `substr-usize-indices` is not enabled, and the target arch is
332 /// 64-bit, and the usizes do not fit in 32 bits, then we panic with a
333 /// (possibly strange-looking) index-out-of-bounds error in order to force
334 /// compilation failure.
335 #[inline]
336 pub const unsafe fn from_parts_unchecked(s: ArcStr, range: Range<usize>) -> Self {
337 Self(s, to_idx_const(range.start), to_idx_const(range.end))
338 }
339
340 /// Returns `true` if the two `Substr`s have identical parents, and are
341 /// covering the same range.
342 ///
343 /// Note that the "identical"ness of parents is determined by
344 /// [`ArcStr::ptr_eq`], which can have surprising/nondeterministic results
345 /// when used on `const` `ArcStr`s. It is guaranteed that `Substr::clone()`s
346 /// will be `shallow_eq` eachother, however.
347 ///
348 /// This should generally only be used as an optimization, or a debugging
349 /// aide. Additionally, it is already used in the implementation of
350 /// `PartialEq`, so optimizing a comparison by performing it first is
351 /// generally unnecessary.
352 ///
353 /// # Examples
354 /// ```
355 /// # use arcstr::{ArcStr, Substr};
356 /// let parent = ArcStr::from("foooo");
357 /// let sub1 = parent.substr(1..3);
358 /// let sub2 = parent.substr(1..3);
359 /// assert!(Substr::shallow_eq(&sub1, &sub2));
360 /// // Same parent *and* contents, but over a different range: not `shallow_eq`.
361 /// let not_same = parent.substr(3..);
362 /// assert!(!Substr::shallow_eq(&sub1, ¬_same));
363 /// ```
364 #[inline]
365 pub fn shallow_eq(this: &Self, o: &Self) -> bool {
366 ArcStr::ptr_eq(&this.0, &o.0) && (this.1 == o.1) && (this.2 == o.2)
367 }
368
369 /// Returns the ArcStr this is a substring of.
370 ///
371 /// Note that the exact pointer value of this can be somewhat
372 /// nondeterministic when used with `const` `ArcStr`s. For example
373 ///
374 /// ```rust,ignore
375 /// const FOO: ArcStr = arcstr::literal!("foo");
376 /// // This is non-deterministic, as all references to a given
377 /// // const are not required to point to the same value.
378 /// ArcStr::ptr_eq(FOO.substr(..).parent(), &FOO);
379 /// ```
380 ///
381 /// # Examples
382 ///
383 /// ```
384 /// # use arcstr::ArcStr;
385 /// let parent = ArcStr::from("abc def");
386 /// let child = parent.substr(2..5);
387 /// assert!(ArcStr::ptr_eq(&parent, child.parent()));
388 ///
389 /// let child = parent.substr(..);
390 /// assert_eq!(child.range(), 0..7);
391 /// ```
392 #[inline]
393 pub fn parent(&self) -> &ArcStr {
394 &self.0
395 }
396
397 /// Returns the range of bytes we occupy inside our parent.
398 ///
399 /// This range is always guaranteed to:
400 ///
401 /// - Have an end >= start.
402 /// - Have both start and end be less than or equal to `self.parent().len()`
403 /// - Have both start and end be on meet `self.parent().is_char_boundary(b)`
404 ///
405 /// To put another way, it's always sound to do
406 /// `s.parent().get_unchecked(s.range())`.
407 ///
408 /// ```
409 /// # use arcstr::ArcStr;
410 /// let parent = ArcStr::from("abc def");
411 /// let child = parent.substr(2..5);
412 /// assert_eq!(child.range(), 2..5);
413 ///
414 /// let child = parent.substr(..);
415 /// assert_eq!(child.range(), 0..7);
416 /// ```
417 #[inline]
418 pub fn range(&self) -> Range<usize> {
419 (self.1 as usize)..(self.2 as usize)
420 }
421
422 /// Returns a [`Substr`] of self over the given `&str`, or panics.
423 ///
424 /// It is not rare to end up with a `&str` which holds a view into a
425 /// `Substr`'s backing data. A common case is when using functionality that
426 /// takes and returns `&str` and are entirely unaware of `arcstr`, for
427 /// example: `str::trim()`.
428 ///
429 /// This function allows you to reconstruct a [`Substr`] from a `&str` which
430 /// is a view into this `Substr`'s backing string.
431 ///
432 /// See [`Substr::try_substr_from`] for a version that returns an option
433 /// rather than panicking.
434 ///
435 /// # Examples
436 ///
437 /// ```
438 /// use arcstr::Substr;
439 /// let text = Substr::from(" abc");
440 /// let trimmed = text.trim();
441 /// let substr: Substr = text.substr_from(trimmed);
442 /// assert_eq!(substr, "abc");
443 /// ```
444 ///
445 /// # Panics
446 ///
447 /// Panics if `substr` isn't a view into our memory.
448 ///
449 /// Also panics if `substr` is a view into our memory but is >= `u32::MAX`
450 /// bytes away from our start, if we're a 64-bit machine and
451 /// `substr-usize-indices` is not enabled.
452 pub fn substr_from(&self, substr: &str) -> Substr {
453 // TODO: should outline `expect` call to avoid fmt bloat and let us
454 // provide better error message like we do for ArcStr
455 self.try_substr_from(substr)
456 .expect("non-substring passed to Substr::substr_from")
457 }
458
459 /// If possible, returns a [`Substr`] of self over the given `&str`.
460 ///
461 /// This is a fallible version of [`Substr::substr_from`].
462 ///
463 /// It is not rare to end up with a `&str` which holds a view into a
464 /// `ArcStr`'s backing data. A common case is when using functionality that
465 /// takes and returns `&str` and are entirely unaware of `arcstr`, for
466 /// example: `str::trim()`.
467 ///
468 /// This function allows you to reconstruct a [`Substr`] from a `&str` which
469 /// is a view into this [`Substr`]'s backing string. Note that we accept the
470 /// empty string as input, in which case we return the same value as
471 /// [`Substr::new`] (For clarity, this no longer holds a reference to
472 /// `self.parent()`).
473 ///
474 /// # Examples
475 ///
476 /// ```
477 /// use arcstr::Substr;
478 /// let text = Substr::from(" abc");
479 /// let trimmed = text.trim();
480 /// let substr: Option<Substr> = text.try_substr_from(trimmed);
481 /// assert_eq!(substr.unwrap(), "abc");
482 /// // `&str`s not derived from `self` will return None.
483 /// let not_substr = text.try_substr_from("abc");
484 /// assert!(not_substr.is_none());
485 /// ```
486 ///
487 /// # Panics
488 ///
489 /// Panics if `substr` is a view into our memory but is >= `u32::MAX` bytes
490 /// away from our start, on a 64-bit machine, when `substr-usize-indices` is
491 /// not enabled.
492 pub fn try_substr_from(&self, substr: &str) -> Option<Substr> {
493 if substr.is_empty() {
494 return Some(Substr::new());
495 }
496 let parent_ptr = self.0.as_ptr() as usize;
497 let self_start = parent_ptr + (self.1 as usize);
498 let self_end = parent_ptr + (self.2 as usize);
499
500 let substr_start = substr.as_ptr() as usize;
501 let substr_end = substr_start + substr.len();
502 if substr_start < self_start || substr_end > self_end {
503 return None;
504 }
505
506 let index = substr_start - self_start;
507 let end = index + substr.len();
508 Some(self.substr(index..end))
509 }
510 /// Compute a derived `&str` a function of `&str` => `&str`, and produce a
511 /// Substr of the result if possible.
512 ///
513 /// The function may return either a derived string, or any empty string.
514 ///
515 /// This function is mainly a wrapper around [`Substr::try_substr_from`]. If
516 /// you're coming to `arcstr` from the `shared_string` crate, this is the
517 /// moral equivalent of the `slice_with` function.
518 ///
519 /// # Examples
520 ///
521 /// ```
522 /// use arcstr::Substr;
523 /// let text = Substr::from(" abc");
524 /// let trimmed: Option<Substr> = text.try_substr_using(str::trim);
525 /// assert_eq!(trimmed.unwrap(), "abc");
526 /// let other = text.try_substr_using(|_s| "different string!");
527 /// assert_eq!(other, None);
528 /// // As a special case, this is allowed.
529 /// let empty = text.try_substr_using(|_s| "");
530 /// assert_eq!(empty.unwrap(), "");
531 /// ```
532 pub fn try_substr_using(&self, f: impl FnOnce(&str) -> &str) -> Option<Self> {
533 self.try_substr_from(f(self.as_str()))
534 }
535 /// Compute a derived `&str` a function of `&str` => `&str`, and produce a
536 /// Substr of the result.
537 ///
538 /// The function may return either a derived string, or any empty string.
539 /// Returning anything else will result in a panic.
540 ///
541 /// This function is mainly a wrapper around [`Substr::try_substr_from`]. If
542 /// you're coming to `arcstr` from the `shared_string` crate, this is the
543 /// likely closest to the `slice_with_unchecked` function, but this panics
544 /// instead of UB on dodginess.
545 ///
546 /// # Examples
547 ///
548 /// ```
549 /// use arcstr::Substr;
550 /// let text = Substr::from(" abc");
551 /// let trimmed: Substr = text.substr_using(str::trim);
552 /// assert_eq!(trimmed, "abc");
553 /// // As a special case, this is allowed.
554 /// let empty = text.substr_using(|_s| "");
555 /// assert_eq!(empty, "");
556 /// ```
557 pub fn substr_using(&self, f: impl FnOnce(&str) -> &str) -> Self {
558 self.substr_from(f(self.as_str()))
559 }
560}
561
562impl From<ArcStr> for Substr {
563 #[inline]
564 fn from(a: ArcStr) -> Self {
565 Self::full(a)
566 }
567}
568
569impl From<&ArcStr> for Substr {
570 #[inline]
571 fn from(a: &ArcStr) -> Self {
572 Self::full(a.clone())
573 }
574}
575
576impl core::ops::Deref for Substr {
577 type Target = str;
578 #[inline]
579 fn deref(&self) -> &str {
580 debug_assert!(self.0.get((self.1 as usize)..(self.2 as usize)).is_some());
581 unsafe { self.0.get_unchecked((self.1 as usize)..(self.2 as usize)) }
582 }
583}
584
585impl PartialEq for Substr {
586 #[inline]
587 fn eq(&self, o: &Self) -> bool {
588 Substr::shallow_eq(self, o) || PartialEq::eq(self.as_str(), o.as_str())
589 }
590 #[inline]
591 fn ne(&self, o: &Self) -> bool {
592 !Substr::shallow_eq(self, o) && PartialEq::ne(self.as_str(), o.as_str())
593 }
594}
595
596impl PartialEq<ArcStr> for Substr {
597 #[inline]
598 fn eq(&self, o: &ArcStr) -> bool {
599 (ArcStr::ptr_eq(&self.0, o) && (self.1 == 0) && (self.2 as usize == o.len()))
600 || PartialEq::eq(self.as_str(), o.as_str())
601 }
602 #[inline]
603 fn ne(&self, o: &ArcStr) -> bool {
604 (!ArcStr::ptr_eq(&self.0, o) || (self.1 != 0) || (self.2 as usize != o.len()))
605 && PartialEq::ne(self.as_str(), o.as_str())
606 }
607}
608impl PartialEq<Substr> for ArcStr {
609 #[inline]
610 fn eq(&self, o: &Substr) -> bool {
611 PartialEq::eq(o, self)
612 }
613 #[inline]
614 fn ne(&self, o: &Substr) -> bool {
615 PartialEq::ne(o, self)
616 }
617}
618
619impl Eq for Substr {}
620
621impl PartialOrd for Substr {
622 #[inline]
623 fn partial_cmp(&self, s: &Self) -> Option<core::cmp::Ordering> {
624 Some(self.as_str().cmp(s.as_str()))
625 }
626}
627
628impl Ord for Substr {
629 #[inline]
630 fn cmp(&self, s: &Self) -> core::cmp::Ordering {
631 self.as_str().cmp(s.as_str())
632 }
633}
634
635impl core::hash::Hash for Substr {
636 #[inline]
637 fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
638 self.as_str().hash(h)
639 }
640}
641
642impl core::fmt::Debug for Substr {
643 #[inline]
644 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
645 core::fmt::Debug::fmt(self.as_str(), f)
646 }
647}
648
649impl core::fmt::Display for Substr {
650 #[inline]
651 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
652 core::fmt::Display::fmt(self.as_str(), f)
653 }
654}
655
656impl Default for Substr {
657 #[inline]
658 fn default() -> Self {
659 Self::new()
660 }
661}
662
663macro_rules! impl_from_via_arcstr {
664 ($($SrcTy:ty),+) => {$(
665 impl From<$SrcTy> for Substr {
666 #[inline]
667 fn from(v: $SrcTy) -> Self {
668 Self::full(ArcStr::from(v))
669 }
670 }
671 )+};
672}
673impl_from_via_arcstr![
674 &str,
675 &mut str,
676 alloc::string::String,
677 &alloc::string::String,
678 alloc::boxed::Box<str>,
679 alloc::rc::Rc<str>,
680 alloc::sync::Arc<str>,
681 alloc::borrow::Cow<'_, str>
682];
683
684impl<'a> From<&'a Substr> for alloc::borrow::Cow<'a, str> {
685 #[inline]
686 fn from(s: &'a Substr) -> Self {
687 alloc::borrow::Cow::Borrowed(s)
688 }
689}
690
691impl<'a> From<Substr> for alloc::borrow::Cow<'a, str> {
692 #[inline]
693 fn from(s: Substr) -> Self {
694 if let Some(st) = ArcStr::as_static(&s.0) {
695 debug_assert!(st.get(s.range()).is_some());
696 alloc::borrow::Cow::Borrowed(unsafe { st.get_unchecked(s.range()) })
697 } else {
698 alloc::borrow::Cow::Owned(s.to_string())
699 }
700 }
701}
702
703macro_rules! impl_peq {
704 (@one $a:ty, $b:ty) => {
705 #[allow(clippy::extra_unused_lifetimes)]
706 impl<'a> PartialEq<$b> for $a {
707 #[inline]
708 fn eq(&self, s: &$b) -> bool {
709 PartialEq::eq(&self[..], &s[..])
710 }
711 #[inline]
712 fn ne(&self, s: &$b) -> bool {
713 PartialEq::ne(&self[..], &s[..])
714 }
715 }
716 };
717 ($(($a:ty, $b:ty),)+) => {$(
718 impl_peq!(@one $a, $b);
719 impl_peq!(@one $b, $a);
720 )+};
721}
722
723impl_peq! {
724 (Substr, str),
725 (Substr, &'a str),
726 (Substr, alloc::string::String),
727 (Substr, alloc::borrow::Cow<'a, str>),
728 (Substr, alloc::boxed::Box<str>),
729 (Substr, alloc::sync::Arc<str>),
730 (Substr, alloc::rc::Rc<str>),
731}
732
733macro_rules! impl_index {
734 ($($IdxT:ty,)*) => {$(
735 impl core::ops::Index<$IdxT> for Substr {
736 type Output = str;
737 #[inline]
738 fn index(&self, i: $IdxT) -> &Self::Output {
739 &self.as_str()[i]
740 }
741 }
742 )*};
743}
744
745impl_index! {
746 core::ops::RangeFull,
747 core::ops::Range<usize>,
748 core::ops::RangeFrom<usize>,
749 core::ops::RangeTo<usize>,
750 core::ops::RangeInclusive<usize>,
751 core::ops::RangeToInclusive<usize>,
752}
753
754impl AsRef<str> for Substr {
755 #[inline]
756 fn as_ref(&self) -> &str {
757 self
758 }
759}
760
761impl AsRef<[u8]> for Substr {
762 #[inline]
763 fn as_ref(&self) -> &[u8] {
764 self.as_bytes()
765 }
766}
767
768impl core::borrow::Borrow<str> for Substr {
769 #[inline]
770 fn borrow(&self) -> &str {
771 self
772 }
773}
774
775impl core::str::FromStr for Substr {
776 type Err = core::convert::Infallible;
777 #[inline]
778 fn from_str(s: &str) -> Result<Self, Self::Err> {
779 Ok(Self::from(ArcStr::from(s)))
780 }
781}
782
783#[cfg(test)]
784mod test {
785 use super::*;
786 #[test]
787 #[should_panic]
788 #[cfg(not(miri))] // XXX does miri still hate unwinding?
789 #[cfg(all(target_pointer_width = "64", not(feature = "substr-usize-indices")))]
790 fn test_from_parts_unchecked_err() {
791 let s = crate::literal!("foo");
792 // Note: this is actually a violation of the safety requirement of
793 // from_parts_unchecked (the indices are illegal), but I can't get an
794 // ArcStr that's big enough, and I'm the author so I know it's fine
795 // because we hit the panic case.
796 let _u = unsafe { Substr::from_parts_unchecked(s, 0x1_0000_0000usize..0x1_0000_0001) };
797 }
798 #[test]
799 fn test_from_parts_unchecked_valid() {
800 let s = crate::literal!("foobar");
801 let u = unsafe { Substr::from_parts_unchecked(s, 2..5) };
802 assert_eq!(&*u, "oba");
803 }
804}