tinymist_world/
system.rs

1use std::{borrow::Cow, sync::Arc};
2
3use tinymist_std::{ImmutPath, error::prelude::*};
4use tinymist_vfs::{ImmutDict, Vfs, system::SystemAccessModel};
5use typst::{Features, utils::LazyHash};
6
7use crate::{
8    EntryState,
9    args::{CompileFontArgs, CompilePackageArgs},
10    config::{CompileFontOpts, CompileOpts},
11    font::{FontResolverImpl, system::SystemFontSearcher},
12    package::{RegistryPathMapper, registry::HttpRegistry},
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        let timestamp = opts.creation_timestamp;
47
48        // todo: enable html
49        Ok(Self::new_raw(
50            opts.entry.clone().try_into()?,
51            Features::default(),
52            Some(Arc::new(LazyHash::new(inputs))),
53            Vfs::new(resolver, SystemAccessModel {}),
54            registry,
55            Arc::new(Self::resolve_fonts(opts)?),
56            timestamp,
57        ))
58    }
59
60    /// Resolve fonts from given options.
61    fn resolve_fonts(opts: CompileOpts) -> Result<FontResolverImpl> {
62        let mut searcher = SystemFontSearcher::new();
63        searcher.resolve_opts(opts.into())?;
64        Ok(searcher.build())
65    }
66}
67
68/// Builders for Typst universe.
69pub struct SystemUniverseBuilder;
70
71impl SystemUniverseBuilder {
72    /// Create [`TypstSystemUniverse`] with the given options.
73    /// See [`SystemCompilerFeat`] for instantiation details.
74    pub fn build(
75        entry: EntryState,
76        inputs: ImmutDict,
77        font_resolver: Arc<FontResolverImpl>,
78        package_registry: HttpRegistry,
79    ) -> TypstSystemUniverse {
80        let registry = Arc::new(package_registry);
81        let resolver = Arc::new(RegistryPathMapper::new(registry.clone()));
82
83        // todo: enable html
84        TypstSystemUniverse::new_raw(
85            entry,
86            Features::default(),
87            Some(inputs),
88            Vfs::new(resolver, SystemAccessModel {}),
89            registry,
90            font_resolver,
91            None, // creation_timestamp - not used in this context
92        )
93    }
94
95    /// Resolve fonts from given options.
96    pub fn resolve_fonts(args: CompileFontArgs) -> Result<FontResolverImpl> {
97        let mut searcher = SystemFontSearcher::new();
98        searcher.resolve_opts(CompileFontOpts {
99            font_paths: args.font_paths,
100            no_system_fonts: args.ignore_system_fonts,
101            with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(),
102        })?;
103        Ok(searcher.build())
104    }
105
106    /// Resolve package registry from given options.
107    pub fn resolve_package(
108        cert_path: Option<ImmutPath>,
109        args: Option<&CompilePackageArgs>,
110    ) -> HttpRegistry {
111        HttpRegistry::new(
112            cert_path,
113            args.and_then(|args| Some(args.package_path.clone()?.into())),
114            args.and_then(|args| Some(args.package_cache_path.clone()?.into())),
115        )
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use std::sync::atomic::AtomicBool;
122
123    use super::*;
124    use clap::Parser;
125
126    use crate::args::CompileOnceArgs;
127    use crate::{CompileSnapshot, WorldComputable, WorldComputeGraph};
128
129    #[test]
130    fn test_args() {
131        use tinymist_std::typst::TypstPagedDocument;
132
133        let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]);
134        let verse = args
135            .resolve_system()
136            .expect("failed to resolve system universe");
137
138        let world = verse.snapshot();
139        let _res = typst::compile::<TypstPagedDocument>(&world);
140    }
141
142    static FONT_COMPUTED: AtomicBool = AtomicBool::new(false);
143
144    pub struct FontsOnce {
145        fonts: Arc<FontResolverImpl>,
146    }
147
148    impl WorldComputable<SystemCompilerFeat> for FontsOnce {
149        type Output = Self;
150
151        fn compute(graph: &Arc<WorldComputeGraph<SystemCompilerFeat>>) -> Result<Self> {
152            // Ensure that this function is only called once.
153            if FONT_COMPUTED.swap(true, std::sync::atomic::Ordering::SeqCst) {
154                bail!("font already computed");
155            }
156
157            Ok(Self {
158                fonts: graph.snap.world.font_resolver.clone(),
159            })
160        }
161    }
162
163    #[test]
164    fn compute_system_fonts() {
165        let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]);
166        let verse = args
167            .resolve_system()
168            .expect("failed to resolve system universe");
169
170        let snap = CompileSnapshot::from_world(verse.snapshot());
171
172        let graph = WorldComputeGraph::new(snap);
173
174        let font = graph.compute::<FontsOnce>().expect("font").fonts.clone();
175        let _ = font;
176
177        let font = graph.compute::<FontsOnce>().expect("font").fonts.clone();
178        let _ = font;
179
180        assert!(FONT_COMPUTED.load(std::sync::atomic::Ordering::SeqCst));
181    }
182}