1use codespan::Span as ByteSpan;
2use fm::FileId;
3use serde::{Deserialize, Serialize};
4use std::{
5 cmp::Ordering,
6 hash::{Hash, Hasher},
7 ops::Range,
8};
9
10pub type Position = u32;
11
12#[derive(Eq, Debug, Clone)]
13pub struct Located<T> {
14 pub contents: T,
15 location: Location,
16}
17
18impl<T: PartialEq> PartialEq<Located<T>> for Located<T> {
21 fn eq(&self, other: &Located<T>) -> bool {
22 self.contents == other.contents
23 }
24}
25
26impl<T: PartialOrd> PartialOrd<Located<T>> for Located<T> {
27 fn partial_cmp(&self, other: &Located<T>) -> Option<Ordering> {
28 self.contents.partial_cmp(&other.contents)
29 }
30}
31
32impl<T: Ord> Ord for Located<T> {
33 fn cmp(&self, other: &Located<T>) -> Ordering {
34 self.contents.cmp(&other.contents)
35 }
36}
37
38impl<T: Default> Default for Located<T> {
39 fn default() -> Self {
40 Self { contents: Default::default(), location: Location::dummy() }
41 }
42}
43
44impl<T: Hash> Hash for Located<T> {
48 fn hash<H: Hasher>(&self, state: &mut H) {
49 self.contents.hash(state);
50 }
51}
52
53impl<T> Located<T> {
54 pub fn from(location: Location, contents: T) -> Located<T> {
55 Located { location, contents }
56 }
57
58 pub fn span(&self) -> Span {
59 self.location.span
60 }
61
62 pub fn location(&self) -> Location {
63 self.location
64 }
65}
66
67#[derive(PartialOrd, Eq, Ord, Debug, Clone, Default)]
68pub struct Spanned<T> {
69 pub contents: T,
70 span: Span,
71}
72
73impl<T: PartialEq> PartialEq<Spanned<T>> for Spanned<T> {
76 fn eq(&self, other: &Spanned<T>) -> bool {
77 self.contents == other.contents
78 }
79}
80
81impl<T: Hash> Hash for Spanned<T> {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 self.contents.hash(state);
87 }
88}
89
90impl<T> Spanned<T> {
91 pub fn from_position(start: Position, end: Position, contents: T) -> Spanned<T> {
92 Spanned { span: Span::inclusive(start, end), contents }
93 }
94
95 pub fn from(t_span: Span, contents: T) -> Spanned<T> {
96 Spanned { span: t_span, contents }
97 }
98
99 pub fn span(&self) -> Span {
100 self.span
101 }
102}
103
104impl<T> std::borrow::Borrow<T> for Spanned<T> {
105 fn borrow(&self) -> &T {
106 &self.contents
107 }
108}
109
110#[derive(
111 PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone, Default, Deserialize, Serialize,
112)]
113pub struct Span(ByteSpan);
114
115impl Span {
116 pub fn inclusive(start: u32, end: u32) -> Span {
117 Span(ByteSpan::from(start..end + 1))
118 }
119
120 pub fn single_char(start: u32) -> Span {
121 Span::inclusive(start, start)
122 }
123
124 pub fn empty(position: u32) -> Span {
125 Span::from(position..position)
126 }
127
128 #[must_use]
129 pub fn merge(self, other: Span) -> Span {
130 Span(self.0.merge(other.0))
131 }
132
133 pub fn to_byte_span(self) -> ByteSpan {
134 self.0
135 }
136
137 pub fn start(&self) -> u32 {
138 self.0.start().into()
139 }
140
141 pub fn end(&self) -> u32 {
142 self.0.end().into()
143 }
144
145 pub fn contains(&self, other: &Span) -> bool {
146 self.start() <= other.start() && self.end() >= other.end()
147 }
148
149 pub fn intersects(&self, other: &Span) -> bool {
152 self.end() >= other.start() && self.start() <= other.end()
153 }
154
155 pub fn is_smaller(&self, other: &Span) -> bool {
156 let self_distance = self.end() - self.start();
157 let other_distance = other.end() - other.start();
158 self_distance < other_distance
159 }
160}
161
162impl From<Span> for Range<usize> {
163 fn from(span: Span) -> Self {
164 span.0.into()
165 }
166}
167
168impl From<Range<u32>> for Span {
169 fn from(Range { start, end }: Range<u32>) -> Self {
170 Self(ByteSpan::new(start, end))
171 }
172}
173
174#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
175pub struct Location {
176 pub span: Span,
177 pub file: FileId,
178}
179
180impl Location {
181 pub fn new(span: Span, file: FileId) -> Self {
182 Self { span, file }
183 }
184
185 pub fn dummy() -> Self {
186 Self { span: Span::default(), file: FileId::dummy() }
187 }
188
189 pub fn contains(&self, other: &Location) -> bool {
190 self.file == other.file && self.span.contains(&other.span)
191 }
192
193 #[must_use]
194 pub fn merge(self, other: Location) -> Location {
195 if self.file == other.file {
196 Location::new(self.span.merge(other.span), self.file)
197 } else {
198 self
199 }
200 }
201}
202
203impl Ord for Location {
204 fn cmp(&self, other: &Self) -> Ordering {
205 (self.file, self.span).cmp(&(other.file, other.span))
206 }
207}
208
209impl PartialOrd for Location {
210 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
211 Some(self.cmp(other))
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use crate::Span;
218
219 #[test]
220 fn test_intersects() {
221 assert!(Span::from(5..10).intersects(&Span::from(5..10)));
222
223 assert!(Span::from(5..10).intersects(&Span::from(5..5)));
224 assert!(Span::from(5..5).intersects(&Span::from(5..10)));
225
226 assert!(Span::from(10..10).intersects(&Span::from(5..10)));
227 assert!(Span::from(5..10).intersects(&Span::from(10..10)));
228
229 assert!(Span::from(5..10).intersects(&Span::from(6..9)));
230 assert!(Span::from(6..9).intersects(&Span::from(5..10)));
231
232 assert!(Span::from(5..10).intersects(&Span::from(4..11)));
233 assert!(Span::from(4..11).intersects(&Span::from(5..10)));
234
235 assert!(Span::from(5..10).intersects(&Span::from(4..6)));
236 assert!(Span::from(4..6).intersects(&Span::from(5..10)));
237
238 assert!(Span::from(5..10).intersects(&Span::from(9..11)));
239 assert!(Span::from(9..11).intersects(&Span::from(5..10)));
240
241 assert!(!Span::from(5..10).intersects(&Span::from(3..4)));
242 assert!(!Span::from(3..4).intersects(&Span::from(5..10)));
243
244 assert!(!Span::from(5..10).intersects(&Span::from(11..12)));
245 assert!(!Span::from(11..12).intersects(&Span::from(5..10)));
246 }
247}