litedoc_core/span.rs
1//! Source location tracking for AST nodes.
2//!
3//! Every AST node includes a `Span` indicating its position in the source text.
4//! This enables precise error reporting and source mapping.
5
6/// A byte range in the source text.
7///
8/// Spans use byte offsets (not character offsets) for efficiency.
9/// Both `start` and `end` are inclusive-exclusive: `[start, end)`.
10///
11/// # Example
12///
13/// ```rust
14/// use litedoc_core::span::Span;
15///
16/// let span = Span::new(0, 10);
17/// assert_eq!(span.len(), 10);
18/// ```
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub struct Span {
21 /// Starting byte offset (inclusive).
22 pub start: u32,
23 /// Ending byte offset (exclusive).
24 pub end: u32,
25}
26
27impl Span {
28 /// Create a new span from byte offsets.
29 #[inline]
30 pub const fn new(start: u32, end: u32) -> Self {
31 Self { start, end }
32 }
33
34 /// Get the length of this span in bytes.
35 #[inline]
36 pub const fn len(&self) -> u32 {
37 self.end.saturating_sub(self.start)
38 }
39
40 /// Check if this span is empty.
41 #[inline]
42 pub const fn is_empty(&self) -> bool {
43 self.start >= self.end
44 }
45
46 /// Check if this span contains a byte offset.
47 #[inline]
48 pub const fn contains(&self, offset: u32) -> bool {
49 offset >= self.start && offset < self.end
50 }
51
52 /// Merge two spans into one covering both.
53 #[inline]
54 pub fn merge(self, other: Span) -> Span {
55 Span {
56 start: self.start.min(other.start),
57 end: self.end.max(other.end),
58 }
59 }
60}