#[cfg(feature = "deserialize")]
use saphyr_parser::Span as ParserSpan;
use serde::Deserialize;
#[cfg(not(feature = "huge_documents"))]
pub(crate) type SpanIndex = u32;
#[cfg(feature = "huge_documents")]
pub(crate) type SpanIndex = u64;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Default)]
pub struct Span {
pub(crate) offset: SpanIndex,
pub(crate) len: SpanIndex,
pub(crate) byte_info: (SpanIndex, SpanIndex),
}
impl Span {
pub const UNKNOWN: Self = Self {
offset: 0,
len: 0,
byte_info: (0, 0),
};
#[inline]
pub fn offset(&self) -> u64 {
#[cfg(not(feature = "huge_documents"))]
{
self.offset as u64
}
#[cfg(feature = "huge_documents")]
{
self.offset
}
}
#[inline]
pub fn len(&self) -> u64 {
#[cfg(not(feature = "huge_documents"))]
{
self.len as u64
}
#[cfg(feature = "huge_documents")]
{
self.len
}
}
#[inline]
pub fn byte_offset(&self) -> Option<u64> {
if self.byte_info == (0, 0) {
None
} else {
#[cfg(not(feature = "huge_documents"))]
{
Some(self.byte_info.0 as u64)
}
#[cfg(feature = "huge_documents")]
{
Some(self.byte_info.0)
}
}
}
#[inline]
pub fn byte_len(&self) -> Option<u64> {
if self.byte_info == (0, 0) {
None
} else {
#[cfg(not(feature = "huge_documents"))]
{
Some(self.byte_info.1 as u64)
}
#[cfg(feature = "huge_documents")]
{
Some(self.byte_info.1)
}
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[cfg(feature = "deserialize")]
#[inline]
pub(crate) fn raw_offset(&self) -> SpanIndex {
self.offset
}
#[cfg(feature = "deserialize")]
#[inline]
pub(crate) fn raw_len(&self) -> SpanIndex {
self.len
}
#[cfg(feature = "deserialize")]
#[inline]
pub(crate) fn raw_byte_info(&self) -> (SpanIndex, SpanIndex) {
self.byte_info
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
pub struct Location {
pub(crate) line: u32,
pub(crate) column: u32,
#[serde(default)]
pub(crate) span: Span,
#[serde(default)]
pub(crate) source_id: u32,
}
impl Location {
#[inline]
pub fn line(&self) -> u64 {
self.line as u64
}
#[inline]
pub fn column(&self) -> u64 {
self.column as u64
}
#[inline]
pub fn span(&self) -> Span {
self.span
}
#[inline]
pub fn source_id(&self) -> u32 {
self.source_id
}
}
impl Location {
pub const UNKNOWN: Self = Self {
line: 0,
column: 0,
span: Span::UNKNOWN,
source_id: 0,
};
#[cfg(feature = "deserialize")]
pub(crate) const fn new(line: usize, column: usize) -> Self {
Self {
line: line as u32,
column: column as u32,
span: Span::UNKNOWN,
source_id: 0,
}
}
#[cfg(feature = "deserialize")]
pub(crate) const fn with_span(mut self, span: Span) -> Self {
self.span = span;
self
}
#[cfg(feature = "deserialize")]
pub(crate) const fn with_source_id(mut self, source_id: u32) -> Self {
self.source_id = source_id;
self
}
}
#[cfg(feature = "deserialize")]
pub(crate) fn location_from_span(span: &ParserSpan) -> Location {
let start = &span.start;
let end = &span.end;
let byte_info =
if let (Some(start_byte), Some(end_byte)) = (start.byte_offset(), end.byte_offset()) {
#[cfg(not(feature = "huge_documents"))]
{
let len = end_byte.saturating_sub(start_byte);
if start_byte > (u32::MAX as usize) || len > (u32::MAX as usize) {
(0, 0)
} else {
(start_byte as SpanIndex, len as SpanIndex)
}
}
#[cfg(feature = "huge_documents")]
{
(
start_byte as SpanIndex,
(end_byte - start_byte) as SpanIndex,
)
}
} else {
(0, 0)
};
Location::new(start.line(), start.col() + 1).with_span(Span {
offset: start.index() as SpanIndex,
len: span.len() as SpanIndex,
byte_info,
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Locations {
pub reference_location: Location,
pub defined_location: Location,
}
impl Locations {
#[cfg_attr(not(any(feature = "garde", feature = "validator")), allow(dead_code))]
pub(crate) const UNKNOWN: Locations = Locations {
reference_location: Location::UNKNOWN,
defined_location: Location::UNKNOWN,
};
#[inline]
#[cfg(feature = "deserialize")]
pub(crate) fn same(location: &Location) -> Option<Locations> {
if location == &Location::UNKNOWN {
None
} else {
Some(Locations {
reference_location: *location,
defined_location: *location,
})
}
}
#[inline]
pub fn primary_location(self) -> Option<Location> {
if self.reference_location != Location::UNKNOWN {
Some(self.reference_location)
} else if self.defined_location != Location::UNKNOWN {
Some(self.defined_location)
} else {
None
}
}
}
#[cfg(all(test, feature = "deserialize"))]
mod tests {
use super::*;
use saphyr_parser::{Event, Parser};
#[test]
fn test_location_from_span() {
let input = "foo";
let mut parser = Parser::new_from_str(input);
parser.next().unwrap().unwrap();
parser.next().unwrap().unwrap();
let (event, parser_span) = parser.next().unwrap().unwrap();
assert!(matches!(event, Event::Scalar(..)));
let loc = location_from_span(&parser_span);
assert_eq!(loc.line(), 1);
assert_eq!(loc.column(), 1);
let span = loc.span();
assert_eq!(span.offset(), 0);
assert_eq!(span.len(), 3);
assert!(!span.is_empty());
assert_eq!(span.byte_offset(), Some(0));
assert_eq!(span.byte_len(), Some(3));
}
#[test]
fn test_location_from_span_offset() {
let input = " bar";
let mut parser = Parser::new_from_str(input);
parser.next().unwrap().unwrap();
parser.next().unwrap().unwrap();
let (event, parser_span) = parser.next().unwrap().unwrap();
assert!(matches!(event, Event::Scalar(..)));
let loc = location_from_span(&parser_span);
assert_eq!(loc.line(), 1);
assert_eq!(loc.column(), 3);
let span = loc.span();
assert_eq!(span.offset(), 2);
assert_eq!(span.len(), 3);
}
#[test]
fn test_span_methods() {
let span = Span {
offset: 10,
len: 5,
byte_info: (20, 5),
};
assert_eq!(span.offset(), 10);
assert_eq!(span.len(), 5);
assert!(!span.is_empty());
assert_eq!(span.byte_offset(), Some(20));
assert_eq!(span.byte_len(), Some(5));
let empty_span = Span {
offset: 10,
len: 0,
byte_info: (20, 0),
};
assert!(empty_span.is_empty());
}
#[test]
fn test_location_methods() {
let loc = Location::new(5, 10);
assert_eq!(loc.line(), 5);
assert_eq!(loc.column(), 10);
assert!(loc.span().is_empty());
let span = Span {
offset: 100,
len: 20,
byte_info: (100, 20),
};
let loc_with_span = loc.with_span(span);
assert_eq!(loc_with_span.line(), 5);
assert_eq!(loc_with_span.column(), 10);
}
#[test]
fn test_locations_methods() {
let l1 = Location::new(1, 1);
let locations = Locations::same(&l1).unwrap();
assert_eq!(locations.reference_location, l1);
assert_eq!(locations.defined_location, l1);
assert_eq!(locations.primary_location(), Some(l1));
let l2 = Location::new(2, 2);
let locations_diff = Locations {
reference_location: l1,
defined_location: l2,
};
assert_eq!(locations_diff.primary_location(), Some(l1));
let locations_unknown = Locations {
reference_location: Location::UNKNOWN,
defined_location: l2,
};
assert_eq!(locations_unknown.primary_location(), Some(l2));
assert!(Locations::same(&Location::UNKNOWN).is_none());
}
}