moxy_token/source/
file.rs1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::path::PathBuf;
4
5use super::Location;
6use crate::span::fallback::Span;
7
8#[derive(Debug)]
12pub struct Source {
13 path: Option<PathBuf>,
16
17 text: String,
19
20 span: Span,
22
23 lines: Vec<usize>,
25
26 char_to_byte: RefCell<BTreeMap<usize, usize>>,
28}
29
30impl Source {
31 pub(crate) fn new(start: usize, src: impl Into<String>) -> Self {
32 let text = src.into();
33 let mut lines = vec![0];
34 let mut total = 0usize;
35
36 for ch in text.chars() {
37 total += 1;
38
39 if ch == '\n' {
40 lines.push(total);
41 }
42 }
43
44 Self {
45 path: None,
46 text,
47 span: Span::new(start as u32, (start + total) as u32),
48 lines,
49 char_to_byte: RefCell::new(BTreeMap::new()),
50 }
51 }
52
53 pub fn from_file(path: impl Into<PathBuf>) -> std::io::Result<Self> {
54 let path = path.into();
55 let src = std::fs::read_to_string(&path)?;
56 let mut src = Self::new(0, src);
57
58 src.path = Some(path);
59 Ok(src)
60 }
61
62 pub fn path(&self) -> Option<&PathBuf> {
63 self.path.as_ref()
64 }
65
66 pub fn text(&self) -> &str {
67 &self.text
68 }
69
70 pub fn span(&self) -> Span {
71 self.span
72 }
73
74 pub fn range(&self, span: Span) -> std::ops::Range<usize> {
76 let r = span.byte_range();
77 self.byte(r.start)..self.byte(r.end)
78 }
79
80 pub fn slice(&self, span: Span) -> String {
82 self.text[self.range(span)].to_owned()
83 }
84
85 pub fn location(&self, i: usize) -> Location {
87 let index = i - self.span.byte_range().start;
88
89 match self.lines.binary_search(&index) {
90 Err(next) => Location::new(index, next - 1, index - self.lines[next - 1]),
91 Ok(line) => Location::new(index, line, 0),
92 }
93 }
94
95 pub fn byte(&self, i: usize) -> usize {
97 let index = i - self.span.byte_range().start;
98 let mut cache = self.char_to_byte.borrow_mut();
99
100 if let Some(byte_index) = cache.get(&index) {
101 return *byte_index;
102 }
103
104 let (ci, bi) = cache
105 .range(..=index)
106 .next_back()
107 .map(|(&char_index, &byte_index)| (char_index, byte_index))
108 .unwrap_or((0, 0));
109
110 let mut char_index = ci;
111 let mut byte_index = bi;
112
113 for ch in self.text[bi..].chars() {
114 if char_index == index {
115 cache.insert(index, byte_index);
116 return byte_index;
117 }
118
119 char_index += 1;
120 byte_index += ch.len_utf8();
121 }
122
123 cache.insert(index, byte_index);
124 byte_index
125 }
126}
127
128impl Default for Source {
129 fn default() -> Self {
130 Self {
131 path: None,
132 text: String::default(),
133 span: Span::default(),
134 lines: vec![0],
135 char_to_byte: RefCell::new(BTreeMap::default()),
136 }
137 }
138}