typst_as_lib/
cached_file_resolver.rs

1use std::{
2    borrow::Cow,
3    collections::HashMap,
4    sync::{Arc, Mutex},
5};
6
7use typst::{
8    diag::FileResult,
9    foundations::Bytes,
10    syntax::{FileId, Source},
11};
12
13use crate::file_resolver::FileResolver;
14
15pub struct CachedFileResolver<T> {
16    pub file_resolver: T,
17    pub in_memory_source_cache: Option<Arc<Mutex<HashMap<FileId, Source>>>>,
18    pub in_memory_binary_cache: Option<Arc<Mutex<HashMap<FileId, Bytes>>>>,
19}
20
21impl<T> CachedFileResolver<T> {
22    pub fn new(file_resolver: T) -> Self {
23        CachedFileResolver {
24            file_resolver,
25            in_memory_source_cache: None,
26            in_memory_binary_cache: None,
27        }
28    }
29
30    pub fn with_in_memory_source_cache(self) -> Self {
31        Self {
32            in_memory_source_cache: Some(Default::default()),
33            ..self
34        }
35    }
36
37    pub fn with_in_memory_binary_cache(self) -> Self {
38        Self {
39            in_memory_binary_cache: Some(Default::default()),
40            ..self
41        }
42    }
43}
44
45impl<T> FileResolver for CachedFileResolver<T>
46where
47    T: FileResolver,
48{
49    fn resolve_binary(&self, id: FileId) -> FileResult<Cow<'_, Bytes>> {
50        let Self {
51            in_memory_binary_cache,
52            ..
53        } = self;
54
55        if let Some(in_memory_binary_cache) = in_memory_binary_cache
56            && let Ok(in_memory_binary_cache) = in_memory_binary_cache.lock()
57            && let Some(cached) = in_memory_binary_cache.get(&id)
58        {
59            return Ok(Cow::Owned(cached.clone()));
60        }
61        let resolved = self.file_resolver.resolve_binary(id)?;
62        if let Some(in_memory_binary_cache) = in_memory_binary_cache
63            && let Ok(mut in_memory_binary_cache) = in_memory_binary_cache.lock()
64        {
65            in_memory_binary_cache.insert(id, resolved.as_ref().clone());
66        }
67        Ok(resolved)
68    }
69
70    fn resolve_source(&self, id: FileId) -> FileResult<Cow<'_, Source>> {
71        let Self {
72            in_memory_source_cache,
73            ..
74        } = self;
75
76        if let Some(in_memory_source_cache) = in_memory_source_cache
77            && let Ok(in_memory_source_cache) = in_memory_source_cache.lock()
78            && let Some(cached) = in_memory_source_cache.get(&id)
79        {
80            return Ok(Cow::Owned(cached.clone()));
81        }
82        let resolved = self.file_resolver.resolve_source(id)?;
83        if let Some(in_memory_source_cache) = in_memory_source_cache
84            && let Ok(mut in_memory_source_cache) = in_memory_source_cache.lock()
85        {
86            in_memory_source_cache.insert(id, resolved.as_ref().clone());
87        }
88        Ok(resolved)
89    }
90}
91
92pub trait IntoCachedFileResolver {
93    fn into_cached(self) -> CachedFileResolver<Self>
94    where
95        Self: Sized;
96}