use std::{
cell::{Ref, RefCell, RefMut},
collections::{hash_map::Entry, HashMap},
fs::File,
io::{self, Read},
path::{Path, PathBuf},
};
use crate::span::Span;
#[derive(Default)]
pub(crate) struct SourceMap {
inner: RefCell<SourceMapInner>,
}
#[derive(Default)]
struct SourceMapInner {
buffer: Vec<u8>,
map: HashMap<PathBuf, Span>,
}
impl SourceMap {
pub(crate) fn get_bytes(&self, span: Span) -> Ref<'_, [u8]> {
Ref::map(self.inner.borrow(), |inner| &inner.buffer[span.lo..span.hi])
}
pub(crate) fn read_file<P: AsRef<Path>>(&self, path: &P) -> io::Result<Span> {
let (mut map, mut buffer) = RefMut::map_split(self.inner.borrow_mut(), |inner| {
(&mut inner.map, &mut inner.buffer)
});
match map.entry(path.as_ref().to_owned()) {
Entry::Occupied(entry) => Ok(*entry.get()),
Entry::Vacant(entry) => {
let lo = buffer.len();
let hi = lo + File::open(path)?.read_to_end(&mut buffer)?;
let span = Span { lo, hi };
entry.insert(span);
Ok(span)
}
}
}
pub(crate) fn store_bytes(&self, bytes: &[u8]) -> Span {
let buffer = &mut self.inner.borrow_mut().buffer;
let lo = buffer.len();
buffer.extend_from_slice(bytes);
let hi = buffer.len();
Span { lo, hi }
}
pub(crate) fn find_file(&self, target: Span) -> Option<PathBuf> {
for (path, span) in &self.inner.borrow().map {
if span.lo <= target.lo && span.hi >= target.hi {
return Some(path.clone());
}
}
None
}
}