1
use std::ops::{Add, AddAssign};
2

            
3
pub struct SourceFile<'s> {
4
    source_text: &'s str,
5
}
6

            
7
impl<'s> SourceFile<'s> {
8
8753
    pub fn new(source_text: &'s str) -> SourceFile<'s> {
9
8753
        SourceFile { source_text }
10
8753
    }
11

            
12
12192
    pub fn get_text_snippet(&self, span: impl Into<Span>) -> &'s str {
13
12192
        let span = span.into();
14
12192
        let (start_idx, end_idx) = (span.start.to_usize(), span.end.to_usize());
15
12192
        &self.source_text[start_idx..end_idx]
16
12192
    }
17

            
18
    // TODO(feroldi): Test this.
19
    pub fn get_text(&self) -> &str {
20
        self.source_text
21
    }
22
}
23

            
24
18426
#[derive(Debug, PartialEq, Copy, Clone)]
25
pub struct Spanned<T> {
26
    pub value: T,
27
    pub span: Span,
28
}
29

            
30
impl<T> Spanned<T> {
31
12236
    pub fn new(value: T, span: Span) -> Spanned<T> {
32
12236
        Spanned { value, span }
33
12236
    }
34

            
35
24862
    pub fn with_dummy_span(value: T) -> Spanned<T> {
36
24862
        Spanned {
37
24862
            value,
38
24862
            span: Span::dummy(),
39
24862
        }
40
24862
    }
41
}
42

            
43
impl<T> std::ops::Deref for Spanned<T> {
44
    type Target = T;
45

            
46
12514
    fn deref(&self) -> &Self::Target {
47
12514
        &self.value
48
12514
    }
49
}
50

            
51
impl<T> From<Spanned<T>> for Span {
52
10651
    fn from(spanned: Spanned<T>) -> Span {
53
10651
        spanned.span
54
10651
    }
55
}
56

            
57
/// This is an exclusive text range as in [start, end).
58
7462
#[derive(Debug, PartialEq, Copy, Clone)]
59
pub struct Span {
60
    pub start: BytePos,
61
    pub end: BytePos,
62
}
63

            
64
impl Span {
65
1285
    pub fn from_usizes(start: usize, end: usize) -> Span {
66
1285
        debug_assert!(start <= end, "cannot have start greater than end");
67

            
68
1029
        Span {
69
1029
            start: BytePos::from_usize(start),
70
1029
            end: BytePos::from_usize(end),
71
1029
        }
72
1029
    }
73

            
74
25377
    pub(crate) fn dummy() -> Span {
75
25377
        Span {
76
25377
            start: BytePos::from_usize(0),
77
25377
            end: BytePos::from_usize(0),
78
25377
        }
79
25377
    }
80
}
81

            
82
pub trait Pos: Sized + Add + AddAssign {
83
    fn from_usize(value: usize) -> Self;
84
    fn to_usize(self) -> usize;
85
}
86

            
87
15474
#[derive(Debug, PartialEq, Copy, Clone)]
88
pub struct BytePos(usize);
89

            
90
impl Pos for BytePos {
91
524603
    fn from_usize(value: usize) -> BytePos {
92
524603
        BytePos(value)
93
524603
    }
94

            
95
484046
    fn to_usize(self) -> usize {
96
484046
        self.0
97
484046
    }
98
}
99

            
100
impl Add for BytePos {
101
    type Output = Self;
102

            
103
229575
    fn add(self, rhs: Self) -> Self {
104
229575
        Self::from_usize(self.to_usize() + rhs.to_usize())
105
229575
    }
106
}
107

            
108
impl AddAssign for BytePos {
109
229317
    fn add_assign(&mut self, rhs: Self) {
110
229317
        *self = *self + rhs
111
229317
    }
112
}
113

            
114
impl std::convert::From<usize> for BytePos {
115
3584
    fn from(value: usize) -> BytePos {
116
3584
        BytePos::from_usize(value)
117
3584
    }
118
}
119

            
120
#[allow(dead_code)]
121
7
fn calc_lines_positions(source_text: &str) -> Vec<BytePos> {
122
7
    std::iter::once(0usize)
123
9
        .chain(source_text.match_indices('\n').map(|(idx, _)| idx + 1))
124
16
        .filter(|&pos| pos < source_text.len())
125
7
        .map(BytePos)
126
7
        .collect()
127
7
}
128

            
129
#[allow(dead_code)]
130
14
fn lookup_line_index(lines_pos: &[BytePos], pos: BytePos) -> Option<usize> {
131
14
    lines_pos
132
14
        .iter()
133
14
        .rev()
134
22
        .position(|lines_pos| lines_pos.0 <= pos.0)
135
14
        .map(|line_index| lines_pos.len() - line_index - 1)
136
14
}
137

            
138
#[cfg(test)]
139
mod legacy_tests {
140
    // These tests are here temporarily, because they don't have any use right
141
    // now, so they aren't part of the API right now, but in the future,
142
    // we'll have a diagnostics system that surely will make use of these
143
    // functions, so we keep them here as is for now.
144
    mod calc_lines_positions {
145
        use crate::source_map::{calc_lines_positions, BytePos};
146

            
147
1
        #[test]
148
1
        fn empty_text() {
149
1
            let source_text = "";
150
1
            let source_lines_pos = calc_lines_positions(source_text);
151
1
            assert_eq!(source_lines_pos, vec![]);
152
1
        }
153

            
154
1
        #[test]
155
1
        fn text_without_newline() {
156
1
            let source_text = "some text without newline";
157
1
            let source_lines_pos = calc_lines_positions(source_text);
158
1
            assert_eq!(source_lines_pos, vec![BytePos(0usize)]);
159
1
        }
160

            
161
1
        #[test]
162
1
        fn text_with_newline_at_the_end() {
163
1
            let source_text = "abc\n";
164
1
            let source_lines_pos = calc_lines_positions(source_text);
165
1
            assert_eq!(source_lines_pos, vec![BytePos(0usize)]);
166
1
        }
167

            
168
1
        #[test]
169
1
        fn text_with_newline_in_the_middle() {
170
1
            let source_text = "abc\ndef";
171
1
            let source_lines_pos = calc_lines_positions(source_text);
172
1
            assert_eq!(source_lines_pos, vec![BytePos(0usize), BytePos(4usize)]);
173
1
        }
174

            
175
1
        #[test]
176
1
        fn text_with_newline_at_the_start() {
177
1
            let source_text = "\nabc";
178
1
            let source_lines_pos = calc_lines_positions(source_text);
179
1
            assert_eq!(source_lines_pos, vec![BytePos(0usize), BytePos(1usize)]);
180
1
        }
181

            
182
1
        #[test]
183
1
        fn text_with_various_newlines_at_the_start() {
184
1
            let source_text = "\n\n\nabc";
185
1
            let source_lines_pos = calc_lines_positions(source_text);
186
1
            assert_eq!(
187
1
                source_lines_pos,
188
1
                vec![
189
1
                    BytePos(0usize),
190
1
                    BytePos(1usize),
191
1
                    BytePos(2usize),
192
1
                    BytePos(3usize),
193
1
                ]
194
1
            );
195
1
        }
196

            
197
1
        #[test]
198
1
        fn text_with_various_newlines_at_the_end() {
199
1
            let source_text = "abc\n\n\n";
200
1
            let source_lines_pos = calc_lines_positions(source_text);
201
1
            assert_eq!(
202
1
                source_lines_pos,
203
1
                vec![BytePos(0usize), BytePos(4usize), BytePos(5usize)]
204
1
            );
205
1
        }
206
    }
207

            
208
    mod lookup_line_index {
209
        use crate::source_map::{lookup_line_index, BytePos};
210

            
211
1
        #[test]
212
1
        fn empty_start_pos_of_lines() {
213
1
            let start_pos_of_lines = Vec::<BytePos>::new();
214
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(0));
215
1
            assert_eq!(line_index, None::<usize>);
216
1
        }
217

            
218
1
        #[test]
219
1
        fn one_line() {
220
1
            let start_pos_of_lines = vec![BytePos(0)];
221
1

            
222
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(0));
223
1
            assert_eq!(line_index, Some(0usize));
224

            
225
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(1));
226
1
            assert_eq!(line_index, Some(0usize));
227

            
228
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(100));
229
1
            assert_eq!(line_index, Some(0usize));
230

            
231
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(5000));
232
1
            assert_eq!(line_index, Some(0usize));
233
1
        }
234

            
235
1
        #[test]
236
1
        fn should_return_index_of_the_first_line_whose_pos_is_less_or_equal_to_input_pos() {
237
1
            let start_pos_of_lines = vec![BytePos(0), BytePos(5), BytePos(10)];
238
1

            
239
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(0));
240
1
            assert_eq!(line_index, Some(0usize));
241
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(4));
242
1
            assert_eq!(line_index, Some(0usize));
243

            
244
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(5));
245
1
            assert_eq!(line_index, Some(1usize));
246
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(9));
247
1
            assert_eq!(line_index, Some(1usize));
248

            
249
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(10));
250
1
            assert_eq!(line_index, Some(2usize));
251
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(50));
252
1
            assert_eq!(line_index, Some(2usize));
253
1
        }
254

            
255
1
        #[test]
256
1
        fn contiguous_lines() {
257
1
            let start_pos_of_lines = vec![BytePos(0), BytePos(1), BytePos(2)];
258
1

            
259
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(0));
260
1
            assert_eq!(line_index, Some(0usize));
261

            
262
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(1));
263
1
            assert_eq!(line_index, Some(1usize));
264

            
265
1
            let line_index = lookup_line_index(&start_pos_of_lines, BytePos(2));
266
1
            assert_eq!(line_index, Some(2usize));
267
1
        }
268
    }
269
}