1use super::*;
2
3use std::{
4 path::{Path, PathBuf},
5 collections::{HashMap, hash_map::Entry},
6 fs,
7};
8
9pub trait Cache<Id: ?Sized> {
11 fn fetch(&mut self, id: &Id) -> Result<&Source, Box<dyn fmt::Debug + '_>>;
14
15 fn display<'a>(&self, id: &'a Id) -> Option<Box<dyn fmt::Display + 'a>>;
20}
21
22impl<'b, C: Cache<Id>, Id: ?Sized> Cache<Id> for &'b mut C {
23 fn fetch(&mut self, id: &Id) -> Result<&Source, Box<dyn fmt::Debug + '_>> { C::fetch(self, id) }
24 fn display<'a>(&self, id: &'a Id) -> Option<Box<dyn fmt::Display + 'a>> { C::display(self, id) }
25}
26
27impl<C: Cache<Id>, Id: ?Sized> Cache<Id> for Box<C> {
28 fn fetch(&mut self, id: &Id) -> Result<&Source, Box<dyn fmt::Debug + '_>> { C::fetch(self, id) }
29 fn display<'a>(&self, id: &'a Id) -> Option<Box<dyn fmt::Display + 'a>> { C::display(self, id) }
30}
31
32#[derive(Clone, Debug, Hash, PartialEq, Eq)]
34pub struct Line {
35 offset: usize,
36 len: usize,
37 chars: String,
38}
39
40impl Line {
41 pub fn offset(&self) -> usize { self.offset }
43
44 pub fn len(&self) -> usize { self.len }
46
47 pub fn span(&self) -> Range<usize> { self.offset..self.offset + self.len }
49
50 pub fn chars(&self) -> impl Iterator<Item = char> + '_ { self.chars.chars() }
52}
53
54#[derive(Clone, Debug, Hash, PartialEq, Eq)]
58pub struct Source {
59 lines: Vec<Line>,
60 len: usize,
61}
62
63impl<S: AsRef<str>> From<S> for Source {
64 fn from(s: S) -> Self {
68 let mut offset = 0;
69 Self {
70 lines: s
71 .as_ref()
72 .split_terminator('\n') .map(|line| {
74 let l = Line {
75 offset,
76 len: line.chars().count() + 1,
77 chars: line.trim_end().to_owned(),
78 };
79 offset += l.len;
80 l
81 })
82 .collect(),
83 len: offset,
84 }
85 }
86}
87
88impl Source {
89 pub fn len(&self) -> usize { self.len }
91
92 pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
94 self.lines.iter().map(|l| l.chars()).flatten()
95 }
96
97 pub fn line(&self, idx: usize) -> Option<&Line> { self.lines.get(idx) }
99
100 pub fn lines(&self) -> impl ExactSizeIterator<Item = &Line> + '_ { self.lines.iter() }
102
103 pub fn get_offset_line(&self, offset: usize) -> Option<(&Line, usize, usize)> {
107 if offset <= self.len {
108 let idx = self.lines
109 .binary_search_by_key(&offset, |line| line.offset)
110 .unwrap_or_else(|idx| idx.saturating_sub(1));
111 let line = &self.lines[idx];
112 assert!(offset >= line.offset, "offset = {}, line.offset = {}", offset, line.offset);
113 Some((line, idx, offset - line.offset))
114 } else {
115 None
116 }
117 }
118
119 pub fn get_line_range<S: Span>(&self, span: &S) -> Range<usize> {
124 let start = self.get_offset_line(span.start()).map_or(0, |(_, l, _)| l);
125 let end = self.get_offset_line(span.end().saturating_sub(1).max(span.start())).map_or(self.lines.len(), |(_, l, _)| l + 1);
126 start..end
127 }
128}
129
130impl Cache<()> for Source {
131 fn fetch(&mut self, _: &()) -> Result<&Source, Box<dyn fmt::Debug + '_>> { Ok(self) }
132 fn display(&self, _: &()) -> Option<Box<dyn fmt::Display>> { None }
133}
134
135impl<Id: fmt::Display + Eq> Cache<Id> for (Id, Source) {
136 fn fetch(&mut self, id: &Id) -> Result<&Source, Box<dyn fmt::Debug + '_>> {
137 if id == &self.0 { Ok(&self.1) } else { Err(Box::new(format!("Failed to fetch source '{}'", id))) }
138 }
139 fn display<'a>(&self, id: &'a Id) -> Option<Box<dyn fmt::Display + 'a>> { Some(Box::new(id)) }
140}
141
142#[derive(Default, Debug, Clone)]
144pub struct FileCache {
145 files: HashMap<PathBuf, Source>,
146}
147
148impl Cache<Path> for FileCache {
149 fn fetch(&mut self, path: &Path) -> Result<&Source, Box<dyn fmt::Debug + '_>> {
150 Ok(match self.files.entry(path.to_path_buf()) { Entry::Occupied(entry) => entry.into_mut(),
152 Entry::Vacant(entry) => entry.insert(Source::from(&fs::read_to_string(path).map_err(|e| Box::new(e) as _)?)),
153 })
154 }
155 fn display<'a>(&self, path: &'a Path) -> Option<Box<dyn fmt::Display + 'a>> { Some(Box::new(path.display())) }
156}
157
158#[derive(Debug, Clone)]
160pub struct FnCache<Id, F> {
161 sources: HashMap<Id, Source>,
162 get: F,
163}
164
165impl<Id, F> FnCache<Id, F> {
166 pub fn new(get: F) -> Self {
168 Self {
169 sources: HashMap::default(),
170 get,
171 }
172 }
173
174 pub fn with_sources(mut self, sources: HashMap<Id, Source>) -> Self
176 where Id: Eq + Hash
177 {
178 self.sources.reserve(sources.len());
179 for (id, src) in sources {
180 self.sources.insert(id, src);
181 }
182 self
183 }
184}
185
186impl<Id: fmt::Display + Hash + PartialEq + Eq + Clone, F> Cache<Id> for FnCache<Id, F>
187 where F: for<'a> FnMut(&'a Id) -> Result<String, Box<dyn fmt::Debug>>
188{
189 fn fetch(&mut self, id: &Id) -> Result<&Source, Box<dyn fmt::Debug + '_>> {
190 Ok(match self.sources.entry(id.clone()) {
191 Entry::Occupied(entry) => entry.into_mut(),
192 Entry::Vacant(entry) => entry.insert(Source::from((self.get)(id)?)),
193 })
194 }
195 fn display<'a>(&self, id: &'a Id) -> Option<Box<dyn fmt::Display + 'a>> { Some(Box::new(id)) }
196}
197
198pub fn sources<Id, S, I>(iter: I) -> impl Cache<Id>
200where
201 Id: fmt::Display + Hash + PartialEq + Eq + Clone + 'static,
202 I: IntoIterator<Item = (Id, S)>,
203 S: AsRef<str>,
204{
205 FnCache::new((move |id| Err(Box::new(format!("Failed to fetch source '{}'", id)) as _)) as fn(&_) -> _)
206 .with_sources(iter
207 .into_iter()
208 .map(|(id, s)| (id, Source::from(s.as_ref())))
209 .collect())
210}