tinymist_world/
system.rs

1use std::{borrow::Cow, sync::Arc};
2
3use tinymist_std::{error::prelude::*, ImmutPath};
4use tinymist_vfs::{system::SystemAccessModel, ImmutDict, Vfs};
5use typst::{utils::LazyHash, Features};
6
7use crate::{
8    args::{CompileFontArgs, CompilePackageArgs},
9    config::{CompileFontOpts, CompileOpts},
10    font::{system::SystemFontSearcher, FontResolverImpl},
11    package::{registry::HttpRegistry, RegistryPathMapper},
12    EntryState,
13};
14
15mod diag;
16pub use diag::*;
17
18/// type trait of [`TypstSystemWorld`].
19#[derive(Debug, Clone, Copy)]
20pub struct SystemCompilerFeat;
21
22impl crate::CompilerFeat for SystemCompilerFeat {
23    /// Uses [`FontResolverImpl`] directly.
24    type FontResolver = FontResolverImpl;
25    /// It accesses a physical file system.
26    type AccessModel = SystemAccessModel;
27    /// It performs native HTTP requests for fetching package data.
28    type Registry = HttpRegistry;
29}
30
31/// The compiler universe in system environment.
32pub type TypstSystemUniverse = crate::world::CompilerUniverse<SystemCompilerFeat>;
33/// The compiler world in system environment.
34pub type TypstSystemWorld = crate::world::CompilerWorld<SystemCompilerFeat>;
35/// The compute graph in system environment.
36pub type SystemWorldComputeGraph = crate::WorldComputeGraph<SystemCompilerFeat>;
37
38impl TypstSystemUniverse {
39    /// Create [`TypstSystemWorld`] with the given options.
40    /// See SystemCompilerFeat for instantiation details.
41    /// See [`CompileOpts`] for available options.
42    pub fn new(mut opts: CompileOpts) -> Result<Self> {
43        let registry: Arc<HttpRegistry> = Arc::default();
44        let resolver = Arc::new(RegistryPathMapper::new(registry.clone()));
45        let inputs = std::mem::take(&mut opts.inputs);
46
47        // todo: enable html
48        Ok(Self::new_raw(
49            opts.entry.clone().try_into()?,
50            Features::default(),
51            Some(Arc::new(LazyHash::new(inputs))),
52            Vfs::new(resolver, SystemAccessModel {}),
53            registry,
54            Arc::new(Self::resolve_fonts(opts)?),
55        ))
56    }
57
58    /// Resolve fonts from given options.
59    fn resolve_fonts(opts: CompileOpts) -> Result<FontResolverImpl> {
60        let mut searcher = SystemFontSearcher::new();
61        searcher.resolve_opts(opts.into())?;
62        Ok(searcher.build())
63    }
64}
65
66/// Builders for Typst universe.
67pub struct SystemUniverseBuilder;
68
69impl SystemUniverseBuilder {
70    /// Create [`TypstSystemUniverse`] with the given options.
71    /// See [`LspCompilerFeat`] for instantiation details.
72    pub fn build(
73        entry: EntryState,
74        inputs: ImmutDict,
75        font_resolver: Arc<FontResolverImpl>,
76        package_registry: HttpRegistry,
77    ) -> TypstSystemUniverse {
78        let registry = Arc::new(package_registry);
79        let resolver = Arc::new(RegistryPathMapper::new(registry.clone()));
80
81        // todo: enable html
82        TypstSystemUniverse::new_raw(
83            entry,
84            Features::default(),
85            Some(inputs),
86            Vfs::new(resolver, SystemAccessModel {}),
87            registry,
88            font_resolver,
89        )
90    }
91
92    /// Resolve fonts from given options.
93    pub fn resolve_fonts(args: CompileFontArgs) -> Result<FontResolverImpl> {
94        let mut searcher = SystemFontSearcher::new();
95        searcher.resolve_opts(CompileFontOpts {
96            font_paths: args.font_paths,
97            no_system_fonts: args.ignore_system_fonts,
98            with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(),
99        })?;
100        Ok(searcher.build())
101    }
102
103    /// Resolve package registry from given options.
104    pub fn resolve_package(
105        cert_path: Option<ImmutPath>,
106        args: Option<&CompilePackageArgs>,
107    ) -> HttpRegistry {
108        HttpRegistry::new(
109            cert_path,
110            args.and_then(|args| Some(args.package_path.clone()?.into())),
111            args.and_then(|args| Some(args.package_cache_path.clone()?.into())),
112        )
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use std::sync::atomic::AtomicBool;
119
120    use super::*;
121    use clap::Parser;
122
123    use crate::args::CompileOnceArgs;
124    use crate::{CompileSnapshot, WorldComputable, WorldComputeGraph};
125
126    #[test]
127    fn test_args() {
128        use tinymist_std::typst::TypstPagedDocument;
129
130        let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]);
131        let verse = args
132            .resolve_system()
133            .expect("failed to resolve system universe");
134
135        let world = verse.snapshot();
136        let _res = typst::compile::<TypstPagedDocument>(&world);
137    }
138
139    static FONT_COMPUTED: AtomicBool = AtomicBool::new(false);
140
141    pub struct FontsOnce {
142        fonts: Arc<FontResolverImpl>,
143    }
144
145    impl WorldComputable<SystemCompilerFeat> for FontsOnce {
146        type Output = Self;
147
148        fn compute(graph: &Arc<WorldComputeGraph<SystemCompilerFeat>>) -> Result<Self> {
149            // Ensure that this function is only called once.
150            if FONT_COMPUTED.swap(true, std::sync::atomic::Ordering::SeqCst) {
151                bail!("font already computed");
152            }
153
154            Ok(Self {
155                fonts: graph.snap.world.font_resolver.clone(),
156            })
157        }
158    }
159
160    #[test]
161    fn compute_system_fonts() {
162        let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]);
163        let verse = args
164            .resolve_system()
165            .expect("failed to resolve system universe");
166
167        let snap = CompileSnapshot::from_world(verse.snapshot());
168
169        let graph = WorldComputeGraph::new(snap);
170
171        let font = graph.compute::<FontsOnce>().expect("font").fonts.clone();
172        let _ = font;
173
174        let font = graph.compute::<FontsOnce>().expect("font").fonts.clone();
175        let _ = font;
176
177        assert!(FONT_COMPUTED.load(std::sync::atomic::Ordering::SeqCst));
178    }
179}