1#![no_std]
2
3use core::{cmp::Ordering, ops::Range};
4
5#[cfg(feature = "span-value-usize")]
6pub type SpanValue = usize;
8#[cfg(feature = "span-value-u128")]
9pub type SpanValue = u128;
11#[cfg(feature = "span-value-u64")]
12pub type SpanValue = u64;
14#[cfg(feature = "span-value-u32")]
15pub type SpanValue = u32;
17#[cfg(feature = "span-value-u16")]
18pub type SpanValue = u16;
20#[cfg(feature = "span-value-u8")]
21pub type SpanValue = u8;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct Span {
27 pub start: SpanValue,
29 pub end: SpanValue,
31}
32
33impl Span {
34 #[inline(always)]
36 pub fn new() -> Self {
37 Self::new_from(0, 0)
38 }
39
40 #[inline(always)]
42 pub fn new_from(start: SpanValue, end: SpanValue) -> Self {
43 Span { start, end }
44 }
45
46 #[inline(always)]
48 pub fn grow_front(&mut self, amount: SpanValue) {
49 self.end += amount;
50 }
51
52 #[inline(always)]
57 pub fn grow_back(&mut self, amount: SpanValue) {
58 assert!(
59 self.start >= amount,
60 "cannot create a span with a negative start value"
61 );
62 self.start -= amount;
63 }
64
65 #[inline(always)]
70 #[allow(clippy::unnecessary_cast)]
71 pub fn shrink_back(&mut self, amount: SpanValue) {
72 assert!(self.len() >= amount, "cannot create negative-size span");
73 self.start += amount;
74 }
75
76 #[inline(always)]
81 #[allow(clippy::unnecessary_cast)]
82 pub fn shrink_front(&mut self, amount: SpanValue) {
83 assert!(self.len() >= amount, "cannot create negative-size span");
84 self.end -= amount;
85 }
86
87 #[inline(always)]
89 pub fn is_empty(&self) -> bool {
90 self.len() == 0
91 }
92
93 #[inline(always)]
95 pub fn len(&self) -> SpanValue {
96 self.end - self.start
97 }
98
99 #[inline(always)]
102 pub fn reset(&mut self) -> Self {
103 let span = *self;
104 self.start = self.end;
105 span
106 }
107
108 #[allow(clippy::unnecessary_cast)]
113 pub fn apply<'a>(&self, string: &'a str) -> &'a str {
114 assert!(
115 string.len() >= self.end as usize,
116 "string is too short to have the span applied"
117 );
118 let start = string.char_indices().nth(self.start as usize).unwrap().0;
119 let end = string.char_indices().nth(self.end as usize).unwrap().0;
120 &string[start..end]
121 }
122}
123
124impl From<Span> for Range<SpanValue> {
125 #[inline(always)]
126 fn from(val: Span) -> Self {
127 val.start..val.end
128 }
129}
130
131impl From<Range<SpanValue>> for Span {
132 #[inline(always)]
133 fn from(value: Range<SpanValue>) -> Self {
134 Self::new_from(value.start, value.end)
135 }
136}
137
138impl PartialOrd for Span {
139 #[inline(always)]
140 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
141 dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
142 }
143}
144
145fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
146 match (x, y) {
147 (x, y) if x == y => Some(x),
148 (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
149 (x, Ordering::Equal) => Some(x),
150 (Ordering::Equal, x) => Some(x),
151 _ => unreachable!(),
152 }
153}