Skip to main content

rama_utils/str/arcstr/
substr.rs

1#![expect(
2    // We follow libstd's lead and prefer to define both.
3    clippy::partialeq_ne_impl,
4    // Vendored from upstream `arcstr`: matches stdlib panicking conventions
5    // and uses inner `#[allow]` attributes in the upstream-idiomatic style.
6    clippy::panic,
7    clippy::unreachable,
8    clippy::allow_attributes,
9    reason = "vendored from upstream arcstr; preserve upstream idioms"
10)]
11
12use crate::std::borrow::ToOwned as _;
13use core::ops::{Range, RangeBounds};
14
15use super::ArcStr;
16
17type Idx = usize;
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    `rama` project 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 rama_utils::str::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 `try_substr_using` to try 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.try_substr_using(str::trim).unwrap();
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(target_pointer_width = "64")]
62#[allow(clippy::let_unit_value)]
63const fn to_idx_const(i: usize) -> Idx {
64    const DUMMY: [(); 1] = [()];
65    _ = DUMMY[i >> 32];
66    i as Idx
67}
68#[inline]
69#[cfg(not(target_pointer_width = "64"))]
70const fn to_idx_const(i: usize) -> Idx {
71    i as Idx
72}
73
74#[inline]
75#[cfg(target_pointer_width = "64")]
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(not(target_pointer_width = "64"))]
85fn to_idx(i: usize) -> Idx {
86    i as Idx
87}
88
89#[cold]
90#[inline(never)]
91#[cfg(target_pointer_width = "64")]
92fn index_overflow(i: usize) -> ! {
93    panic!(
94        "The index {i} is too large for arcstr::Substr (enable the `substr-usize-indices` feature in `arcstr` if you need this)"
95    );
96}
97#[cold]
98#[inline(never)]
99fn bad_substr_idx(s: &ArcStr, i: usize, e: usize) -> ! {
100    assert!(i <= e, "Bad substr range: start {i} must be <= end {e}");
101    let max = if cfg!(target_pointer_width = "64",) {
102        u32::MAX as usize
103    } else {
104        usize::MAX
105    };
106    let len = s.len().min(max);
107    assert!(
108        e <= len,
109        "Bad substr range: end {e} must be <= string length/index max size {len}"
110    );
111    assert!(
112        s.is_char_boundary(i) && s.is_char_boundary(e),
113        "Bad substr range: start and end must be on char boundaries"
114    );
115    unreachable!(
116        "[arcstr bug]: should have failed one of the above tests: \
117                  please report me. debugging info: b={}, e={}, l={}, max={:#x}",
118        i,
119        e,
120        s.len(),
121        max
122    );
123}
124
125impl Substr {
126    /// Construct an empty substr.
127    ///
128    /// # Examples
129    /// ```
130    /// # use rama_utils::str::arcstr::Substr;
131    /// let s = Substr::new();
132    /// assert_eq!(s, "");
133    /// ```
134    #[inline]
135    #[must_use]
136    pub const fn new() -> Self {
137        Self(ArcStr::new(), 0, 0)
138    }
139
140    /// Construct a Substr over the entire ArcStr.
141    ///
142    /// This is also provided as `Substr::from(some_arcstr)`, and can be
143    /// accomplished with `a.substr(..)`, `a.into_substr(..)`, ...
144    ///
145    /// # Examples
146    /// ```
147    /// # use rama_utils::str::arcstr::{Substr, ArcStr};
148    /// let s = Substr::full(ArcStr::from("foo"));
149    /// assert_eq!(s, "foo");
150    /// assert_eq!(s.range(), 0..3);
151    /// ```
152    #[inline]
153    #[must_use]
154    pub fn full(a: ArcStr) -> Self {
155        let l = to_idx(a.len());
156        Self(a, 0, l)
157    }
158
159    #[inline]
160    pub(crate) fn from_parts(a: &ArcStr, range: impl RangeBounds<usize>) -> Self {
161        use core::ops::Bound;
162        let begin = match range.start_bound() {
163            Bound::Included(&n) => n,
164            Bound::Excluded(&n) => n + 1,
165            Bound::Unbounded => 0,
166        };
167
168        let end = match range.end_bound() {
169            Bound::Included(&n) => n + 1,
170            Bound::Excluded(&n) => n,
171            Bound::Unbounded => a.len(),
172        };
173        _ = &a.as_str()[begin..end];
174
175        Self(ArcStr::clone(a), to_idx(begin), to_idx(end))
176    }
177
178    /// Extract a substr of this substr.
179    ///
180    /// If the result would be empty, a new strong reference to our parent is
181    /// not created.
182    ///
183    /// # Examples
184    /// ```
185    /// # use rama_utils::str::arcstr::{Substr, arcstr};
186    /// let s: Substr = arcstr!("foobarbaz").substr(3..);
187    /// assert_eq!(s.as_str(), "barbaz");
188    ///
189    /// let s2 = s.substr(1..5);
190    /// assert_eq!(s2, "arba");
191    /// ```
192    /// # Panics
193    /// If any of the following are untrue, we panic
194    /// - `range.start() <= range.end()`
195    /// - `range.end() <= self.len()`
196    /// - `self.is_char_boundary(start) && self.is_char_boundary(end)`
197    /// - These can be conveniently verified in advance using
198    ///   `self.get(start..end).is_some()` if needed.
199    #[inline]
200    #[must_use]
201    pub fn substr(&self, range: impl RangeBounds<usize>) -> Self {
202        use core::ops::Bound;
203        let my_end = self.2;
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 + begin;
217        let new_end = self.1 + end;
218        // _ = &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        Self(ArcStr::clone(&self.0), new_begin as Idx, new_end as Idx)
229    }
230
231    /// Extract a string slice containing our data.
232    ///
233    /// Note: This is an equivalent to our `Deref` implementation, but can be
234    /// more readable than `&*s` in the cases where a manual invocation of
235    /// `Deref` would be required.
236    ///
237    /// # Examples
238    /// ```
239    /// # use rama_utils::str::arcstr::{Substr, arcstr};
240    /// let s: Substr = arcstr!("foobar").substr(3..);
241    /// assert_eq!(s.as_str(), "bar");
242    /// ```
243    #[inline]
244    #[must_use]
245    pub fn as_str(&self) -> &str {
246        self
247    }
248
249    /// Returns the length of this `Substr` in bytes.
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// # use rama_utils::str::arcstr::{ArcStr, Substr};
255    /// let a: Substr = ArcStr::from("foo").substr(1..);
256    /// assert_eq!(a.len(), 2);
257    /// ```
258    #[inline]
259    #[must_use]
260    pub fn len(&self) -> usize {
261        debug_assert!(self.2 >= self.1);
262        self.2 - self.1
263    }
264
265    /// Returns true if this `Substr` is empty.
266    ///
267    /// # Examples
268    ///
269    /// ```
270    /// # use rama_utils::str::arcstr::{Substr, arcstr};
271    /// assert!(arcstr!("abc").substr(3..).is_empty());
272    /// assert!(!arcstr!("abc").substr(2..).is_empty());
273    /// assert!(Substr::new().is_empty());
274    /// ```
275    #[inline]
276    #[must_use]
277    pub fn is_empty(&self) -> bool {
278        self.2 == self.1
279    }
280
281    /// Convert us to a `core::string::String`.
282    ///
283    /// This is provided as an inherent method to avoid needing to route through
284    /// the `Display` machinery, but is equivalent to `ToString::to_string`.
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// # use rama_utils::str::arcstr::{Substr, arcstr};
290    /// let s: Substr = arcstr!("12345").substr(1..4);
291    /// assert_eq!(s.to_string(), "234");
292    /// ```
293    #[inline]
294    #[allow(clippy::inherent_to_string_shadow_display)]
295    #[must_use]
296    pub fn to_string(&self) -> crate::std::string::String {
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`].
303    ///
304    /// This is unsafe because currently `ArcStr` cannot provide a `&str` in a
305    /// `const fn`. If that changes then we will likely deprecate this function,
306    /// and provide a `pub const fn from_parts` with equivalent functionality.
307    ///
308    /// In the distant future, it would be nice if this accepted other kinds of
309    /// ranges too.
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// use rama_utils::str::arcstr::{ArcStr, Substr, arcstr};
315    /// const FOOBAR: ArcStr = arcstr!("foobar");
316    /// const OBA: Substr = unsafe { Substr::from_parts_unchecked(FOOBAR, 2..5) };
317    /// assert_eq!(OBA, "oba");
318    /// ```
319    // TODO: can I do a compile_fail test that only is a failure under a certain feature?
320    ///
321    /// # Safety
322    /// You promise that `range` is in bounds for `s`, and that the start and
323    /// end are both on character boundaries. Note that we do check that the
324    /// `usize` indices fit into `u32` if thats our configured index type, so
325    /// `_unchecked` is not *entirely* a lie.
326    ///
327    /// # Panics
328    /// If the `substr-usize-indices` is not enabled, and the target arch is
329    /// 64-bit, and the usizes do not fit in 32 bits, then we panic with a
330    /// (possibly strange-looking) index-out-of-bounds error in order to force
331    /// compilation failure.
332    #[inline]
333    #[must_use]
334    pub const unsafe fn from_parts_unchecked(s: ArcStr, range: Range<usize>) -> Self {
335        Self(s, to_idx_const(range.start), to_idx_const(range.end))
336    }
337
338    /// Returns `true` if the two `Substr`s have identical parents, and are
339    /// covering the same range.
340    ///
341    /// Note that the "identical"ness of parents is determined by
342    /// [`ArcStr::ptr_eq`], which can have surprising/nondeterministic results
343    /// when used on `const` `ArcStr`s. It is guaranteed that `Substr::clone()`s
344    /// will be `shallow_eq` eachother, however.
345    ///
346    /// This should generally only be used as an optimization, or a debugging
347    /// aide. Additionally, it is already used in the implementation of
348    /// `PartialEq`, so optimizing a comparison by performing it first is
349    /// generally unnecessary.
350    ///
351    /// # Examples
352    /// ```
353    /// # use rama_utils::str::arcstr::{ArcStr, Substr};
354    /// let parent = ArcStr::from("foooo");
355    /// let sub1 = parent.substr(1..3);
356    /// let sub2 = parent.substr(1..3);
357    /// assert!(Substr::shallow_eq(&sub1, &sub2));
358    /// // Same parent *and* contents, but over a different range: not `shallow_eq`.
359    /// let not_same = parent.substr(3..);
360    /// assert!(!Substr::shallow_eq(&sub1, &not_same));
361    /// ```
362    #[inline]
363    #[must_use]
364    pub fn shallow_eq(this: &Self, o: &Self) -> bool {
365        ArcStr::ptr_eq(&this.0, &o.0) && (this.1 == o.1) && (this.2 == o.2)
366    }
367
368    /// Returns the ArcStr this is a substring of.
369    ///
370    /// Note that the exact pointer value of this can be somewhat
371    /// nondeterministic when used with `const` `ArcStr`s. For example
372    ///
373    /// ```rust,ignore
374    /// use rama_utils::str::arcstr::{ArcStr, arcstr};
375    /// const FOO: ArcStr = arcstr!("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 rama_utils::str::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    #[must_use]
394    pub fn parent(&self) -> &ArcStr {
395        &self.0
396    }
397
398    /// Returns the range of bytes we occupy inside our parent.
399    ///
400    /// This range is always guaranteed to:
401    ///
402    /// - Have an end >= start.
403    /// - Have both start and end be less than or equal to `self.parent().len()`
404    /// - Have both start and end be on meet `self.parent().is_char_boundary(b)`
405    ///
406    /// To put another way, it's always sound to do
407    /// `s.parent().get_unchecked(s.range())`.
408    ///
409    /// ```
410    /// # use rama_utils::str::arcstr::ArcStr;
411    /// let parent = ArcStr::from("abc def");
412    /// let child = parent.substr(2..5);
413    /// assert_eq!(child.range(), 2..5);
414    ///
415    /// let child = parent.substr(..);
416    /// assert_eq!(child.range(), 0..7);
417    /// ```
418    #[inline]
419    #[must_use]
420    pub fn range(&self) -> Range<usize> {
421        self.1..self.2
422    }
423
424    /// If possible, returns a [`Substr`] of self over the given `&str`.
425    ///
426    /// It is not rare to end up with a `&str` which holds a view into a
427    /// `ArcStr`'s backing data. A common case is when using functionality that
428    /// takes and returns `&str` and are entirely unaware of `arcstr`, for
429    /// example: `str::trim()`.
430    ///
431    /// This function allows you to reconstruct a [`Substr`] from a `&str` which
432    /// is a view into this [`Substr`]'s backing string. Note that we accept the
433    /// empty string as input, in which case we return the same value as
434    /// [`Substr::new`] (For clarity, this no longer holds a reference to
435    /// `self.parent()`).
436    ///
437    /// # Examples
438    ///
439    /// ```
440    /// use rama_utils::str::arcstr::Substr;
441    /// let text = Substr::from("   abc");
442    /// let trimmed = text.trim();
443    /// let substr: Option<Substr> = text.try_substr_from(trimmed);
444    /// assert_eq!(substr.unwrap(), "abc");
445    /// // `&str`s not derived from `self` will return None.
446    /// let not_substr = text.try_substr_from("abc");
447    /// assert!(not_substr.is_none());
448    /// ```
449    ///
450    /// # Panics
451    ///
452    /// Panics if `substr` is a view into our memory but is >= `u32::MAX` bytes
453    /// away from our start, on a 64-bit machine, when `substr-usize-indices` is
454    /// not enabled.
455    #[must_use]
456    pub fn try_substr_from(&self, substr: &str) -> Option<Self> {
457        if substr.is_empty() {
458            return Some(Self::new());
459        }
460        let parent_ptr = self.0.as_ptr() as usize;
461        let self_start = parent_ptr + self.1;
462        let self_end = parent_ptr + self.2;
463
464        let substr_start = substr.as_ptr() as usize;
465        let substr_end = substr_start + substr.len();
466        if substr_start < self_start || substr_end > self_end {
467            return None;
468        }
469
470        let index = substr_start - self_start;
471        let end = index + substr.len();
472        Some(self.substr(index..end))
473    }
474    /// Compute a derived `&str` a function of `&str` => `&str`, and produce a
475    /// Substr of the result if possible.
476    ///
477    /// The function may return either a derived string, or any empty string.
478    ///
479    /// This function is mainly a wrapper around [`Substr::try_substr_from`]. If
480    /// you're coming to `arcstr` from the `shared_string` crate, this is the
481    /// moral equivalent of the `slice_with` function.
482    ///
483    /// # Examples
484    ///
485    /// ```
486    /// use rama_utils::str::arcstr::Substr;
487    /// let text = Substr::from("   abc");
488    /// let trimmed: Option<Substr> = text.try_substr_using(str::trim);
489    /// assert_eq!(trimmed.unwrap(), "abc");
490    /// let other = text.try_substr_using(|_s| "different string!");
491    /// assert_eq!(other, None);
492    /// // As a special case, this is allowed.
493    /// let empty = text.try_substr_using(|_s| "");
494    /// assert_eq!(empty.unwrap(), "");
495    /// ```
496    pub fn try_substr_using(&self, f: impl FnOnce(&str) -> &str) -> Option<Self> {
497        self.try_substr_from(f(self.as_str()))
498    }
499}
500
501impl From<ArcStr> for Substr {
502    #[inline]
503    fn from(a: ArcStr) -> Self {
504        Self::full(a)
505    }
506}
507
508impl From<&ArcStr> for Substr {
509    #[inline]
510    fn from(a: &ArcStr) -> Self {
511        Self::full(a.clone())
512    }
513}
514
515impl core::ops::Deref for Substr {
516    type Target = str;
517    #[inline]
518    fn deref(&self) -> &str {
519        debug_assert!(self.0.get(self.1..self.2).is_some());
520        unsafe { self.0.get_unchecked(self.1..self.2) }
521    }
522}
523
524impl PartialEq for Substr {
525    #[inline]
526    fn eq(&self, o: &Self) -> bool {
527        Self::shallow_eq(self, o) || PartialEq::eq(self.as_str(), o.as_str())
528    }
529    #[inline]
530    fn ne(&self, o: &Self) -> bool {
531        !Self::shallow_eq(self, o) && PartialEq::ne(self.as_str(), o.as_str())
532    }
533}
534
535impl PartialEq<ArcStr> for Substr {
536    #[inline]
537    fn eq(&self, o: &ArcStr) -> bool {
538        (ArcStr::ptr_eq(&self.0, o) && (self.1 == 0) && (self.2 == o.len()))
539            || PartialEq::eq(self.as_str(), o.as_str())
540    }
541    #[inline]
542    fn ne(&self, o: &ArcStr) -> bool {
543        (!ArcStr::ptr_eq(&self.0, o) || (self.1 != 0) || (self.2 != o.len()))
544            && PartialEq::ne(self.as_str(), o.as_str())
545    }
546}
547impl PartialEq<Substr> for ArcStr {
548    #[inline]
549    fn eq(&self, o: &Substr) -> bool {
550        PartialEq::eq(o, self)
551    }
552    #[inline]
553    fn ne(&self, o: &Substr) -> bool {
554        PartialEq::ne(o, self)
555    }
556}
557
558impl Eq for Substr {}
559
560impl PartialOrd for Substr {
561    #[inline]
562    #[allow(clippy::non_canonical_partial_ord_impl)]
563    fn partial_cmp(&self, s: &Self) -> Option<core::cmp::Ordering> {
564        Some(self.cmp(s))
565    }
566}
567
568impl Ord for Substr {
569    #[inline]
570    fn cmp(&self, s: &Self) -> core::cmp::Ordering {
571        self.as_str().cmp(s.as_str())
572    }
573}
574
575impl core::hash::Hash for Substr {
576    #[inline]
577    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
578        self.as_str().hash(h)
579    }
580}
581
582impl core::fmt::Debug for Substr {
583    #[inline]
584    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
585        core::fmt::Debug::fmt(self.as_str(), f)
586    }
587}
588
589impl core::fmt::Display for Substr {
590    #[inline]
591    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
592        core::fmt::Display::fmt(self.as_str(), f)
593    }
594}
595
596impl Default for Substr {
597    #[inline]
598    fn default() -> Self {
599        Self::new()
600    }
601}
602
603macro_rules! impl_from_via_arcstr {
604    ($($SrcTy:ty),+) => {$(
605        impl From<$SrcTy> for Substr {
606            #[inline]
607            fn from(v: $SrcTy) -> Self {
608                Self::full(ArcStr::from(v))
609            }
610        }
611    )+};
612}
613impl_from_via_arcstr![
614    &str,
615    &mut str,
616    crate::std::string::String,
617    &crate::std::string::String,
618    crate::std::boxed::Box<str>,
619    crate::std::rc::Rc<str>,
620    crate::std::Arc<str>,
621    crate::std::borrow::Cow<'_, str>
622];
623
624impl<'a> From<&'a Substr> for crate::std::borrow::Cow<'a, str> {
625    #[inline]
626    fn from(s: &'a Substr) -> Self {
627        crate::std::borrow::Cow::Borrowed(s)
628    }
629}
630
631impl<'a> From<Substr> for crate::std::borrow::Cow<'a, str> {
632    #[inline]
633    fn from(s: Substr) -> Self {
634        if let Some(st) = ArcStr::as_static(&s.0) {
635            debug_assert!(st.get(s.range()).is_some());
636            crate::std::borrow::Cow::Borrowed(unsafe { st.get_unchecked(s.range()) })
637        } else {
638            crate::std::borrow::Cow::Owned(s.to_string())
639        }
640    }
641}
642
643macro_rules! impl_peq {
644    (@one $a:ty, $b:ty) => {
645        #[allow(clippy::extra_unused_lifetimes)]
646        impl<'a> PartialEq<$b> for $a {
647            #[inline]
648            fn eq(&self, s: &$b) -> bool {
649                PartialEq::eq(&self[..], &s[..])
650            }
651            #[inline]
652            fn ne(&self, s: &$b) -> bool {
653                PartialEq::ne(&self[..], &s[..])
654            }
655        }
656    };
657    ($(($a:ty, $b:ty),)+) => {$(
658        impl_peq!(@one $a, $b);
659        impl_peq!(@one $b, $a);
660    )+};
661}
662
663impl_peq! {
664    (Substr, str),
665    (Substr, &'a str),
666    (Substr, crate::std::string::String),
667    (Substr, crate::std::borrow::Cow<'a, str>),
668    (Substr, crate::std::boxed::Box<str>),
669    (Substr, crate::std::Arc<str>),
670    (Substr, crate::std::rc::Rc<str>),
671}
672
673macro_rules! impl_index {
674    ($($IdxT:ty,)*) => {$(
675        impl core::ops::Index<$IdxT> for Substr {
676            type Output = str;
677            #[inline]
678            fn index(&self, i: $IdxT) -> &Self::Output {
679                &self.as_str()[i]
680            }
681        }
682    )*};
683}
684
685impl_index! {
686    core::ops::RangeFull,
687    core::ops::Range<usize>,
688    core::ops::RangeFrom<usize>,
689    core::ops::RangeTo<usize>,
690    core::ops::RangeInclusive<usize>,
691    core::ops::RangeToInclusive<usize>,
692}
693
694impl AsRef<str> for Substr {
695    #[inline]
696    fn as_ref(&self) -> &str {
697        self
698    }
699}
700
701impl AsRef<[u8]> for Substr {
702    #[inline]
703    fn as_ref(&self) -> &[u8] {
704        self.as_bytes()
705    }
706}
707
708impl core::borrow::Borrow<str> for Substr {
709    #[inline]
710    fn borrow(&self) -> &str {
711        self
712    }
713}
714
715impl core::str::FromStr for Substr {
716    type Err = core::convert::Infallible;
717    #[inline]
718    fn from_str(s: &str) -> Result<Self, Self::Err> {
719        Ok(Self::from(ArcStr::from(s)))
720    }
721}
722
723#[cfg(test)]
724mod test {
725    use super::*;
726    #[test]
727    #[should_panic]
728    #[cfg(not(miri))] // XXX does miri still hate unwinding?
729    #[cfg(target_pointer_width = "64")]
730    fn test_from_parts_unchecked_err() {
731        let s = crate::str::arcstr::arcstr!("foo");
732        // Note: this is actually a violation of the safety requirement of
733        // from_parts_unchecked (the indices are illegal), but I can't get an
734        // ArcStr that's big enough, and I'm the author so I know it's fine
735        // because we hit the panic case.
736        let _u = unsafe { Substr::from_parts_unchecked(s, 0x1_0000_0000usize..0x1_0000_0001) };
737    }
738    #[test]
739    fn test_from_parts_unchecked_valid() {
740        let s = crate::str::arcstr::arcstr!("foobar");
741        let u = unsafe { Substr::from_parts_unchecked(s, 2..5) };
742        assert_eq!(&*u, "oba");
743    }
744}