1pub use crate::span::Span;
4#[cfg(feature = "deserialize")]
5use granit_parser::Span as ParserSpan;
6use serde_core::de::{self, IgnoredAny, MapAccess, Visitor};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
45pub struct Location {
46 pub(crate) line: u32,
48 pub(crate) column: u32,
50 pub(crate) span: Span,
53 pub(crate) source_id: u32,
55}
56
57impl<'de> serde_core::Deserialize<'de> for Location {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: serde_core::Deserializer<'de>,
62 {
63 enum Field {
64 Line,
65 Column,
66 Span,
67 SourceId,
68 Ignore,
69 }
70
71 impl<'de> serde_core::Deserialize<'de> for Field {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde_core::Deserializer<'de>,
75 {
76 struct FieldVisitor;
77
78 impl Visitor<'_> for FieldVisitor {
79 type Value = Field;
80
81 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82 f.write_str("a Location field")
83 }
84
85 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
86 where
87 E: de::Error,
88 {
89 Ok(match value {
90 "line" => Field::Line,
91 "column" => Field::Column,
92 "span" => Field::Span,
93 "source_id" => Field::SourceId,
94 _ => Field::Ignore,
95 })
96 }
97 }
98
99 deserializer.deserialize_identifier(FieldVisitor)
100 }
101 }
102
103 struct LocationVisitor;
104
105 impl<'de> Visitor<'de> for LocationVisitor {
106 type Value = Location;
107
108 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
109 f.write_str("a source location")
110 }
111
112 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
113 where
114 A: MapAccess<'de>,
115 {
116 let mut line = None;
117 let mut column = None;
118 let mut span = None;
119 let mut source_id = None;
120
121 while let Some(field) = map.next_key::<Field>()? {
122 match field {
123 Field::Line => {
124 if line.is_some() {
125 return Err(de::Error::duplicate_field("line"));
126 }
127 line = Some(map.next_value()?);
128 }
129 Field::Column => {
130 if column.is_some() {
131 return Err(de::Error::duplicate_field("column"));
132 }
133 column = Some(map.next_value()?);
134 }
135 Field::Span => {
136 if span.is_some() {
137 return Err(de::Error::duplicate_field("span"));
138 }
139 span = Some(map.next_value()?);
140 }
141 Field::SourceId => {
142 if source_id.is_some() {
143 return Err(de::Error::duplicate_field("source_id"));
144 }
145 source_id = Some(map.next_value()?);
146 }
147 Field::Ignore => {
148 let _ = map.next_value::<IgnoredAny>()?;
149 }
150 }
151 }
152
153 let line = line.ok_or_else(|| de::Error::missing_field("line"))?;
154 let column = column.ok_or_else(|| de::Error::missing_field("column"))?;
155 let span = span.unwrap_or_default();
156 let source_id = source_id.unwrap_or_default();
157
158 Ok(Location {
159 line,
160 column,
161 span,
162 source_id,
163 })
164 }
165 }
166
167 const FIELDS: &[&str] = &["line", "column", "span", "source_id"];
168 deserializer.deserialize_struct("Location", FIELDS, LocationVisitor)
169 }
170}
171
172impl Location {
173 #[inline]
175 #[must_use]
176 pub fn line(&self) -> u64 {
177 u64::from(self.line)
178 }
179
180 #[inline]
182 #[must_use]
183 pub fn column(&self) -> u64 {
184 u64::from(self.column)
185 }
186
187 #[inline]
190 #[must_use]
191 pub fn span(&self) -> Span {
192 self.span
193 }
194
195 #[inline]
197 #[must_use]
198 pub fn source_id(&self) -> u32 {
199 self.source_id
200 }
201}
202
203impl Location {
204 pub const UNKNOWN: Self = Self {
208 line: 0,
209 column: 0,
210 span: Span::UNKNOWN,
211 source_id: 0,
212 };
213
214 #[cfg(feature = "deserialize")]
220 pub(crate) const fn new(line: usize, column: usize) -> Self {
221 Self {
222 line: if line > u32::MAX as usize {
223 u32::MAX
224 } else {
225 line as u32
226 },
227 column: if column > u32::MAX as usize {
228 u32::MAX
229 } else {
230 column as u32
231 },
232 span: Span::UNKNOWN,
233 source_id: 0,
234 }
235 }
236
237 #[cfg(feature = "deserialize")]
238 pub(crate) const fn with_span(mut self, span: Span) -> Self {
239 self.span = span;
240 self
241 }
242
243 #[cfg(feature = "deserialize")]
244 pub(crate) const fn with_source_id(mut self, source_id: u32) -> Self {
245 self.source_id = source_id;
246 self
247 }
248}
249
250#[cfg(feature = "deserialize")]
258pub(crate) fn location_from_span(span: &ParserSpan) -> Location {
259 let start = &span.start;
260 let end = &span.end;
261
262 let mut out = Span::new(start.index() as u64, span.len() as u64);
263
264 if let (Some(start_byte), Some(end_byte)) = (start.byte_offset(), end.byte_offset()) {
265 let byte_len = end_byte.saturating_sub(start_byte);
266
267 #[cfg(not(feature = "huge_documents"))]
268 {
269 if start_byte <= (u32::MAX as usize) && byte_len <= (u32::MAX as usize) {
271 out = out.with_byte_info(start_byte as u64, byte_len as u64);
272 }
273 }
274
275 #[cfg(feature = "huge_documents")]
276 {
277 out = out.with_byte_info(start_byte as u64, byte_len as u64);
278 }
279 }
280
281 Location::new(start.line(), start.col() + 1).with_span(out)
282}
283
284#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
289pub struct Locations {
290 pub reference_location: Location,
291 pub defined_location: Location,
292}
293
294impl Locations {
295 #[cfg_attr(not(any(feature = "garde", feature = "validator")), allow(dead_code))]
296 pub(crate) const UNKNOWN: Locations = Locations {
297 reference_location: Location::UNKNOWN,
298 defined_location: Location::UNKNOWN,
299 };
300
301 #[inline]
302 #[cfg(feature = "deserialize")]
303 pub(crate) fn same(location: &Location) -> Option<Locations> {
304 if location == &Location::UNKNOWN {
305 None
306 } else {
307 Some(Locations {
308 reference_location: *location,
309 defined_location: *location,
310 })
311 }
312 }
313
314 #[inline]
315 #[must_use]
316 pub fn primary_location(self) -> Option<Location> {
317 if self.reference_location != Location::UNKNOWN {
318 Some(self.reference_location)
319 } else if self.defined_location != Location::UNKNOWN {
320 Some(self.defined_location)
321 } else {
322 None
323 }
324 }
325}
326
327#[cfg(all(test, feature = "deserialize"))]
328mod tests {
329 use super::*;
330 use granit_parser::{Event, Parser};
331
332 #[test]
333 fn test_location_from_span() {
334 let input = "foo";
335 let mut parser = Parser::new_from_str(input);
336
337 parser.next().unwrap().unwrap();
339 parser.next().unwrap().unwrap();
341
342 let (event, parser_span) = parser.next().unwrap().unwrap();
344 assert!(matches!(event, Event::Scalar(..)));
345
346 let loc = location_from_span(&parser_span);
347
348 assert_eq!(loc.line(), 1);
350 assert_eq!(loc.column(), 1);
351
352 let span = loc.span();
353 assert_eq!(span.offset(), 0);
354 assert_eq!(span.len(), 3);
355 assert!(!span.is_empty());
356
357 assert_eq!(span.byte_offset(), Some(0));
359 assert_eq!(span.byte_len(), Some(3));
360 }
361
362 #[test]
363 fn test_location_from_span_offset() {
364 let input = " bar";
365 let mut parser = Parser::new_from_str(input);
366
367 parser.next().unwrap().unwrap();
368 parser.next().unwrap().unwrap();
369
370 let (event, parser_span) = parser.next().unwrap().unwrap();
371 assert!(matches!(event, Event::Scalar(..)));
372
373 let loc = location_from_span(&parser_span);
374
375 assert_eq!(loc.line(), 1);
381 assert_eq!(loc.column(), 3);
382
383 let span = loc.span();
384 assert_eq!(span.offset(), 2);
385 assert_eq!(span.len(), 3);
386 }
387
388 #[test]
389 fn test_span_methods() {
390 let span = Span::new(10, 5).with_byte_info(20, 5);
391
392 assert_eq!(span.offset(), 10);
393 assert_eq!(span.len(), 5);
394 assert!(!span.is_empty());
395 assert_eq!(span.byte_offset(), Some(20));
396 assert_eq!(span.byte_len(), Some(5));
397
398 let empty_span = Span::new(10, 0).with_byte_info(20, 0);
399 assert!(empty_span.is_empty());
400 }
401
402 #[test]
403 fn test_location_methods() {
404 let loc = Location::new(5, 10);
405 assert_eq!(loc.line(), 5);
406 assert_eq!(loc.column(), 10);
407 assert_eq!(loc.span(), Span::UNKNOWN);
408
409 let span = Span::new(100, 20).with_byte_info(100, 20);
410 let loc_with_span = loc.with_span(span);
411
412 assert_eq!(loc_with_span.line(), 5);
413 assert_eq!(loc_with_span.column(), 10);
414 assert_eq!(loc_with_span.span(), span);
415 }
416
417 #[test]
418 fn location_new_saturates_line_and_column() {
419 let loc = Location::new(usize::MAX, usize::MAX);
420
421 assert_eq!(loc.line(), u64::from(u32::MAX));
422 assert_eq!(loc.column(), u64::from(u32::MAX));
423 }
424
425 #[test]
426 fn deserialize_location_accepts_optional_and_unknown_fields() {
427 let loc: Location = serde_json::from_str(
428 r#"{
429 "ignored": true,
430 "line": 5,
431 "column": 10,
432 "span": { "offset": 20, "len": 3, "byte_info": [40, 3] },
433 "source_id": 7
434 }"#,
435 )
436 .unwrap();
437
438 assert_eq!(loc.line(), 5);
439 assert_eq!(loc.column(), 10);
440 assert_eq!(loc.span().offset(), 20);
441 assert_eq!(loc.span().len(), 3);
442 assert_eq!(loc.span().byte_offset(), Some(40));
443 assert_eq!(loc.span().byte_len(), Some(3));
444 assert_eq!(loc.source_id(), 7);
445 }
446
447 #[test]
448 fn deserialize_location_defaults_span_and_source_id() {
449 let loc: Location = serde_json::from_str(r#"{ "line": 1, "column": 2 }"#).unwrap();
450
451 assert_eq!(loc.line(), 1);
452 assert_eq!(loc.column(), 2);
453 assert_eq!(loc.span(), Span::default());
454 assert_eq!(loc.source_id(), 0);
455 }
456
457 #[test]
458 fn deserialize_location_rejects_duplicate_required_fields() {
459 let err = serde_json::from_str::<Location>(r#"{ "line": 1, "line": 2, "column": 3 }"#)
460 .unwrap_err();
461
462 assert!(err.to_string().contains("duplicate field `line`"));
463 }
464
465 #[test]
466 fn deserialize_location_rejects_duplicate_defaulted_fields() {
467 let duplicate_span = serde_json::from_str::<Location>(
468 r#"{
469 "line": 1,
470 "column": 2,
471 "span": { "offset": 1, "len": 1, "byte_info": [0, 0] },
472 "span": { "offset": 2, "len": 2, "byte_info": [0, 0] }
473 }"#,
474 )
475 .unwrap_err();
476 assert!(
477 duplicate_span
478 .to_string()
479 .contains("duplicate field `span`")
480 );
481
482 let duplicate_source_id = serde_json::from_str::<Location>(
483 r#"{ "line": 1, "column": 2, "source_id": 1, "source_id": 2 }"#,
484 )
485 .unwrap_err();
486 assert!(
487 duplicate_source_id
488 .to_string()
489 .contains("duplicate field `source_id`")
490 );
491 }
492
493 #[test]
494 fn deserialize_location_rejects_missing_required_fields() {
495 let err = serde_json::from_str::<Location>(r#"{ "line": 1 }"#).unwrap_err();
496
497 assert!(err.to_string().contains("missing field `column`"));
498 }
499
500 #[test]
501 fn test_locations_methods() {
502 let l1 = Location::new(1, 1);
503 let locations = Locations::same(&l1).unwrap();
504 assert_eq!(locations.reference_location, l1);
505 assert_eq!(locations.defined_location, l1);
506
507 assert_eq!(locations.primary_location(), Some(l1));
508
509 let l2 = Location::new(2, 2);
510 let locations_diff = Locations {
511 reference_location: l1,
512 defined_location: l2,
513 };
514 assert_eq!(locations_diff.primary_location(), Some(l1));
515
516 let locations_unknown = Locations {
517 reference_location: Location::UNKNOWN,
518 defined_location: l2,
519 };
520 assert_eq!(locations_unknown.primary_location(), Some(l2));
521
522 assert!(Locations::same(&Location::UNKNOWN).is_none());
523 }
524}