typst_as_lib/
file_resolver.rs1use ecow::eco_format;
2use std::{
3 borrow::Cow,
4 collections::HashMap,
5 path::{Path, PathBuf},
6};
7use typst::{
8 diag::{FileError, FileResult},
9 foundations::Bytes,
10 syntax::{FileId, Source},
11};
12
13use crate::{
14 cached_file_resolver::{CachedFileResolver, IntoCachedFileResolver},
15 conversions::{IntoBytes, IntoFileId, IntoSource},
16 util::{bytes_to_source, not_found},
17};
18
19pub const DEFAULT_PACKAGES_SUBDIR: &str = "typst/packages";
22
23pub trait FileResolver {
24 fn resolve_binary(&self, id: FileId) -> FileResult<Cow<Bytes>>;
25 fn resolve_source(&self, id: FileId) -> FileResult<Cow<Source>>;
26}
27
28#[derive(Debug, Clone)]
29pub(crate) struct MainSourceFileResolver {
30 main_source: Source,
31}
32
33impl MainSourceFileResolver {
34 pub(crate) fn new(main_source: Source) -> Self {
35 Self { main_source }
36 }
37}
38
39impl FileResolver for MainSourceFileResolver {
40 fn resolve_binary(&self, id: FileId) -> FileResult<Cow<Bytes>> {
41 Err(not_found(id))
42 }
43
44 fn resolve_source(&self, id: FileId) -> FileResult<Cow<Source>> {
45 let Self { main_source } = self;
46 if id == main_source.id() {
47 return Ok(Cow::Borrowed(main_source));
48 }
49 Err(not_found(id))
50 }
51}
52
53#[derive(Debug, Clone)]
54pub struct StaticSourceFileResolver {
55 sources: HashMap<FileId, Source>,
56}
57
58impl StaticSourceFileResolver {
59 pub(crate) fn new<IS, S>(sources: IS) -> Self
60 where
61 IS: IntoIterator<Item = S>,
62 S: IntoSource,
63 {
64 let sources = sources
65 .into_iter()
66 .map(|s| {
67 let s = s.into_source();
68 (s.id(), s)
69 })
70 .collect();
71 Self { sources }
72 }
73}
74
75impl FileResolver for StaticSourceFileResolver {
76 fn resolve_binary(&self, id: FileId) -> FileResult<Cow<Bytes>> {
77 Err(not_found(id))
78 }
79
80 fn resolve_source(&self, id: FileId) -> FileResult<Cow<Source>> {
81 self.sources
82 .get(&id)
83 .map(Cow::Borrowed)
84 .ok_or_else(|| not_found(id))
85 }
86}
87
88#[derive(Debug, Clone)]
89pub struct StaticFileResolver {
90 binaries: HashMap<FileId, Bytes>,
91}
92
93impl StaticFileResolver {
94 pub(crate) fn new<IB, F, B>(binaries: IB) -> Self
95 where
96 IB: IntoIterator<Item = (F, B)>,
97 F: IntoFileId,
98 B: IntoBytes,
99 {
100 let binaries = binaries
101 .into_iter()
102 .map(|(id, b)| (id.into_file_id(), b.into_bytes()))
103 .collect();
104 Self { binaries }
105 }
106}
107
108impl FileResolver for StaticFileResolver {
109 fn resolve_binary(&self, id: FileId) -> FileResult<Cow<Bytes>> {
110 self.binaries
111 .get(&id)
112 .map(Cow::Borrowed)
113 .ok_or_else(|| not_found(id))
114 }
115
116 fn resolve_source(&self, id: FileId) -> FileResult<Cow<Source>> {
117 Err(not_found(id))
118 }
119}
120
121#[derive(Debug, Clone)]
122pub struct FileSystemResolver {
123 root: PathBuf,
124 local_package_root: Option<PathBuf>,
125}
126
127impl FileSystemResolver {
128 pub fn new(root: PathBuf) -> Self {
129 let mut root = root.clone();
130 root.push("");
133 Self {
134 root,
135 local_package_root: None,
136 }
137 }
138
139 #[deprecated(
141 since = "0.14.1",
142 note = "Use `FileSystemResolver::local_package_root` instead"
143 )]
144 pub fn with_local_package_root(self, path: PathBuf) -> Self {
145 Self {
146 local_package_root: Some(path),
147 ..self
148 }
149 }
150
151 pub fn local_package_root(self, local_package_root: PathBuf) -> Self {
153 Self {
154 local_package_root: Some(local_package_root),
155 ..self
156 }
157 }
158
159 fn resolve_bytes(&self, id: FileId) -> FileResult<Vec<u8>> {
160 let Self {
161 root,
162 local_package_root,
163 } = self;
164 let dir: Cow<Path> = if let Some(package) = id.package() {
166 let data_dir = if let Some(data_dir) = local_package_root {
167 Cow::Borrowed(data_dir)
168 } else if let Some(data_dir) = dirs::data_dir() {
169 Cow::Owned(data_dir.join(DEFAULT_PACKAGES_SUBDIR))
170 } else {
171 return Err(FileError::Other(Some(eco_format!("No data dir set!"))));
172 };
173 let subdir = Path::new(package.namespace.as_str())
174 .join(package.name.as_str())
175 .join(package.version.to_string());
176 Cow::Owned(data_dir.join(subdir))
177 } else {
178 Cow::Borrowed(root)
179 };
180
181 let path = id
182 .vpath()
183 .resolve(&dir)
184 .ok_or_else(|| FileError::NotFound(dir.to_path_buf()))?;
185 let content = std::fs::read(&path).map_err(|error| FileError::from_io(error, &path))?;
186 Ok(content)
187 }
188}
189
190impl IntoCachedFileResolver for FileSystemResolver {
191 fn into_cached(self) -> CachedFileResolver<Self> {
192 CachedFileResolver::new(self)
193 .with_in_memory_source_cache()
194 .with_in_memory_binary_cache()
195 }
196}
197
198impl FileResolver for FileSystemResolver {
199 fn resolve_binary(&self, id: FileId) -> FileResult<Cow<Bytes>> {
200 let b = self.resolve_bytes(id)?;
201 Ok(Cow::Owned(Bytes::new(b)))
202 }
203
204 fn resolve_source(&self, id: FileId) -> FileResult<Cow<Source>> {
205 let file = self.resolve_bytes(id)?;
206 let source = bytes_to_source(id, &file)?;
207 Ok(Cow::Owned(source))
208 }
209}