use std::error::Error; use std::fmt; use std::path::PathBuf; use crate::{Diagnostic, SourceLocation}; /// Source provenance, independent of filesystem layout or VM heap handles. /// Endor revisions are opaque labels supplied by the host, not verified hashes. #[derive(Debug, Clone, PartialEq, Eq)] pub enum SourceOrigin { Text { name: String }, Stream { name: String }, File { path: PathBuf }, Endor { uri: String, revision: String }, } impl fmt::Display for SourceOrigin { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Text { name } | Self::Stream { name } => formatter.write_str(name), Self::File { path } => write!(formatter, "{}", path.display()), Self::Endor { uri, revision } => write!(formatter, "{uri} [revision {revision}]"), } } } /// An owned, immutable UTF-8 snapshot. Reader adapters preserve the text exactly /// so token byte offsets still refer to the original decoded source. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SourceUnit { origin: SourceOrigin, text: String, } impl SourceUnit { pub(crate) fn new(origin: SourceOrigin, text: String) -> Self { Self { origin, text } } pub fn origin(&self) -> &SourceOrigin { &self.origin } pub fn text(&self) -> &str { &self.text } /// Bind a diagnostic to this snapshot before discarding its source buffer. pub fn annotate(&self, diagnostic: Diagnostic) -> SourceDiagnostic { SourceDiagnostic { location: diagnostic.location(&self.text), origin: self.origin.clone(), diagnostic: Box::new(diagnostic), } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct SourceDiagnostic { pub origin: SourceOrigin, pub location: SourceLocation, pub diagnostic: Box, } impl fmt::Display for SourceDiagnostic { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!( formatter, "{}:{}:{}: {}", self.origin, self.location.line, self.location.column, self.diagnostic.message ) } } impl Error for SourceDiagnostic { fn source(&self) -> Option<&(dyn Error + 'static)> { Some(self.diagnostic.as_ref()) } }