tex_engine/engine/
filesystem.rs1pub mod kpathsea;
12
13use std::collections::hash_map::Entry;
14use std::marker::PhantomData;
15use std::path::PathBuf;
16use std::sync::RwLock;
17use kpathsea::Kpathsea;
18use ahash::HashMap;
19use crate::engine::filesystem::kpathsea::KpseResult;
20use crate::engine::mouth::string_source::StringSource;
21use crate::engine::state::State;
22use crate::tex::catcodes::CategoryCode;
23use crate::tex::token::{BaseToken, Token};
24use crate::utils::errors::{OtherError, TeXError};
25use crate::utils::Ptr;
26use crate::utils::strings::CharType;
27
28pub trait File<Char:CharType>:Clone {
29 fn path(&self) -> &PathBuf;
30 fn exists(&self) -> bool;
31 fn content_string(&self) -> Vec<u8>;
32 fn open_out(&self);
33 fn open_in(&self);
34 fn close_out(&self);
35 fn close_in(&self);
36 fn write(&self,string:&str);
37 fn eof(&self) -> bool;
38 fn read<T:Token<Char=Char>,S:State<T>>(&self,state:&S) -> Result<Vec<T>,Box<dyn TeXError<T>>>;
39}
40
41pub trait FileSystem<Char:CharType>:'static {
42 type F:File<Char>;
43 fn new(pwd:PathBuf) -> Self;
44 fn get(&mut self,path:&str) -> Self::F;
45 fn set_pwd(&mut self, pwd:PathBuf) -> PathBuf;
46}
47
48pub struct PhysicalFile<Char:CharType> {path:PathBuf,contents:Vec<u8>,phantom:PhantomData<Char>}
49impl<Char:CharType> PhysicalFile<Char> {
50 pub fn new(path:PathBuf) -> Self {
51 PhysicalFile {
52 contents: {
53 std::fs::read(&path).ok().unwrap_or(vec!())
54 },
55 phantom: PhantomData,
56 path
57 }
58 }
59}
60impl<Char:CharType> File<Char> for Ptr<PhysicalFile<Char>> {
61 fn path(&self) -> &PathBuf { &self.path }
62 fn exists(&self) -> bool { self.path.exists() }
63 fn content_string(&self) -> Vec<u8> {
64 self.contents.clone()
65 }
66 fn open_out(&self) {
67 todo!("Physical file system not implemented yet")
68 }
69 fn close_in(&self) {
70 todo!("Physical file system not implemented yet")
71 }
72 fn close_out(&self) {
73 todo!("Physical file system not implemented yet")
74 }
75 fn eof(&self) -> bool {
76 todo!("Physical file system not implemented yet")
77 }
78 fn open_in(&self) {
79 todo!("Physical file system not implemented yet")
80 }
81 fn write(&self,_:&str) {
82 todo!("Physical file system not implemented yet")
83 }
84 fn read<T:Token,S:State<T>>(&self,_:&S) -> Result<Vec<T>,Box<dyn TeXError<T>>> {
85 todo!("Physical file system not implemented yet")
86 }
87}
88
89
90pub struct VirtualFile<Char:CharType> {path:PathBuf,contents:RwLock<Option<Vec<u8>>>,open:RwLock<Option<StringSource<Char>>>}
91impl<Char:CharType> File<Char> for Ptr<VirtualFile<Char>> {
92 fn path(&self) -> &PathBuf { &self.path }
93 fn exists(&self) -> bool { self.contents.read().unwrap().is_some() }
94 fn content_string(&self) -> Vec<u8> {
95 match &*self.contents.read().unwrap() {
96 Some(s) => s.clone(),
97 None => vec!()
98 }
99 }
100 fn close_out(&self) {}
101 fn open_out(&self) {
102 let mut w = self.contents.write().unwrap();
103 *w = Some(vec!());
104 }
105 fn open_in(&self) {
106 let w = self.contents.read().unwrap();
107 let mut open = self.open.write().unwrap();
108 let v = match &*w {
109 None => vec!(),
110 Some(v) => v.clone()
111 };
112 *open = Some(StringSource::new(v,Some(Ptr::new(self.path.to_str().unwrap().to_string()))));
113 }
114 fn close_in(&self) {
115 let mut open = self.open.write().unwrap();
116 *open = None;
117 }
118 fn eof(&self) -> bool {
119 let mut open = self.open.write().unwrap();
120 open.as_mut().unwrap().preview().is_empty()}
122 fn read<T: Token<Char=Char>, S: State<T>>(&self, state: &S) -> Result<Vec<T>,Box<dyn TeXError<T>>> {
123 let mut open = self.open.write().unwrap();
124 match &mut *open {
125 None => Err(OtherError{msg:"File not open".to_string(),source:None,cause:None}.into()),
126 Some(m) => {
127 let line = m.line();
128 let mut ret: Vec<T> = vec!();
129 let mut ingroups = 0;
130 loop {
131 if ingroups != 0 || m.line() == line {
132 match m.get_next::<T>(state.get_catcode_scheme(),state.get_endlinechar())? {
133 None =>
134 return Ok(ret),
135 Some(tk) => {
136 match tk.base() {
137 BaseToken::Char(_,CategoryCode::BeginGroup) => ingroups += 1,
138 BaseToken::Char(_,CategoryCode::EndGroup) => ingroups -= 1,
139 _ => () }
141 ret.push(tk)
142 }
143 }
144 } else { return Ok(ret) }
145 }
146 }
147 }
148 }
149 fn write(&self, string: &str) {
150 let mut write = self.contents.write().unwrap();
151 let mut v = write.as_mut().unwrap();
152 v.extend(string.as_bytes());
153 v.push(b'\n');
154 }
155}
156
157pub struct KpsePhysicalFileSystem<Char:CharType> {kpathsea:Kpathsea,phantom:PhantomData<Char>}
158impl<Char:CharType> FileSystem<Char> for KpsePhysicalFileSystem<Char> {
159 type F = Ptr<PhysicalFile<Char>>;
160 fn new(pwd:PathBuf) -> Self { KpsePhysicalFileSystem {kpathsea:Kpathsea::new(pwd),phantom:PhantomData} }
161 fn get(&mut self, _path: &str) -> Self::F {
162 todo!("Physical file system not implemented yet")
163 }
164 fn set_pwd(&mut self, _pwd: PathBuf) -> PathBuf {
165 todo!("Physical file system not implemented yet")
166 }
167}
168
169pub struct KpseVirtualFileSystem<Char:CharType> {pwd:PathBuf,kpathsea:Kpathsea,files:HashMap<PathBuf,Ptr<VirtualFile<Char>>>}
170impl<Char:CharType> KpsePhysicalFileSystem<Char> {
171 pub fn kpsewhich(&self, path: &str) -> KpseResult {
172 self.kpathsea.kpsewhich(path)
173 }
174}
175impl<Char:CharType> KpseVirtualFileSystem<Char> {
176 pub fn kpsewhich(&self, path: &str) -> KpseResult {
177 self.kpathsea.kpsewhich(path)
178 }
179}
180impl<Char:CharType> FileSystem<Char> for KpseVirtualFileSystem<Char> {
181 type F = Ptr<VirtualFile<Char>>;
182 fn new(pwd:PathBuf) -> Self { KpseVirtualFileSystem {
183 pwd:pwd.clone(),
184 kpathsea:Kpathsea::new(pwd),
185 files:HashMap::default()
186 } }
187 fn set_pwd(&mut self, pwd: PathBuf) -> PathBuf {
188 let old = std::mem::replace(&mut self.kpathsea, Kpathsea::new(pwd));
189 old.pwd
190 }
191 fn get(&mut self, path: &str) -> Self::F {
192 let ret = self.kpathsea.kpsewhich(path);
193 match self.files.entry(ret.path) {
194 Entry::Occupied(e) => e.get().clone(),
195 Entry::Vacant(e) => {
196 let s: Option<Vec<u8>> = self.kpathsea.get(&self.pwd,e.key());
197 let ret = Ptr::new(VirtualFile{
198 path:e.key().clone(),
199 contents:RwLock::new(s),
200 open:RwLock::new(None)
201 });
202 e.insert(ret.clone());
203 ret
204 }
205 }
206 }
207}