Skip to main content

factorio_ir/
span.rs

1use std::ops::Range;
2
3/// Inclusive-exclusive byte range into a Rust source buffer.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
5pub struct SourceSpan {
6    pub start: usize,
7    pub end: usize,
8}
9
10impl SourceSpan {
11    #[must_use]
12    pub const fn new(start: usize, end: usize) -> Self {
13        Self { start, end }
14    }
15
16    #[must_use]
17    pub const fn is_empty(self) -> bool {
18        self.start >= self.end
19    }
20
21    #[must_use]
22    pub const fn range(self) -> Range<usize> {
23        self.start..self.end
24    }
25}
26
27impl From<Range<usize>> for SourceSpan {
28    fn from(range: Range<usize>) -> Self {
29        Self {
30            start: range.start,
31            end: range.end,
32        }
33    }
34}
35
36/// A span plus optional human-readable detail (e.g. expression kind).
37#[derive(Debug, Clone, PartialEq, Eq, Default)]
38pub struct SourceLoc {
39    pub span: SourceSpan,
40    pub note: Option<String>,
41}
42
43impl SourceLoc {
44    #[must_use]
45    pub fn new(span: impl Into<SourceSpan>) -> Self {
46        Self {
47            span: span.into(),
48            note: None,
49        }
50    }
51
52    #[must_use]
53    pub fn with_note(mut self, note: impl Into<String>) -> Self {
54        self.note = Some(note.into());
55        self
56    }
57}
58
59impl std::fmt::Display for SourceLoc {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}..{}", self.span.start, self.span.end)?;
62        if let Some(note) = &self.note {
63            write!(f, " ({note})")?;
64        }
65        Ok(())
66    }
67}