1use std::fmt::{self, Display, Formatter};
3use std::ops::Range;
4
5#[derive(Debug)]
6pub(super) struct Source<'s> {
7 body: &'s str,
8}
9
10impl<'s> Source<'s> {
11 pub(super) fn new(body: &'s str) -> Source<'s> {
12 Source { body }
13 }
14
15 pub(super) fn as_str(&self) -> &str {
16 self.body
17 }
18
19 pub(super) fn extract(&self, range: Range<usize>) -> &str {
20 &self.body[range]
21 }
22
23 pub(super) fn location_of(&self, pos: usize) -> LineAndColumn {
24 const START_COL: u32 = 1;
25 const START_LINE: u32 = 1;
26
27 let mut line = START_LINE;
28 let mut column = START_COL;
29 for (i, ch) in self.body.char_indices() {
30 if i == pos {
31 break;
32 }
33 match ch {
34 '\n' => {
35 column = START_COL;
36 line += 1;
37 }
38 _ => {
39 column += 1;
40 }
41 }
42 }
43 LineAndColumn { line, column }
44 }
45
46 pub(crate) fn extract_prefix(&self, pos: usize) -> &'s str {
47 let body_prefix = &self.body[0..pos];
48 let line_start = match body_prefix.rfind('\n') {
49 None => 0,
50 Some(n) => n + 1,
51 };
52 let prefix = &self.body[line_start..pos];
53 if prefix.chars().all(char::is_whitespace) {
54 prefix
55 } else {
56 ""
57 }
58 }
59}
60
61#[test]
62fn test_extract_prefix() {
63 fn extract_prefix(s: &str, pos: usize) -> &str {
64 let src = Source::new(s);
65 src.extract_prefix(pos)
66 }
67
68 assert_eq!(extract_prefix("hello", 0), "");
69 assert_eq!(extract_prefix(" hello", 0), "");
70 assert_eq!(extract_prefix(" hello", 1), " ");
71 assert_eq!(extract_prefix("x\nhello", 2), "");
72 assert_eq!(extract_prefix("x\n hello", 3), " ");
73 assert_eq!(extract_prefix("x\nY hello", 4), "");
74}
75
76#[derive(Debug, PartialEq, Eq, Hash, Clone)]
77pub struct LineAndColumn {
78 line: u32,
79 column: u32,
80}
81
82impl Display for LineAndColumn {
83 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84 write!(f, "line {}, column {}", self.line, self.column)
85 }
86}
87
88pub trait Located {
89 fn location(&self) -> LineAndColumn;
90}
91
92#[derive(Debug, PartialEq, Eq, Hash)]
93pub struct WithLocation<T> {
94 pub(crate) inner: T,
95 pub(crate) location: LineAndColumn,
96}
97
98impl<T> Located for WithLocation<T> {
99 fn location(&self) -> LineAndColumn {
100 self.location.clone()
101 }
102}
103
104impl<T: Located> From<T> for WithLocation<T> {
105 fn from(inner: T) -> WithLocation<T> {
106 WithLocation {
107 location: inner.location().clone(),
108 inner,
109 }
110 }
111}
112
113impl<T> WithLocation<T> {
114 pub fn location(&self) -> &LineAndColumn {
115 &self.location
116 }
117}
118
119impl<T: Display> Display for WithLocation<T> {
120 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121 write!(f, "{}: {}", &self.location, &self.inner)
122 }
123}