1#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14use std::{
15 path::PathBuf,
16 sync::atomic::{AtomicBool, Ordering},
17};
18use tectonic_bundles::{detect_bundle, Bundle};
19use tectonic_io_base::app_dirs;
20
21use crate::errors::{ErrorKind, Result};
22
23static CONFIG_TEST_MODE_ACTIVATED: AtomicBool = AtomicBool::new(false);
30
31#[doc(hidden)]
32pub fn activate_config_test_mode(forced: bool) {
33 CONFIG_TEST_MODE_ACTIVATED.store(forced, Ordering::SeqCst);
34}
35
36#[doc(hidden)]
37pub fn is_config_test_mode_activated() -> bool {
38 CONFIG_TEST_MODE_ACTIVATED.load(Ordering::SeqCst)
39}
40
41#[doc(hidden)]
42pub fn is_test_bundle_wanted(bundle: Option<String>) -> bool {
43 if !is_config_test_mode_activated() {
44 return false;
45 }
46 match bundle {
47 None => true,
48 Some(x) if x.contains("test-bundle://") => true,
49 _ => false,
50 }
51}
52
53#[doc(hidden)]
54pub fn maybe_return_test_bundle(bundle: Option<String>) -> Result<Box<dyn Bundle>> {
55 if is_test_bundle_wanted(bundle) {
56 Ok(Box::<crate::test_util::TestBundle>::default())
57 } else {
58 Err(ErrorKind::Msg("not asking for the default test bundle".to_owned()).into())
59 }
60}
61
62#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
64pub struct PersistentConfig {
65 default_bundles: Vec<BundleInfo>,
66}
67
68#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
70pub struct BundleInfo {
71 url: String,
72}
73
74impl PersistentConfig {
75 #[cfg(feature = "serialization")]
76 pub fn open(auto_create_config_file: bool) -> Result<PersistentConfig> {
86 use std::{
87 fs::File,
88 io::{ErrorKind as IoErrorKind, Read, Write},
89 };
90
91 let mut cfg_path = if auto_create_config_file {
92 app_dirs::ensure_user_config()?
93 } else {
94 app_dirs::get_user_config()?
95 };
96 cfg_path.push("config.toml");
97
98 let config = match File::open(&cfg_path) {
99 Ok(mut f) => {
100 let mut buf = String::new();
101 f.read_to_string(&mut buf)?;
102 toml::from_str(&buf)?
103 }
104 Err(e) => {
105 if e.kind() == IoErrorKind::NotFound {
106 let config = PersistentConfig::default();
108 if auto_create_config_file {
109 let mut f = File::create(&cfg_path)?;
110 write!(f, "{}", toml::to_string(&config)?)?;
111 }
112 config
113 } else {
114 return Err(e.into());
116 }
117 }
118 };
119
120 Ok(config)
121 }
122
123 #[cfg(not(feature = "serialization"))]
124 pub fn open(_auto_create_config_file: bool) -> Result<PersistentConfig> {
132 Ok(PersistentConfig::default())
133 }
134
135 pub fn default_bundle_loc(&self) -> &str {
137 &self.default_bundles[0].url
138 }
139
140 pub fn default_bundle(&self, only_cached: bool) -> Result<Box<dyn Bundle>> {
142 if CONFIG_TEST_MODE_ACTIVATED.load(Ordering::SeqCst) {
143 let bundle = crate::test_util::TestBundle::default();
144 return Ok(Box::new(bundle));
145 }
146
147 if self.default_bundles.len() != 1 {
148 return Err(ErrorKind::Msg(
149 "exactly one default_bundle item must be specified (for now)".to_owned(),
150 )
151 .into());
152 }
153
154 Ok(
155 detect_bundle(self.default_bundles[0].url.to_owned(), only_cached, None)
156 .unwrap()
157 .unwrap(),
158 )
159 }
160
161 pub fn format_cache_path(&self) -> Result<PathBuf> {
163 if is_config_test_mode_activated() {
164 Ok(crate::test_util::test_path(&[]))
165 } else {
166 Ok(app_dirs::get_user_cache_dir("formats")?)
167 }
168 }
169}
170
171impl Default for PersistentConfig {
172 fn default() -> Self {
173 let url = tectonic_bundles::get_fallback_bundle_url(tectonic_engine_xetex::FORMAT_SERIAL);
174
175 PersistentConfig {
176 default_bundles: vec![BundleInfo { url }],
177 }
178 }
179}