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 }, 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}