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}