copy_range/
lib.rs

1#![no_std]
2//! `copy_range` provides three structs: [`CopyRange`], [`CopyRangeFrom`], and
3//! [`CopyRangeInclusive`].
4//!
5//! They are similar to `core::ops`'s [`Range`], [`RangeFrom`], and
6//! [`RangeInclusive`], respectively, except they implement `Copy` if their
7//! element type implements `Copy`, and they implement `IntoIterator` instead of
8//! `Iterator`.
9//!
10//! They are freely convertible to and from their `core::ops` counterparts (with
11//! a [note](CopyRangeInclusive::from_std) about `RangeInclusive`), and they
12//! implement most of the same (non-iterator-related) traits, notably
13//! [`RangeBounds`].
14//!
15//! Ranges of `usize` are additionally usable as the [`Index`] parameter for
16//! [arrays](prim@array), [slices](prim@slice), [string slices](prim@str) and
17//! (with the `"alloc"` feature enabled) [`Vec`][alloc::vec::Vec] and
18//! [`String`][alloc::string::String].
19
20// Much of this crate is adapted from the stdlib, specifically
21// `library/core/src/ops/range.rs`.
22
23#[cfg(feature = "alloc")]
24extern crate alloc;
25
26use core::ops::{
27    Bound, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull,
28    RangeInclusive, RangeTo, RangeToInclusive,
29};
30
31/// A (half-open) range bounded inclusively below and exclusively above. See
32/// [`core::ops::Range`].
33///
34/// Unlike `Range`, this struct is `Copy` if `Idx` is `Copy`, and implements
35/// `IntoIterator` instead of `Interator`.
36#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
37pub struct CopyRange<Idx> {
38    pub start: Idx,
39    pub end: Idx,
40}
41
42impl<Idx: core::fmt::Debug> core::fmt::Debug for CopyRange<Idx> {
43    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        self.start.fmt(fmt)?;
45        write!(fmt, "..")?;
46        self.end.fmt(fmt)?;
47        Ok(())
48    }
49}
50
51impl<Idx> CopyRange<Idx> {
52    /// Returns `true` if `item` is contained in the range.
53    ///
54    /// See [`Range::contains`][core::ops::Range::contains].
55    pub fn contains<U>(&self, item: &U) -> bool
56    where
57        Idx: PartialOrd<U>,
58        U: ?Sized + PartialOrd<Idx>,
59    {
60        <Self as RangeBounds<Idx>>::contains(self, item)
61    }
62
63    /// Convert a [`Range`] into a `CopyRange`.
64    pub fn from_std(range: Range<Idx>) -> Self {
65        range.into()
66    }
67
68    /// Convert a `CopyRange` into a [`Range`].
69    pub fn into_std(self) -> Range<Idx> {
70        self.into()
71    }
72
73    /// Returns `true` if the range contains no items.
74    ///
75    /// See [`Range::contains`][core::ops::Range::contains].
76    pub fn is_empty(&self) -> bool
77    where
78        Idx: PartialOrd,
79    {
80        self.start >= self.end
81    }
82
83    /// Returns the exact length of the range.
84    pub fn len(&self) -> usize
85    where
86        core::ops::Range<Idx>: ExactSizeIterator,
87        Self: Copy,
88    {
89        self.into_std().len()
90    }
91}
92
93/// Convert a [`Range`] into a `CopyRange`.
94impl<Idx> From<Range<Idx>> for CopyRange<Idx> {
95    fn from(Range { start, end }: Range<Idx>) -> Self {
96        Self { start, end }
97    }
98}
99
100/// Convert a `CopyRange` into a [`Range`].
101impl<Idx> From<CopyRange<Idx>> for Range<Idx> {
102    fn from(value: CopyRange<Idx>) -> Self {
103        value.start..value.end
104    }
105}
106
107/// A range only bounded inclusively below. See [`core::ops::RangeFrom`].
108///
109/// Unlike `RangeFrom`, this struct is `Copy` if `Idx` is `Copy`, and implements
110/// `IntoIterator` instead of `Interator`.
111#[derive(Clone, Copy, PartialEq, Eq, Hash)]
112pub struct CopyRangeFrom<Idx> {
113    pub start: Idx,
114}
115
116impl<Idx: core::fmt::Debug> core::fmt::Debug for CopyRangeFrom<Idx> {
117    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118        self.start.fmt(fmt)?;
119        write!(fmt, "..")?;
120        Ok(())
121    }
122}
123
124impl<Idx> CopyRangeFrom<Idx> {
125    /// Returns `true` if `item` is contained in the range.
126    ///
127    /// See [`RangeFrom::contains`][core::ops::RangeFrom::contains].
128    pub fn contains<U>(&self, item: &U) -> bool
129    where
130        Idx: PartialOrd<U>,
131        U: ?Sized + PartialOrd<Idx>,
132    {
133        <Self as RangeBounds<Idx>>::contains(self, item)
134    }
135
136    /// Convert a [`RangeFrom`] into a `CopyRangeFrom`.
137    pub fn from_std(range: RangeFrom<Idx>) -> Self {
138        range.into()
139    }
140
141    /// Convert a `CopyRangeFrom` into a [`RangeFrom`].
142    pub fn into_std(self) -> RangeFrom<Idx> {
143        self.into()
144    }
145}
146
147/// Convert a [`RangeFrom`] into a `CopyRangeFrom`.
148impl<Idx> From<RangeFrom<Idx>> for CopyRangeFrom<Idx> {
149    fn from(RangeFrom { start }: RangeFrom<Idx>) -> Self {
150        Self { start }
151    }
152}
153
154/// Convert a `CopyRangeFrom` into a [`RangeFrom`].
155impl<Idx> From<CopyRangeFrom<Idx>> for RangeFrom<Idx> {
156    fn from(value: CopyRangeFrom<Idx>) -> Self {
157        value.start..
158    }
159}
160
161/// A range bounded inclusively above and below. See
162/// [`core::ops::RangeInclusive`].
163///
164/// Unlike `RangeInclusive`, this struct is `Copy` if `Idx` is `Copy`, and
165/// implements `IntoIterator` instead of `Interator`.
166#[derive(Clone, Copy, PartialEq, Eq, Hash)]
167pub struct CopyRangeInclusive<Idx> {
168    pub start: Idx,
169    pub end: Idx,
170}
171
172impl<Idx: core::fmt::Debug> core::fmt::Debug for CopyRangeInclusive<Idx> {
173    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        self.start.fmt(fmt)?;
175        write!(fmt, "..=")?;
176        self.end.fmt(fmt)?;
177        Ok(())
178    }
179}
180
181impl<Idx> CopyRangeInclusive<Idx> {
182    /// Returns `true` if `item` is contained in the range.
183    ///
184    /// See [`RangeInclusive::contains`][core::ops::RangeInclusive::contains].
185    pub fn contains<U>(&self, item: &U) -> bool
186    where
187        Idx: PartialOrd<U>,
188        U: ?Sized + PartialOrd<Idx>,
189    {
190        <Self as RangeBounds<Idx>>::contains(self, item)
191    }
192
193    /// Returns `true` if the range contains no items.
194    ///
195    /// See [`RangeInclusive::contains`][core::ops::RangeInclusive::contains].
196    pub fn is_empty(&self) -> bool
197    where
198        Idx: PartialOrd,
199    {
200        !(self.start <= self.end)
201    }
202
203    /// Convert a [`RangeInclusive`] into a `CopyRangeInclusive`.
204    ///
205    /// Note: the value returned by this conversion is unspecified after the
206    /// `RangeInclusive` has been iterated to exhaustion.
207    pub fn from_std(range: RangeInclusive<Idx>) -> Self {
208        range.into()
209    }
210
211    /// Convert a `CopyRangeInclusive` into a [`RangeInclusive`].
212    pub fn into_std(self) -> RangeInclusive<Idx> {
213        self.into()
214    }
215
216    /// Returns the exact length of the range.
217    pub fn len(&self) -> usize
218    where
219        core::ops::RangeInclusive<Idx>: ExactSizeIterator,
220        Self: Copy,
221    {
222        self.into_std().len()
223    }
224}
225
226/// Convert a [`RangeInclusive`] into a `CopyRangeInclusive`.
227///
228/// Note: the value returned by this conversion is unspecified after the
229/// `RangeInclusive` has been iterated to exhaustion.
230impl<Idx> From<RangeInclusive<Idx>> for CopyRangeInclusive<Idx> {
231    fn from(range: RangeInclusive<Idx>) -> Self {
232        let (start, end) = range.into_inner();
233        Self { start, end }
234    }
235}
236
237/// Convert a `CopyRangeInclusive` into a [`RangeInclusive`].
238impl<Idx> From<CopyRangeInclusive<Idx>> for RangeInclusive<Idx> {
239    fn from(value: CopyRangeInclusive<Idx>) -> Self {
240        value.start..=value.end
241    }
242}
243
244impl<Idx> RangeBounds<Idx> for CopyRange<Idx> {
245    fn start_bound(&self) -> Bound<&Idx> {
246        Bound::Included(&self.start)
247    }
248
249    fn end_bound(&self) -> Bound<&Idx> {
250        Bound::Excluded(&self.end)
251    }
252}
253
254impl<Idx> RangeBounds<Idx> for CopyRange<&Idx> {
255    fn start_bound(&self) -> Bound<&Idx> {
256        Bound::Included(self.start)
257    }
258
259    fn end_bound(&self) -> Bound<&Idx> {
260        Bound::Excluded(self.end)
261    }
262}
263
264impl<Idx> IntoIterator for CopyRange<Idx>
265where
266    Range<Idx>: Iterator<Item = Idx>,
267{
268    type Item = Idx;
269
270    type IntoIter = Range<Idx>;
271
272    fn into_iter(self) -> Self::IntoIter {
273        self.start..self.end
274    }
275}
276
277impl<Idx> RangeBounds<Idx> for CopyRangeFrom<Idx> {
278    fn start_bound(&self) -> Bound<&Idx> {
279        Bound::Included(&self.start)
280    }
281
282    fn end_bound(&self) -> Bound<&Idx> {
283        Bound::Unbounded
284    }
285}
286
287impl<Idx> RangeBounds<Idx> for CopyRangeFrom<&Idx> {
288    fn start_bound(&self) -> Bound<&Idx> {
289        Bound::Included(self.start)
290    }
291
292    fn end_bound(&self) -> Bound<&Idx> {
293        Bound::Unbounded
294    }
295}
296
297impl<Idx> IntoIterator for CopyRangeFrom<Idx>
298where
299    RangeFrom<Idx>: Iterator<Item = Idx>,
300{
301    type Item = Idx;
302
303    type IntoIter = RangeFrom<Idx>;
304
305    fn into_iter(self) -> Self::IntoIter {
306        self.start..
307    }
308}
309
310impl<Idx> RangeBounds<Idx> for CopyRangeInclusive<Idx> {
311    fn start_bound(&self) -> Bound<&Idx> {
312        Bound::Included(&self.start)
313    }
314
315    fn end_bound(&self) -> Bound<&Idx> {
316        Bound::Included(&self.end)
317    }
318}
319
320impl<Idx> RangeBounds<Idx> for CopyRangeInclusive<&Idx> {
321    fn start_bound(&self) -> Bound<&Idx> {
322        Bound::Included(self.start)
323    }
324
325    fn end_bound(&self) -> Bound<&Idx> {
326        Bound::Included(self.end)
327    }
328}
329
330impl<Idx> IntoIterator for CopyRangeInclusive<Idx>
331where
332    RangeInclusive<Idx>: Iterator<Item = Idx>,
333{
334    type Item = Idx;
335
336    type IntoIter = RangeInclusive<Idx>;
337
338    fn into_iter(self) -> Self::IntoIter {
339        self.start..=self.end
340    }
341}
342
343/// [`core::ops::RangeFull`] is already `Copy`, so we just reexport it.
344pub type CopyRangeFull = RangeFull;
345/// [`core::ops::RangeTo`] is already `Copy` if `Idx` is `Copy`, so we just
346/// reexport it.
347pub type CopyRangeTo<Idx> = RangeTo<Idx>;
348/// [`core::ops::RangeToInclusive`] is already `Copy` if `Idx` is `Copy`, so we
349/// just reexport it.
350pub type CopyRangeToInclusive<Idx> = RangeToInclusive<Idx>;
351
352macro_rules! impl_index {
353    ([$($generics:tt)*], $ty:ty) => {
354        impl<$($generics)*> Index<CopyRange<usize>> for $ty
355        where
356            $ty: Index<Range<usize>>,
357        {
358            type Output = <$ty as Index<Range<usize>>>::Output;
359
360            fn index(&self, index: CopyRange<usize>) -> &Self::Output {
361                self.index(index.into_std())
362            }
363        }
364        impl<$($generics)*> IndexMut<CopyRange<usize>> for $ty
365        where
366            $ty: IndexMut<Range<usize>>,
367        {
368            fn index_mut(&mut self, index: CopyRange<usize>) -> &mut Self::Output {
369                self.index_mut(index.into_std())
370            }
371        }
372        impl<$($generics)*> Index<CopyRangeFrom<usize>> for $ty
373        where
374            $ty: Index<RangeFrom<usize>>,
375        {
376            type Output = <$ty as Index<RangeFrom<usize>>>::Output;
377
378            fn index(&self, index: CopyRangeFrom<usize>) -> &Self::Output {
379                self.index(index.into_std())
380            }
381        }
382        impl<$($generics)*> IndexMut<CopyRangeFrom<usize>> for $ty
383        where
384            $ty: IndexMut<RangeFrom<usize>>,
385        {
386            fn index_mut(&mut self, index: CopyRangeFrom<usize>) -> &mut Self::Output {
387                self.index_mut(index.into_std())
388            }
389        }
390        impl<$($generics)*> Index<CopyRangeInclusive<usize>> for $ty
391        where
392            $ty: Index<RangeInclusive<usize>>,
393        {
394            type Output = <$ty as Index<RangeInclusive<usize>>>::Output;
395
396            fn index(&self, index: CopyRangeInclusive<usize>) -> &Self::Output {
397                self.index(index.into_std())
398            }
399        }
400        impl<$($generics)*> IndexMut<CopyRangeInclusive<usize>> for $ty
401        where
402            $ty: IndexMut<RangeInclusive<usize>>,
403        {
404            fn index_mut(&mut self, index: CopyRangeInclusive<usize>) -> &mut Self::Output {
405                self.index_mut(index.into_std())
406            }
407        }
408    };
409}
410
411impl_index!([T], [T]);
412impl_index!([], str);
413#[cfg(feature = "alloc")]
414impl_index!([T], ::alloc::vec::Vec<T>);
415#[cfg(feature = "alloc")]
416impl_index!([], ::alloc::string::String);