use core::ops::{RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; #[derive(Eq, PartialEq, Copy, Clone, core::marker::ConstParamTy)] pub enum Range { Range(usize, usize), RangeFrom(usize), RangeTo(usize), } impl Range { pub const fn range(self) -> (usize, usize) { match self { Range::Range(begin, end) => (begin, end), Range::RangeFrom(begin) => (begin, N), Range::RangeTo(end) => (0, end), } } pub const fn length(self) -> usize { let (begin, end) = self.range::(); end - begin } pub const fn valid(self) -> usize { (self.range::().1 <= N) as usize } } #[diagnostic::on_unimplemented( message = "{Self} is not a valid range type", label = "use a correct range type, such as {{Range(x..y), RangeInclusive(x..=y), RangeTo(..x), RangeToInclusive(..=x)}}" )] pub const trait Ranged { fn range(self) -> Range; } impl const Ranged for core::ops::Range { fn range(self) -> Range { Range::Range(self.start, self.end) } } impl const Ranged for RangeInclusive { fn range(self) -> Range { Range::Range(*self.start(), *self.end() + 1) } } impl const Ranged for RangeFrom { fn range(self) -> Range { Range::RangeFrom(self.start) } } impl const Ranged for RangeTo { fn range(self) -> Range { Range::RangeTo(self.end) } } impl const Ranged for RangeToInclusive { fn range(self) -> Range { Range::RangeTo(self.end + 1) } } /// Constifies a range. For use with [`slice`](Slice::slice). /// /// Takes a type in the form {[`Range`], [`RangeInclusive`], [`RangeTo`], [`RangeToInclusive`]}. #[allow(private_bounds)] pub const fn r(x: T) -> Range { Ranged::range(x) } /// Slicing arrays up. pub const trait Slice { /// Slices the array. /// Compile time checked. /// ``` /// # #![feature(generic_const_exprs, const_trait_impl)] /// # use atools::prelude::*; /// let x = atools::range::<5>(); /// assert_eq!(*x.slice::<{ r(2..=4) }>(), [2, 3, 4]); /// // x.slice::<{ r(..10) }>(); // ERROR /// ``` fn slice(&self) -> &[T; RANGE.length::()] where // comptime length check [(); RANGE.valid::() - 1]:; } const unsafe fn slice(x: &[T; N], begin: usize) -> &[T; TO] { // SAFETY: up to caller unsafe { &*x.as_ptr().add(begin).cast::<[T; TO]>() } } impl const Slice for [T; N] { fn slice(&self) -> &[T; RANGE.length::()] where [(); RANGE.valid::() - 1]:, { // SAFETY: the validity check ensures that the array will be in bounds. unsafe { slice::() }>(self, RANGE.range::().0) } } } #[test] fn slicing() { let x = [1, 2, 3]; let &[y] = x.slice::<{ r(2..) }>(); assert_eq!(y, 3); let &[y, z] = x.slice::<{ r(1..=2) }>(); assert_eq!([y, z], [2, 3]); let &y = x.slice::<{ r(..=2) }>(); assert_eq!(x, y); let &y = x.slice::<{ r(..2) }>(); assert_eq!(y, [1, 2]); }