typst_batch/
config.rs

1//! Configuration for typst-batch.
2//!
3//! This module provides runtime configuration for package downloads.
4//! Use [`ConfigBuilder`] at application startup to configure the User-Agent string.
5
6use std::sync::OnceLock;
7
8use typst_kit::download::Downloader;
9use typst_kit::package::PackageStorage;
10
11/// Global configuration, initialized via [`init`].
12static CONFIG: OnceLock<Config> = OnceLock::new();
13
14/// Runtime configuration for typst-batch.
15#[derive(Debug, Clone)]
16pub struct Config {
17    /// User-Agent string for package downloads from the Typst registry.
18    /// Example: "my-app/1.0.0"
19    pub user_agent: String,
20}
21
22impl Default for Config {
23    fn default() -> Self {
24        Self {
25            user_agent: concat!("typst-batch/", env!("CARGO_PKG_VERSION")).to_string(),
26        }
27    }
28}
29
30/// Configuration builder for fluent API.
31#[derive(Debug, Clone, Default)]
32pub struct ConfigBuilder {
33    user_agent: Option<String>,
34}
35
36impl ConfigBuilder {
37    /// Create a new configuration builder.
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Set the User-Agent string for package downloads.
43    ///
44    /// Default: "typst-batch/{version}"
45    ///
46    /// # Example
47    ///
48    /// ```
49    /// use typst_batch::config::ConfigBuilder;
50    ///
51    /// ConfigBuilder::new()
52    ///     .user_agent("my-app/1.0.0")
53    ///     .init();
54    /// ```
55    pub fn user_agent(mut self, agent: impl Into<String>) -> Self {
56        self.user_agent = Some(agent.into());
57        self
58    }
59
60    /// Build and initialize the global configuration.
61    ///
62    /// This can only be called once. Subsequent calls are ignored.
63    /// Returns `true` if configuration was set, `false` if already initialized.
64    pub fn init(self) -> bool {
65        let config = Config {
66            user_agent: self
67                .user_agent
68                .unwrap_or_else(|| Config::default().user_agent),
69        };
70        CONFIG.set(config).is_ok()
71    }
72}
73
74/// Initialize typst-batch with default configuration.
75///
76/// This is equivalent to `ConfigBuilder::new().init()`.
77pub fn init_default() -> bool {
78    ConfigBuilder::new().init()
79}
80
81/// Get the current configuration, or default if not initialized.
82pub fn get() -> &'static Config {
83    CONFIG.get_or_init(Config::default)
84}
85
86/// Global shared package storage - one cache for all compilations.
87///
88/// Uses the configured User-Agent string. If not configured, uses default.
89pub static PACKAGE_STORAGE: OnceLock<PackageStorage> = OnceLock::new();
90
91/// Get or initialize the global package storage.
92pub fn package_storage() -> &'static PackageStorage {
93    PACKAGE_STORAGE.get_or_init(|| {
94        let config = get();
95        PackageStorage::new(
96            None, // Use default cache path
97            None, // Use default package path
98            Downloader::new(config.user_agent.clone()),
99        )
100    })
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_default_config() {
109        let config = Config::default();
110        assert!(config.user_agent.starts_with("typst-batch/"));
111    }
112
113    #[test]
114    fn test_builder() {
115        let builder = ConfigBuilder::new().user_agent("test/1.0");
116        assert_eq!(builder.user_agent, Some("test/1.0".to_string()));
117    }
118}