Skip to main content

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
15/// Wraps a file resolver with in-memory caching.
16pub struct CachedFileResolver<T> {
17    /// The underlying file resolver.
18    pub file_resolver: T,
19    /// Optional cache for source files.
20    pub in_memory_source_cache: Option<Arc<Mutex<HashMap<FileId, Source>>>>,
21    /// Optional cache for binary files.
22    pub in_memory_binary_cache: Option<Arc<Mutex<HashMap<FileId, Bytes>>>>,
23}
24
25impl<T> CachedFileResolver<T> {
26    /// Creates a new cached file resolver wrapping the given resolver.
27    pub fn new(file_resolver: T) -> Self {
28        CachedFileResolver {
29            file_resolver,
30            in_memory_source_cache: None,
31            in_memory_binary_cache: None,
32        }
33    }
34
35    /// Enables in-memory caching for source files.
36    pub fn with_in_memory_source_cache(self) -> Self {
37        Self {
38            in_memory_source_cache: Some(Default::default()),
39            ..self
40        }
41    }
42
43    /// Enables in-memory caching for binary files.
44    pub fn with_in_memory_binary_cache(self) -> Self {
45        Self {
46            in_memory_binary_cache: Some(Default::default()),
47            ..self
48        }
49    }
50}
51
52impl<T> FileResolver for CachedFileResolver<T>
53where
54    T: FileResolver,
55{
56    fn resolve_binary(&self, id: FileId) -> FileResult<Cow<'_, Bytes>> {
57        let Self {
58            in_memory_binary_cache,
59            ..
60        } = self;
61
62        if let Some(in_memory_binary_cache) = in_memory_binary_cache
63            && let Ok(in_memory_binary_cache) = in_memory_binary_cache.lock()
64            && let Some(cached) = in_memory_binary_cache.get(&id)
65        {
66            return Ok(Cow::Owned(cached.clone()));
67        }
68        let resolved = self.file_resolver.resolve_binary(id)?;
69        if let Some(in_memory_binary_cache) = in_memory_binary_cache
70            && let Ok(mut in_memory_binary_cache) = in_memory_binary_cache.lock()
71        {
72            in_memory_binary_cache.insert(id, resolved.as_ref().clone());
73        }
74        Ok(resolved)
75    }
76
77    fn resolve_source(&self, id: FileId) -> FileResult<Cow<'_, Source>> {
78        let Self {
79            in_memory_source_cache,
80            ..
81        } = self;
82
83        if let Some(in_memory_source_cache) = in_memory_source_cache
84            && let Ok(in_memory_source_cache) = in_memory_source_cache.lock()
85            && let Some(cached) = in_memory_source_cache.get(&id)
86        {
87            return Ok(Cow::Owned(cached.clone()));
88        }
89        let resolved = self.file_resolver.resolve_source(id)?;
90        if let Some(in_memory_source_cache) = in_memory_source_cache
91            && let Ok(mut in_memory_source_cache) = in_memory_source_cache.lock()
92        {
93            in_memory_source_cache.insert(id, resolved.as_ref().clone());
94        }
95        Ok(resolved)
96    }
97}
98
99/// Trait for converting a file resolver into a cached version.
100pub trait IntoCachedFileResolver {
101    /// Wraps this resolver with caching.
102    fn into_cached(self) -> CachedFileResolver<Self>
103    where
104        Self: Sized;
105}