Skip to main content

oxilean_parse/source_map/
types.rs

1//! Types for source mapping of macro-expanded and generated code.
2
3use std::fmt;
4
5/// Opaque identifier for a source file or fragment.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct SourceId(pub u32);
8
9impl fmt::Display for SourceId {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        write!(f, "SourceId({})", self.0)
12    }
13}
14
15/// A byte range within a specific source.
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct Span {
18    /// The source this span refers to.
19    pub source: SourceId,
20    /// Inclusive start byte offset.
21    pub start: usize,
22    /// Exclusive end byte offset.
23    pub end: usize,
24}
25
26impl Span {
27    /// Create a new span.
28    pub fn new(source: SourceId, start: usize, end: usize) -> Self {
29        Self { source, start, end }
30    }
31
32    /// Return the length of the span in bytes.
33    pub fn len(&self) -> usize {
34        self.end.saturating_sub(self.start)
35    }
36
37    /// Returns `true` if the span covers zero bytes.
38    pub fn is_empty(&self) -> bool {
39        self.start >= self.end
40    }
41}
42
43impl fmt::Display for Span {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(f, "{}:{}..{}", self.source, self.start, self.end)
46    }
47}
48
49/// The kind of a source fragment.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub enum SourceKind {
52    /// A regular source file on disk.
53    File,
54    /// Code produced by a macro expansion.
55    MacroExpansion {
56        /// Name of the macro that was expanded.
57        macro_name: String,
58        /// The site in the original source where expansion occurred.
59        expansion_site: Span,
60    },
61    /// Synthetically generated code (e.g. by a derive handler).
62    Generated {
63        /// Name of the code generator.
64        generator: String,
65    },
66    /// Source entered interactively in a REPL session.
67    Repl,
68}
69
70impl fmt::Display for SourceKind {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            SourceKind::File => write!(f, "file"),
74            SourceKind::MacroExpansion { macro_name, .. } => {
75                write!(f, "macro expansion of `{}`", macro_name)
76            }
77            SourceKind::Generated { generator } => write!(f, "generated by `{}`", generator),
78            SourceKind::Repl => write!(f, "repl"),
79        }
80    }
81}
82
83/// Metadata about a single source fragment.
84#[derive(Debug, Clone)]
85pub struct SourceInfo {
86    /// Unique identifier for this source.
87    pub id: SourceId,
88    /// Human-readable name (file path, macro name, etc.).
89    pub name: String,
90    /// Full source text.
91    pub content: String,
92    /// What kind of source this is.
93    pub kind: SourceKind,
94}
95
96/// A chain of spans tracing a token back through macro expansions.
97#[derive(Debug, Clone, Default)]
98pub struct SpanChain {
99    /// Sequence of spans, outermost (user-written) first.
100    pub spans: Vec<Span>,
101}
102
103impl SpanChain {
104    /// Create an empty chain.
105    pub fn new() -> Self {
106        Self { spans: Vec::new() }
107    }
108
109    /// Push a span onto the chain.
110    pub fn push(&mut self, span: Span) {
111        self.spans.push(span);
112    }
113
114    /// Return the deepest (innermost expanded) span, if any.
115    pub fn innermost(&self) -> Option<&Span> {
116        self.spans.last()
117    }
118
119    /// Return the outermost (user-facing) span, if any.
120    pub fn outermost(&self) -> Option<&Span> {
121        self.spans.first()
122    }
123
124    /// Returns the number of spans in the chain.
125    pub fn depth(&self) -> usize {
126        self.spans.len()
127    }
128}
129
130/// A 0-based line and column position within a source.
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132pub struct Position {
133    /// 0-based line number.
134    pub line: u32,
135    /// 0-based UTF-8 column number (byte column, not character column).
136    pub col: u32,
137}
138
139impl Position {
140    /// Create a position from 0-based line and column.
141    pub fn new(line: u32, col: u32) -> Self {
142        Self { line, col }
143    }
144}
145
146impl fmt::Display for Position {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        write!(f, "{}:{}", self.line + 1, self.col + 1)
149    }
150}
151
152/// A [`Position`] with an associated source identifier.
153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub struct LineColumn {
155    /// The source this position belongs to.
156    pub source: SourceId,
157    /// Line and column within that source.
158    pub position: Position,
159}
160
161impl LineColumn {
162    /// Create a `LineColumn`.
163    pub fn new(source: SourceId, line: u32, col: u32) -> Self {
164        Self {
165            source,
166            position: Position::new(line, col),
167        }
168    }
169}
170
171impl fmt::Display for LineColumn {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        write!(f, "{}:{}", self.source, self.position)
174    }
175}
176
177/// The central registry that owns all source fragments.
178///
179/// `SourceMap` assigns each registered fragment a unique [`SourceId`] and
180/// provides utilities for converting between byte offsets and line/column
181/// positions.
182#[derive(Debug, Default)]
183pub struct SourceMap {
184    /// All registered source fragments, indexed by their `SourceId`.
185    pub sources: Vec<SourceInfo>,
186    /// The next `SourceId` to assign.
187    pub next_id: u32,
188}