use std::cell::RefCell;
use crate::span::SourceId;
#[derive(Default)]
pub struct SourceCache {
files: RefCell<Vec<(String, *mut str)>>,
}
impl SourceCache {
pub fn new() -> Self {
Self::default()
}
pub fn add(&self, name: String, text: String) -> (SourceId, &str) {
let ptr: *mut str = Box::into_raw(text.into_boxed_str());
let mut files = self.files.borrow_mut();
files.push((name, ptr));
let id = (files.len() - 1) as SourceId;
(id, unsafe { &*ptr })
}
pub fn name(&self, id: SourceId) -> Option<String> {
self.files.borrow().get(id as usize).map(|(n, _)| n.clone())
}
pub fn text(&self, id: SourceId) -> Option<&str> {
let ptr = *self.files.borrow().get(id as usize).map(|(_, p)| p)?;
Some(unsafe { &*ptr })
}
}
impl Drop for SourceCache {
fn drop(&mut self) {
for (_, ptr) in self.files.borrow_mut().drain(..) {
unsafe { drop(Box::from_raw(ptr)) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn references_survive_later_insertions_and_reallocation() {
let cache = SourceCache::new();
let (first_id, first) = cache.add(
"a.aura".into(),
"port: 8080
"
.into(),
);
let mut held = vec![first];
for i in 0..64 {
let (_, text) = cache.add(
format!("f{i}.aura"),
format!(
"x: {i}
"
),
);
held.push(text);
}
assert_eq!(
first,
"port: 8080
"
);
assert_eq!(
cache.text(first_id),
Some(
"port: 8080
"
)
);
for (i, text) in held.iter().skip(1).enumerate() {
assert_eq!(
*text,
format!(
"x: {i}
"
)
);
}
let borrowed = cache.text(first_id).unwrap();
let _ = cache.add(
"later.aura".into(),
"y: 1
"
.into(),
);
assert_eq!(
borrowed,
"port: 8080
"
);
}
#[test]
fn an_unknown_id_is_none_rather_than_a_panic() {
let cache = SourceCache::new();
assert_eq!(cache.text(7), None);
assert_eq!(cache.name(7), None);
}
}