1mod line_col;
17mod refer;
18mod src_referrer;
19
20pub use line_col::*;
21pub use refer::*;
22pub use src_referrer::*;
23
24use crate::parser::*;
25use derive_more::Deref;
26
27#[derive(Clone, Default, Deref)]
31pub struct SrcRef(pub Option<Box<SrcRefInner>>);
32
33impl SrcRef {
34 pub fn new(
39 range: std::ops::Range<usize>,
40 line: usize,
41 col: usize,
42 source_file_hash: u64,
43 ) -> Self {
44 Self(Some(Box::new(SrcRefInner {
45 range,
46 at: LineCol { line, col },
47 source_file_hash,
48 })))
49 }
50}
51#[derive(Clone, Default)]
53pub struct SrcRefInner {
54 pub range: std::ops::Range<usize>,
56 pub at: LineCol,
58 pub source_file_hash: u64,
60}
61
62impl std::fmt::Display for SrcRef {
63 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64 match &self.0 {
65 Some(s) => write!(f, "{}", s.at),
66 _ => write!(f, crate::invalid_no_ansi!(REF)),
67 }
68 }
69}
70
71impl std::fmt::Debug for SrcRef {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 match &self.0 {
74 Some(s) => write!(
75 f,
76 "{} ({}..{}) in {:#x}",
77 s.at, s.range.start, s.range.end, s.source_file_hash
78 ),
79 _ => write!(f, crate::invalid!(REF)),
80 }
81 }
82}
83
84impl PartialEq for SrcRef {
85 fn eq(&self, _: &Self) -> bool {
86 true
87 }
88}
89
90impl PartialOrd for SrcRef {
91 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
92 Some(self.cmp(other))
93 }
94}
95
96impl Eq for SrcRef {}
97
98impl Ord for SrcRef {
99 fn cmp(&self, _: &Self) -> std::cmp::Ordering {
100 std::cmp::Ordering::Equal
101 }
102}
103
104impl SrcRef {
105 pub fn len(&self) -> usize {
107 self.0.as_ref().map(|s| s.range.len()).unwrap_or(0)
108 }
109
110 #[must_use]
112 pub fn is_empty(&self) -> bool {
113 self.len() == 0
114 }
115
116 pub fn source_hash(&self) -> u64 {
122 self.0.as_ref().map(|s| s.source_file_hash).unwrap_or(0)
123 }
124
125 pub fn source_slice<'a>(&self, src: &'a str) -> &'a str {
127 &src[self.0.as_ref().expect("SrcRef").range.to_owned()]
128 }
129
130 pub fn merge(lhs: &impl SrcReferrer, rhs: &impl SrcReferrer) -> SrcRef {
137 match (lhs.src_ref(), rhs.src_ref()) {
138 (SrcRef(Some(lhs)), SrcRef(Some(rhs))) => {
139 if lhs.source_file_hash == rhs.source_file_hash {
140 let source_file_hash = lhs.source_file_hash;
141
142 if lhs.range.end > rhs.range.start || lhs.range.start > rhs.range.end {
143 log::warn!("ranges not in correct order");
144 SrcRef(None)
145 } else {
146 SrcRef(Some(Box::new(SrcRefInner {
147 range: {
148 assert!(lhs.range.end <= rhs.range.end);
150 assert!(lhs.range.start <= rhs.range.start);
151
152 lhs.range.start..rhs.range.end
153 },
154 at: lhs.at,
155 source_file_hash,
156 })))
157 }
158 } else {
159 log::warn!("references are not in the same file");
160 SrcRef(None)
161 }
162 }
163 (SrcRef(Some(hs)), SrcRef(None)) | (SrcRef(None), SrcRef(Some(hs))) => SrcRef(Some(hs)),
164 _ => SrcRef(None),
165 }
166 }
167
168 pub fn merge_all<S: SrcReferrer>(referrers: impl Iterator<Item = S>) -> SrcRef {
172 let mut result = SrcRef(None);
173 for referrer in referrers {
174 if let Some(src_ref) = referrer.src_ref().0 {
175 if let SrcRef(Some(result)) = &mut result {
176 if result.source_file_hash != src_ref.source_file_hash {
177 panic!("can only merge source references of the same file");
178 }
179 if src_ref.range.start < result.range.start {
180 result.range.start = src_ref.range.start;
181 result.at = src_ref.at;
182 }
183 result.range.end = std::cmp::max(src_ref.range.end, result.range.end);
184 } else {
185 result = SrcRef(Some(src_ref));
186 }
187 }
188 }
189 result
190 }
191
192 pub fn at(&self) -> Option<LineCol> {
194 self.0.as_ref().map(|s| s.at.clone())
195 }
196}
197
198#[test]
199fn merge_all() {
200 use std::ops::Range;
201 assert_eq!(
202 SrcRef::merge_all(
203 [
204 SrcRef::new(Range { start: 5, end: 8 }, 1, 6, 123),
205 SrcRef::new(Range { start: 8, end: 10 }, 2, 1, 123),
206 SrcRef::new(Range { start: 12, end: 16 }, 3, 1, 123),
207 SrcRef::new(Range { start: 0, end: 10 }, 1, 1, 123),
208 ]
209 .iter(),
210 ),
211 SrcRef::new(Range { start: 0, end: 16 }, 1, 1, 123),
212 );
213}
214
215impl From<Pair<'_>> for SrcRef {
216 fn from(pair: Pair) -> Self {
217 let (line, col) = pair.line_col();
218 Self::new(
219 pair.as_span().start()..pair.as_span().end(),
220 line,
221 col,
222 pair.source_hash(),
223 )
224 }
225}
226
227#[test]
228fn test_src_ref() {
229 let input = "geo3d::Cube(size_x = 3.0, size_y = 3.0, size_z = 3.0);";
230
231 let cube = 7..11;
232 let size_y = 26..32;
233
234 let cube = SrcRef::new(cube, 1, 0, 0);
235 let size_y = SrcRef::new(size_y, 1, 0, 0);
236
237 assert_eq!(cube.source_slice(input), "Cube");
238 assert_eq!(size_y.source_slice(input), "size_y");
239}