Skip to main content

chrony_confile/
span.rs

1//! Source location tracking for directives and parse errors.
2//!
3//! [`Span`] records the file name, line range, and column range of a parsed directive.
4//! It is used in error reporting and debugging to provide precise source locations.
5
6use std::fmt;
7use std::path::PathBuf;
8
9/// Represents a location (span) in a source file.
10///
11/// Records the file name (if known), line range, and column range of a parsed
12/// configuration element. Used in error reporting to provide precise source locations.
13///
14/// # Examples
15///
16/// ```rust
17/// use chrony_confile::Span;
18/// use std::path::PathBuf;
19///
20/// let span = Span::new(Some(PathBuf::from("chrony.conf")), 10, 1, 40);
21/// assert_eq!(span.to_string(), "chrony.conf:10:1");
22/// ```
23#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
24pub struct Span {
25    /// The source file path, if known.
26    pub file: Option<PathBuf>,
27    /// The starting line number (1-indexed).
28    pub line_start: usize,
29    /// The ending line number (1-indexed).
30    pub line_end: usize,
31    /// The starting column (1-indexed).
32    pub col_start: usize,
33    /// The ending column (1-indexed).
34    pub col_end: usize,
35}
36
37impl Span {
38    /// Creates a new span at a single line.
39    pub fn new(file: Option<PathBuf>, line: usize, col_start: usize, col_end: usize) -> Self {
40        Self { file, line_start: line, line_end: line, col_start, col_end }
41    }
42
43    /// Merges two spans from the same file, producing a span that covers both.
44    ///
45    /// # Panics
46    ///
47    /// Panics if the two spans are from different files (in debug builds).
48    pub fn merge(&self, other: &Self) -> Self {
49        debug_assert_eq!(self.file, other.file, "cannot merge spans from different files");
50        Self {
51            file: self.file.clone(),
52            line_start: self.line_start.min(other.line_start),
53            line_end: self.line_end.max(other.line_end),
54            col_start: if self.line_start <= other.line_start { self.col_start } else { other.col_start },
55            col_end: if self.line_end >= other.line_end { self.col_end } else { other.col_end },
56        }
57    }
58}
59
60impl fmt::Display for Span {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        match &self.file {
63            Some(file) => write!(f, "{}:{}:{}", file.display(), self.line_start, self.col_start),
64            None => write!(f, "line {}:{}", self.line_start, self.col_start),
65        }
66    }
67}