miden_diagnostics/
source.rs1use std::convert::Into;
2use std::num::NonZeroU32;
3use std::ops::Range;
4
5use super::*;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct SourceId(pub(crate) NonZeroU32);
10impl SourceId {
11 pub(crate) const UNKNOWN_SOURCE_ID: u32 = u32::max_value();
12
13 pub const UNKNOWN: Self = Self(unsafe { NonZeroU32::new_unchecked(Self::UNKNOWN_SOURCE_ID) });
14
15 pub(crate) fn new(index: u32) -> Self {
16 assert!(index > 0);
17 assert!(index < Self::UNKNOWN_SOURCE_ID);
18 Self(NonZeroU32::new(index).unwrap())
19 }
20
21 #[inline]
22 pub(crate) fn get(self) -> u32 {
23 self.0.get()
24 }
25}
26
27#[derive(Debug, Clone)]
29pub struct SourceFile {
30 id: SourceId,
31 name: FileName,
32 source: String,
33 line_starts: Vec<ByteIndex>,
34 parent: Option<SourceSpan>,
35}
36impl SourceFile {
37 pub(crate) fn new(
38 id: SourceId,
39 name: FileName,
40 source: String,
41 parent: Option<SourceSpan>,
42 ) -> Self {
43 let line_starts = codespan_reporting::files::line_starts(source.as_str())
44 .map(|i| ByteIndex::from(i as u32))
45 .collect();
46
47 Self {
48 id,
49 name,
50 source,
51 line_starts,
52 parent,
53 }
54 }
55
56 pub fn name(&self) -> &FileName {
58 &self.name
59 }
60
61 pub fn id(&self) -> SourceId {
63 self.id
64 }
65
66 pub fn parent(&self) -> Option<SourceSpan> {
68 self.parent
69 }
70
71 pub fn line_start(&self, line_index: LineIndex) -> Result<ByteIndex, Error> {
73 use std::cmp::Ordering;
74
75 match line_index.cmp(&self.last_line_index()) {
76 Ordering::Less => Ok(self.line_starts[line_index.to_usize()]),
77 Ordering::Equal => Ok(self.source_span().end_index()),
78 Ordering::Greater => Err(Error::LineTooLarge {
79 given: line_index.to_usize(),
80 max: self.last_line_index().to_usize(),
81 }),
82 }
83 }
84
85 pub fn last_line_index(&self) -> LineIndex {
87 LineIndex::from(self.line_starts.len() as RawIndex)
88 }
89
90 pub(crate) fn line_span(&self, line_index: LineIndex) -> Result<codespan::Span, Error> {
91 let line_start = self.line_start(line_index)?;
92 let next_line_start = self.line_start(line_index + LineOffset::from(1))?;
93
94 Ok(codespan::Span::new(line_start, next_line_start))
95 }
96
97 pub(crate) fn line_index(&self, byte_index: ByteIndex) -> LineIndex {
98 match self.line_starts.binary_search(&byte_index) {
99 Ok(line) => LineIndex::from(line as u32),
101 Err(next_line) => LineIndex::from(next_line as u32 - 1),
102 }
103 }
104
105 pub(crate) fn line_column_to_span(
106 &self,
107 line_index: LineIndex,
108 column_index: ColumnIndex,
109 ) -> Result<codespan::Span, Error> {
110 let column_index = column_index.to_usize();
111 let line_span = self.line_span(line_index)?;
112 let line_src = self
113 .source
114 .as_str()
115 .get(line_span.start().to_usize()..line_span.end().to_usize())
116 .unwrap();
117 if line_src.len() < column_index {
118 let base = line_span.start().to_usize();
119 return Err(Error::IndexTooLarge {
120 given: base + column_index,
121 max: base + line_src.len(),
122 });
123 }
124 let (pre, _) = line_src.split_at(column_index);
125 let start = line_span.start();
126 let offset = ByteOffset::from_str_len(pre);
127 Ok(codespan::Span::new(start + offset, start + offset))
128 }
129
130 pub fn location<I: Into<ByteIndex>>(&self, byte_index: I) -> Result<Location, Error> {
132 let byte_index = byte_index.into();
133 let line_index = self.line_index(byte_index);
134 let line_start_index = self
135 .line_start(line_index)
136 .map_err(|_| Error::IndexTooLarge {
137 given: byte_index.to_usize(),
138 max: self.source().len() - 1,
139 })?;
140 let line_src = self
141 .source
142 .as_str()
143 .get(line_start_index.to_usize()..byte_index.to_usize())
144 .ok_or_else(|| {
145 let given = byte_index.to_usize();
146 let max = self.source().len() - 1;
147 if given >= max {
148 Error::IndexTooLarge { given, max }
149 } else {
150 Error::InvalidCharBoundary { given }
151 }
152 })?;
153
154 Ok(Location {
155 line: line_index,
156 column: ColumnIndex::from(line_src.chars().count() as u32),
157 })
158 }
159
160 #[inline(always)]
162 pub fn source(&self) -> &str {
163 self.source.as_str()
164 }
165
166 pub fn source_span(&self) -> SourceSpan {
168 SourceSpan {
169 source_id: self.id,
170 start: ByteIndex(0),
171 end: ByteIndex(self.source.len() as u32),
172 }
173 }
174
175 pub fn source_slice(&self, span: impl Into<Range<usize>>) -> Result<&str, Error> {
179 let span = span.into();
180 let start = span.start;
181 let end = span.end;
182
183 self.source().get(start..end).ok_or_else(|| {
184 let max = self.source().len() - 1;
185 Error::IndexTooLarge {
186 given: if start > max { start } else { end },
187 max,
188 }
189 })
190 }
191}