Skip to main content

libgraphql_parser/
byte_span.rs

1use crate::{SourceMap, SourceSpan};
2
3/// A compact source span representing a half-open byte range `[start, end)`.
4///
5/// This is the primary span type stored on all tokens, AST nodes, and parse
6/// errors. It is 8 bytes, `Copy`, and `#[repr(C)]` for predictable layout.
7///
8/// `ByteSpan` does **not** carry line/column/file information — those are
9/// resolved on demand via [`SourceMap::resolve_span()`](crate::SourceMap::resolve_span).
10///
11/// # Indexing Convention
12///
13/// Both `start` and `end` are 0-based byte offsets from the beginning of the
14/// source text. The range is half-open: `start` is the byte offset of the
15/// first character, and `end` is the byte offset immediately after the last
16/// character.
17#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
18#[repr(C)]
19pub struct ByteSpan {
20    /// Byte offset of the first character (inclusive).
21    pub start: u32,
22
23    /// Byte offset immediately after the last character (exclusive).
24    pub end: u32,
25}
26
27impl ByteSpan {
28    /// Creates a new `ByteSpan` from start (inclusive) and end (exclusive)
29    /// byte offsets.
30    #[inline]
31    pub fn new(start: u32, end: u32) -> Self {
32        debug_assert!(
33            start <= end,
34            "ByteSpan::new called with start ({start}) > end ({end})",
35        );
36        Self { start, end }
37    }
38
39    /// Returns the length of this span in bytes.
40    #[inline]
41    pub fn len(&self) -> u32 {
42        self.end - self.start
43    }
44
45    /// Returns `true` if the span is empty (zero length).
46    #[inline]
47    pub fn is_empty(&self) -> bool {
48        self.start == self.end
49    }
50
51    /// Creates an empty span at the given byte offset.
52    ///
53    /// Useful for representing zero-width positions (e.g., EOF).
54    #[inline]
55    pub fn empty_at(offset: u32) -> Self {
56        Self {
57            start: offset,
58            end: offset,
59        }
60    }
61
62    /// Creates a span that covers both `self` and `other`.
63    ///
64    /// The resulting span starts at the minimum start and ends at the maximum
65    /// end of the two spans.
66    #[inline]
67    pub fn merge(self, other: ByteSpan) -> ByteSpan {
68        ByteSpan {
69            start: self.start.min(other.start),
70            end: self.end.max(other.end),
71        }
72    }
73
74    /// Resolves this byte span to a [`SourceSpan`] with
75    /// line/column positions using the given [`SourceMap`].
76    ///
77    /// Returns [`None`] if the byte offsets cannot be resolved.
78    /// Convenience wrapper for
79    /// [`SourceMap::resolve_span()`].
80    #[inline]
81    pub fn resolve(&self, source_map: &SourceMap) -> Option<SourceSpan> {
82        source_map.resolve_span(*self)
83    }
84}