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