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)]
45 pub fn new_from(start: SpanValue, end: SpanValue) -> Self {
46 assert!(end >= start, "cannot create negative-size span");
47
48 Span { start, end }
49 }
50
51 #[inline(always)]
53 pub fn grow_front(&mut self, amount: SpanValue) {
54 self.end += amount;
55 }
56
57 #[inline(always)]
59 pub fn with_grow_front(&self, amount: SpanValue) -> Self {
60 let mut new = *self;
61 new.end += amount;
62 new
63 }
64
65 #[inline(always)]
70 pub fn grow_back(&mut self, amount: SpanValue) {
71 assert!(
72 self.start >= amount,
73 "cannot create a span with a negative start value"
74 );
75 self.start -= amount;
76 }
77
78 #[inline(always)]
83 pub fn with_grow_back(&self, amount: SpanValue) -> Self {
84 assert!(
85 self.start >= amount,
86 "cannot create a span with a negative start value"
87 );
88 let mut new = *self;
89 new.start -= amount;
90 new
91 }
92
93 #[inline(always)]
98 pub fn shrink_back(&mut self, amount: SpanValue) {
99 assert!(self.len() >= amount, "cannot create negative-size span");
100 self.start += amount;
101 }
102
103 #[inline(always)]
108 pub fn with_shrink_back(&self, amount: SpanValue) -> Self {
109 assert!(self.len() >= amount, "cannot create negative-size span");
110 let mut new = *self;
111 new.start += amount;
112 new
113 }
114
115 #[inline(always)]
120 pub fn shrink_front(&mut self, amount: SpanValue) {
121 assert!(self.len() >= amount, "cannot create negative-size span");
122 self.end -= amount;
123 }
124
125 #[inline(always)]
130 pub fn with_shrink_front(&self, amount: SpanValue) -> Self {
131 assert!(self.len() >= amount, "cannot create negative-size span");
132 let mut new = *self;
133 new.end -= amount;
134 new
135 }
136
137 #[inline(always)]
139 pub fn is_empty(&self) -> bool {
140 self.len() == 0
141 }
142
143 #[inline(always)]
145 pub fn len(&self) -> SpanValue {
146 self.end - self.start
147 }
148
149 #[inline(always)]
152 pub fn reset(&mut self) -> Self {
153 let span = *self;
154 self.start = self.end;
155 span
156 }
157
158 #[allow(clippy::unnecessary_cast)]
163 pub fn apply<'a>(&self, string: &'a str) -> &'a str {
164 let mut chars = string.char_indices();
165
166 let start = chars
167 .nth(self.start as usize)
168 .expect("string is too short to have the span applied")
169 .0;
170 let end = chars
171 .nth(self.len() as usize)
172 .expect("string is too short to have the span applied")
173 .0;
174 &string[start..end]
175 }
176
177 #[allow(clippy::unnecessary_cast)]
182 pub fn apply_bytes<'a>(&self, string: &'a str) -> &'a str {
183 assert!(
184 string.len() >= self.end as usize,
185 "string is too short to have the span applied"
186 );
187 &string[(self.start as usize)..(self.end as usize)]
188 }
189}
190
191impl From<Span> for Range<SpanValue> {
192 #[inline(always)]
193 fn from(val: Span) -> Self {
194 val.start..val.end
195 }
196}
197
198impl From<Range<SpanValue>> for Span {
199 #[inline(always)]
200 fn from(value: Range<SpanValue>) -> Self {
201 Self::new_from(value.start, value.end)
202 }
203}
204
205impl PartialOrd for Span {
206 #[inline(always)]
207 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
208 dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
209 }
210}
211
212fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
213 match (x, y) {
214 (x, y) if x == y => Some(x),
215 (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
216 (x, Ordering::Equal) => Some(x),
217 (Ordering::Equal, x) => Some(x),
218 _ => unreachable!(),
219 }
220}
221
222#[cfg(feature = "ariadne")]
223impl ariadne::Span for Span {
224 type SourceId = ();
225
226 fn source(&self) -> &Self::SourceId {
227 &()
228 }
229
230 #[allow(clippy::unnecessary_cast)]
231 fn start(&self) -> usize {
232 self.start as usize
233 }
234
235 #[allow(clippy::unnecessary_cast)]
236 fn end(&self) -> usize {
237 self.end as usize
238 }
239}