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 assert!(
165 string.len() >= self.end as usize,
166 "string is too short to have the span applied"
167 );
168 let start = string
169 .char_indices()
170 .map(|x| x.0)
171 .nth(self.start as usize)
172 .unwrap();
173 let end = string
174 .char_indices()
175 .map(|x| x.0)
176 .nth(self.end as usize)
177 .unwrap_or(string.len());
178 &string[start..end]
179 }
180}
181
182impl From<Span> for Range<SpanValue> {
183 #[inline(always)]
184 fn from(val: Span) -> Self {
185 val.start..val.end
186 }
187}
188
189impl From<Range<SpanValue>> for Span {
190 #[inline(always)]
191 fn from(value: Range<SpanValue>) -> Self {
192 Self::new_from(value.start, value.end)
193 }
194}
195
196impl PartialOrd for Span {
197 #[inline(always)]
198 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
199 dual_order(self.start.cmp(&other.start), self.end.cmp(&other.end))
200 }
201}
202
203fn dual_order(x: Ordering, y: Ordering) -> Option<Ordering> {
204 match (x, y) {
205 (x, y) if x == y => Some(x),
206 (Ordering::Greater, Ordering::Less) | (Ordering::Less, Ordering::Greater) => None,
207 (x, Ordering::Equal) => Some(x),
208 (Ordering::Equal, x) => Some(x),
209 _ => unreachable!(),
210 }
211}
212
213#[cfg(feature = "ariadne")]
214impl ariadne::Span for Span {
215 type SourceId = ();
216
217 fn source(&self) -> &Self::SourceId {
218 &()
219 }
220
221 #[allow(clippy::unnecessary_cast)]
222 fn start(&self) -> usize {
223 self.start as usize
224 }
225
226 #[allow(clippy::unnecessary_cast)]
227 fn end(&self) -> usize {
228 self.end as usize
229 }
230}