use std::{convert::From, fmt::Debug, fs, io, ops::Sub, path::Path};
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Pos(usize);
impl Pos {
pub fn new(p: usize) -> Self {
Pos(p)
}
}
impl Sub<Pos> for Pos {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Pos(self.0 - other.0)
}
}
impl From<Pos> for usize {
fn from(pos: Pos) -> Self {
pos.0
}
}
impl From<usize> for Pos {
fn from(val: usize) -> Self {
Pos(val)
}
}
#[derive(Debug)]
pub struct SourceFile {
pub src_file: String,
pub src: String,
pub beginnings: Vec<Pos>,
}
impl SourceFile {
pub fn new<P>(src_file: P) -> Result<Self, io::Error>
where
P: AsRef<Path> + Debug,
{
let src = fs::read_to_string(src_file.as_ref())?;
let mut beginnings = vec![Pos::new(0)];
beginnings.extend_from_slice(
&src.match_indices("\n")
.map(|(idx, _)| Pos::new(idx))
.collect::<Vec<_>>(),
);
Ok(SourceFile {
src_file: src_file
.as_ref()
.to_str()
.ok_or(io::Error::new(
io::ErrorKind::Other,
"could not read source file contents",
))?
.to_owned(),
src,
beginnings,
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct Span {
pub low: Pos,
pub high: Pos,
}
impl Span {
pub fn merge(&self, other: &Span) -> Self {
Span {
low: std::cmp::min(self.low, other.low),
high: std::cmp::max(self.high, other.high),
}
}
pub fn location<'s>(&self, source_file: &'s SourceFile) -> Location<'s> {
let src_file = &source_file.src_file;
let (line, col) = match source_file.beginnings.binary_search(&self.low) {
Ok(line) => {
let line = line + 1;
let col: usize = (self.low - source_file.beginnings[line - 1]).into();
(line, col)
}
Err(line) => {
let line = line;
let col: usize = (self.low - source_file.beginnings[line - 1]).into();
(line, col)
}
};
Location {
src_file,
line,
col,
}
}
pub fn source<'s>(&self, source_file: &'s SourceFile) -> &'s str {
&source_file.src[self.low.into()..self.high.into()]
}
pub fn source_line<'s>(&self, source_file: &'s SourceFile) -> &'s str {
let Location { line, .. } = self.location(&source_file);
let start_pos = source_file.beginnings[line - 1];
let end_pos = if line == source_file.beginnings.len() {
source_file.beginnings[line - 1]
} else {
source_file.beginnings[line]
};
&source_file.src[start_pos.into()..end_pos.into()]
}
}
#[derive(Debug)]
pub struct Location<'l> {
pub src_file: &'l str,
pub line: usize,
pub col: usize,
}