Skip to main content

flams_utils/
settings.rs

1use std::{
2    ops::{Add, AddAssign},
3    path::{Path, PathBuf},
4};
5
6#[derive(Debug, Default, Clone)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct SettingsSpec {
9    #[cfg_attr(feature = "serde", serde(default))]
10    pub mathhubs: Vec<PathBuf>,
11    #[cfg_attr(feature = "serde", serde(default))]
12    pub debug: Option<bool>,
13    #[cfg_attr(feature = "serde", serde(default))]
14    pub server: ServerSettings,
15    #[cfg_attr(feature = "serde", serde(default))]
16    pub log_dir: Option<Box<Path>>,
17    #[cfg_attr(feature = "serde", serde(default))]
18    pub temp_dir: Option<Box<Path>>,
19    #[cfg_attr(feature = "serde", serde(default))]
20    pub embedding_dir: Option<Box<Path>>,
21    #[cfg_attr(feature = "serde", serde(default))]
22    pub buildqueue: BuildQueueSettings,
23    #[cfg_attr(feature = "serde", serde(skip))]
24    pub lsp: bool,
25    #[cfg_attr(feature = "serde", serde(default))]
26    pub database: Option<Box<Path>>,
27    #[cfg_attr(feature = "serde", serde(default))]
28    pub rdf_database: Option<Box<Path>>,
29    #[cfg_attr(feature = "serde", serde(default))]
30    pub gitlab: GitlabSettings,
31    #[cfg_attr(feature = "serde", serde(default))]
32    pub stack_size: Option<u8>,
33    #[cfg_attr(feature = "serde", serde(default))]
34    pub remotes: rustc_hash::FxHashMap<Box<str>, url::Url>,
35    #[cfg_attr(feature = "serde", serde(default))]
36    pub fonts: rustc_hash::FxHashMap<Box<str>, Box<str>>,
37}
38impl Add for SettingsSpec {
39    type Output = Self;
40    fn add(mut self, rhs: Self) -> Self::Output {
41        for (k, v) in rhs.remotes {
42            self.remotes.insert(k, v);
43        }
44        for (k, v) in rhs.fonts {
45            self.fonts.insert(k, v);
46        }
47        Self {
48            mathhubs: if self.mathhubs.is_empty() {
49                rhs.mathhubs
50            } else {
51                self.mathhubs
52            }, //self.mathhubs.into_iter().chain(rhs.mathhubs.into_iter()).collect(),
53            debug: self.debug.or(rhs.debug),
54            server: self.server + rhs.server,
55            log_dir: self.log_dir.or(rhs.log_dir),
56            temp_dir: self.temp_dir.or(rhs.temp_dir),
57            database: self.database.or(rhs.database),
58            rdf_database: self.rdf_database.or(rhs.rdf_database),
59            embedding_dir: self.embedding_dir.or(rhs.embedding_dir),
60            stack_size: self.stack_size.or(rhs.stack_size),
61            buildqueue: self.buildqueue + rhs.buildqueue,
62            gitlab: self.gitlab + rhs.gitlab,
63            lsp: self.lsp || rhs.lsp,
64            remotes: self.remotes,
65            fonts: self.fonts,
66        }
67    }
68}
69impl AddAssign for SettingsSpec {
70    fn add_assign(&mut self, rhs: Self) {
71        for (k, v) in rhs.remotes {
72            self.remotes.insert(k, v);
73        }
74        for (k, v) in rhs.fonts {
75            self.fonts.insert(k, v);
76        }
77        if self.mathhubs.is_empty() {
78            self.mathhubs = rhs.mathhubs;
79        }
80        self.debug = self.debug.or(rhs.debug);
81        self.lsp = self.lsp || rhs.lsp;
82        self.server += rhs.server;
83        if self.log_dir.is_none() {
84            self.log_dir = rhs.log_dir;
85        }
86        if self.stack_size.is_none() {
87            self.stack_size = rhs.stack_size;
88        }
89        if self.temp_dir.is_none() {
90            self.temp_dir = rhs.temp_dir;
91        }
92        if self.embedding_dir.is_none() {
93            self.embedding_dir = rhs.embedding_dir;
94        }
95        if self.database.is_none() {
96            self.database = rhs.database;
97        }
98        if self.rdf_database.is_none() {
99            self.rdf_database = rhs.rdf_database;
100        }
101        self.gitlab += rhs.gitlab;
102        self.buildqueue += rhs.buildqueue;
103    }
104}
105
106impl SettingsSpec {
107    #[allow(clippy::missing_panics_doc)]
108    #[must_use]
109    pub fn from_envs() -> Self {
110        Self {
111            mathhubs: Vec::new(),
112            debug: std::env::var("FLAMS_DEBUG")
113                .ok()
114                .and_then(|s| s.parse().ok()),
115            log_dir: std::env::var("FLAMS_LOG_DIR")
116                .ok()
117                .map(|s| PathBuf::from(s).into_boxed_path()),
118            temp_dir: std::env::var("FLAMS_TEMP_DIR")
119                .ok()
120                .map(|s| PathBuf::from(s).into_boxed_path()),
121            embedding_dir: std::env::var("FLAMS_EMBEDDING_DIR")
122                .ok()
123                .map(|s| PathBuf::from(s).into_boxed_path()),
124            database: std::env::var("FLAMS_DATABASE")
125                .ok()
126                .map(|s| PathBuf::from(s).into_boxed_path()),
127            rdf_database: std::env::var("FLAMS_RDF_DATABASE")
128                .ok()
129                .map(|s| PathBuf::from(s).into_boxed_path()),
130            stack_size: std::env::var("FLAMS_STACK_SIZE").ok().map(|s| {
131                s.parse().expect(
132                    "Could not parse stack size parameter (environment variable FLAMS_STACK_SIZE)",
133                )
134            }),
135            server: ServerSettings {
136                port: std::env::var("FLAMS_PORT")
137                    .ok()
138                    .and_then(|s| s.parse().ok())
139                    .unwrap_or_default(),
140                ip: std::env::var("FLAMS_IP").ok().map(|s| {
141                    s.parse()
142                        .expect("Could not parse IP address (environment variable FLAMS_IP)")
143                }),
144                external_url: std::env::var("FLAMS_EXTERNAL_URL").ok(),
145                admin_pwd: std::env::var("FLAMS_ADMIN_PWD").ok(),
146            },
147            buildqueue: BuildQueueSettings {
148                num_threads: std::env::var("FLAMS_NUM_THREADS")
149                    .ok()
150                    .and_then(|s| s.parse().ok()),
151            },
152            gitlab: GitlabSettings {
153                url: std::env::var("FLAMS_GITLAB_URL").ok().map(|s| {
154                    s.parse()
155                        .expect("Could not parse URL (environment variable FLAMS_GITLAB_URL)")
156                }),
157                token: std::env::var("FLAMS_GITLAB_TOKEN").ok().map(Into::into),
158                app_id: std::env::var("FLAMS_GITLAB_APP_ID").ok().map(Into::into),
159                app_secret: std::env::var("FLAMS_GITLAB_APP_SECRET")
160                    .ok()
161                    .map(Into::into),
162                redirect_url: std::env::var("FLAMS_GITLAB_REDIRECT_URL")
163                    .ok()
164                    .map(Into::into),
165            },
166            lsp: false,
167            remotes: rustc_hash::FxHashMap::default(),
168            fonts: rustc_hash::FxHashMap::default(),
169        }
170    }
171}
172
173#[derive(Debug, Default, Clone)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175pub struct ServerSettings {
176    #[cfg_attr(feature = "serde", serde(default))]
177    pub port: u16,
178    #[cfg_attr(feature = "serde", serde(default))]
179    pub ip: Option<std::net::IpAddr>,
180    #[cfg_attr(feature = "serde", serde(default))]
181    pub admin_pwd: Option<String>,
182    #[cfg_attr(feature = "serde", serde(default))]
183    pub external_url: Option<String>,
184}
185impl Add for ServerSettings {
186    type Output = Self;
187    fn add(self, rhs: Self) -> Self::Output {
188        Self {
189            port: if self.port == 0 { rhs.port } else { self.port },
190            ip: self.ip.or(rhs.ip),
191            admin_pwd: self.admin_pwd.or(rhs.admin_pwd),
192            external_url: self.external_url.or(rhs.external_url),
193        }
194    }
195}
196impl AddAssign for ServerSettings {
197    fn add_assign(&mut self, rhs: Self) {
198        if self.port == 0 {
199            self.port = rhs.port;
200        }
201        if self.ip.is_none() {
202            self.ip = rhs.ip;
203        }
204        if self.admin_pwd.is_none() {
205            self.admin_pwd = rhs.admin_pwd;
206        }
207        if self.external_url.is_none() {
208            self.external_url = rhs.external_url;
209        }
210    }
211}
212
213#[derive(Debug, Default, Clone)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
215pub struct BuildQueueSettings {
216    #[cfg_attr(feature = "serde", serde(default))]
217    pub num_threads: Option<u8>,
218}
219impl Add for BuildQueueSettings {
220    type Output = Self;
221    fn add(self, rhs: Self) -> Self::Output {
222        Self {
223            num_threads: self.num_threads.or(rhs.num_threads),
224        }
225    }
226}
227impl AddAssign for BuildQueueSettings {
228    fn add_assign(&mut self, rhs: Self) {
229        if self.num_threads.is_none() {
230            self.num_threads = rhs.num_threads;
231        }
232    }
233}
234
235#[derive(Debug, Default, Clone)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237pub struct GitlabSettings {
238    #[cfg_attr(feature = "serde", serde(default))]
239    pub url: Option<url::Url>,
240    #[cfg_attr(feature = "serde", serde(default))]
241    pub token: Option<Box<str>>,
242    #[cfg_attr(feature = "serde", serde(default))]
243    pub app_id: Option<Box<str>>,
244    #[cfg_attr(feature = "serde", serde(default))]
245    pub app_secret: Option<Box<str>>,
246    #[cfg_attr(feature = "serde", serde(default))]
247    pub redirect_url: Option<Box<str>>,
248}
249
250impl Add for GitlabSettings {
251    type Output = Self;
252    fn add(self, rhs: Self) -> Self::Output {
253        Self {
254            url: self.url.or(rhs.url),
255            token: self.token.or(rhs.token),
256            app_id: self.app_id.or(rhs.app_id),
257            app_secret: self.app_secret.or(rhs.app_secret),
258            redirect_url: self.redirect_url.or(rhs.redirect_url),
259        }
260    }
261}
262impl AddAssign for GitlabSettings {
263    fn add_assign(&mut self, rhs: Self) {
264        if self.url.is_none() {
265            self.url = rhs.url;
266        }
267        if self.token.is_none() {
268            self.token = rhs.token;
269        }
270        if self.app_id.is_none() {
271            self.app_id = rhs.app_id;
272        }
273        if self.app_secret.is_none() {
274            self.app_secret = rhs.app_secret;
275        }
276        if self.redirect_url.is_none() {
277            self.redirect_url = rhs.redirect_url;
278        }
279    }
280}