oxilean-parse 0.1.2

OxiLean parser - Concrete syntax to abstract syntax
Documentation
//! Types for source mapping of macro-expanded and generated code.

use std::fmt;

/// Opaque identifier for a source file or fragment.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SourceId(pub u32);

impl fmt::Display for SourceId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "SourceId({})", self.0)
    }
}

/// A byte range within a specific source.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Span {
    /// The source this span refers to.
    pub source: SourceId,
    /// Inclusive start byte offset.
    pub start: usize,
    /// Exclusive end byte offset.
    pub end: usize,
}

impl Span {
    /// Create a new span.
    pub fn new(source: SourceId, start: usize, end: usize) -> Self {
        Self { source, start, end }
    }

    /// Return the length of the span in bytes.
    pub fn len(&self) -> usize {
        self.end.saturating_sub(self.start)
    }

    /// Returns `true` if the span covers zero bytes.
    pub fn is_empty(&self) -> bool {
        self.start >= self.end
    }
}

impl fmt::Display for Span {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}..{}", self.source, self.start, self.end)
    }
}

/// The kind of a source fragment.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SourceKind {
    /// A regular source file on disk.
    File,
    /// Code produced by a macro expansion.
    MacroExpansion {
        /// Name of the macro that was expanded.
        macro_name: String,
        /// The site in the original source where expansion occurred.
        expansion_site: Span,
    },
    /// Synthetically generated code (e.g. by a derive handler).
    Generated {
        /// Name of the code generator.
        generator: String,
    },
    /// Source entered interactively in a REPL session.
    Repl,
}

impl fmt::Display for SourceKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SourceKind::File => write!(f, "file"),
            SourceKind::MacroExpansion { macro_name, .. } => {
                write!(f, "macro expansion of `{}`", macro_name)
            }
            SourceKind::Generated { generator } => write!(f, "generated by `{}`", generator),
            SourceKind::Repl => write!(f, "repl"),
        }
    }
}

/// Metadata about a single source fragment.
#[derive(Debug, Clone)]
pub struct SourceInfo {
    /// Unique identifier for this source.
    pub id: SourceId,
    /// Human-readable name (file path, macro name, etc.).
    pub name: String,
    /// Full source text.
    pub content: String,
    /// What kind of source this is.
    pub kind: SourceKind,
}

/// A chain of spans tracing a token back through macro expansions.
#[derive(Debug, Clone, Default)]
pub struct SpanChain {
    /// Sequence of spans, outermost (user-written) first.
    pub spans: Vec<Span>,
}

impl SpanChain {
    /// Create an empty chain.
    pub fn new() -> Self {
        Self { spans: Vec::new() }
    }

    /// Push a span onto the chain.
    pub fn push(&mut self, span: Span) {
        self.spans.push(span);
    }

    /// Return the deepest (innermost expanded) span, if any.
    pub fn innermost(&self) -> Option<&Span> {
        self.spans.last()
    }

    /// Return the outermost (user-facing) span, if any.
    pub fn outermost(&self) -> Option<&Span> {
        self.spans.first()
    }

    /// Returns the number of spans in the chain.
    pub fn depth(&self) -> usize {
        self.spans.len()
    }
}

/// A 0-based line and column position within a source.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
    /// 0-based line number.
    pub line: u32,
    /// 0-based UTF-8 column number (byte column, not character column).
    pub col: u32,
}

impl Position {
    /// Create a position from 0-based line and column.
    pub fn new(line: u32, col: u32) -> Self {
        Self { line, col }
    }
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.line + 1, self.col + 1)
    }
}

/// A [`Position`] with an associated source identifier.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LineColumn {
    /// The source this position belongs to.
    pub source: SourceId,
    /// Line and column within that source.
    pub position: Position,
}

impl LineColumn {
    /// Create a `LineColumn`.
    pub fn new(source: SourceId, line: u32, col: u32) -> Self {
        Self {
            source,
            position: Position::new(line, col),
        }
    }
}

impl fmt::Display for LineColumn {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.source, self.position)
    }
}

/// The central registry that owns all source fragments.
///
/// `SourceMap` assigns each registered fragment a unique [`SourceId`] and
/// provides utilities for converting between byte offsets and line/column
/// positions.
#[derive(Debug, Default)]
pub struct SourceMap {
    /// All registered source fragments, indexed by their `SourceId`.
    pub sources: Vec<SourceInfo>,
    /// The next `SourceId` to assign.
    pub next_id: u32,
}