torrust_tracker_configuration/
lib.rs1pub mod v2_0_0;
8pub mod validator;
9
10use std::collections::HashMap;
11use std::env;
12use std::sync::Arc;
13use std::time::Duration;
14
15use camino::Utf8PathBuf;
16use derive_more::{Constructor, Display};
17use serde::{Deserialize, Serialize};
18use serde_with::serde_as;
19use thiserror::Error;
20use torrust_tracker_located_error::{DynError, LocatedError};
21
22pub const TORRENT_PEERS_LIMIT: usize = 74;
24
25pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
28
29const ENV_VAR_CONFIG_TOML: &str = "TORRUST_TRACKER_CONFIG_TOML";
34
35pub const ENV_VAR_CONFIG_TOML_PATH: &str = "TORRUST_TRACKER_CONFIG_TOML_PATH";
37
38pub type Configuration = v2_0_0::Configuration;
39pub type Core = v2_0_0::core::Core;
40pub type HealthCheckApi = v2_0_0::health_check_api::HealthCheckApi;
41pub type HttpApi = v2_0_0::tracker_api::HttpApi;
42pub type HttpTracker = v2_0_0::http_tracker::HttpTracker;
43pub type UdpTracker = v2_0_0::udp_tracker::UdpTracker;
44pub type Database = v2_0_0::database::Database;
45pub type Driver = v2_0_0::database::Driver;
46pub type Threshold = v2_0_0::logging::Threshold;
47
48pub type AccessTokens = HashMap<String, String>;
49
50pub const LATEST_VERSION: &str = "2.0.0";
51
52#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
54#[display("Metadata(app: {app}, purpose: {purpose}, schema_version: {schema_version})")]
55pub struct Metadata {
56 #[serde(default = "Metadata::default_app")]
58 app: App,
59
60 #[serde(default = "Metadata::default_purpose")]
62 purpose: Purpose,
63
64 #[serde(default = "Metadata::default_schema_version")]
66 #[serde(flatten)]
67 schema_version: Version,
68}
69
70impl Default for Metadata {
71 fn default() -> Self {
72 Self {
73 app: Self::default_app(),
74 purpose: Self::default_purpose(),
75 schema_version: Self::default_schema_version(),
76 }
77 }
78}
79
80impl Metadata {
81 fn default_app() -> App {
82 App::TorrustTracker
83 }
84
85 fn default_purpose() -> Purpose {
86 Purpose::Configuration
87 }
88
89 fn default_schema_version() -> Version {
90 Version::latest()
91 }
92}
93
94#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
95#[serde(rename_all = "kebab-case")]
96pub enum App {
97 TorrustTracker,
98}
99
100#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
101#[serde(rename_all = "lowercase")]
102pub enum Purpose {
103 Configuration,
104}
105
106#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
108#[serde(rename_all = "lowercase")]
109pub struct Version {
110 #[serde(default = "Version::default_semver")]
111 schema_version: String,
112}
113
114impl Default for Version {
115 fn default() -> Self {
116 Self {
117 schema_version: Self::default_semver(),
118 }
119 }
120}
121
122impl Version {
123 fn new(semver: &str) -> Self {
124 Self {
125 schema_version: semver.to_owned(),
126 }
127 }
128
129 fn latest() -> Self {
130 Self {
131 schema_version: LATEST_VERSION.to_string(),
132 }
133 }
134
135 fn default_semver() -> String {
136 LATEST_VERSION.to_string()
137 }
138}
139
140#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Constructor)]
141pub struct TrackerPolicy {
142 #[serde(default = "TrackerPolicy::default_max_peer_timeout")]
147 pub max_peer_timeout: u32,
148
149 #[serde(default = "TrackerPolicy::default_persistent_torrent_completed_stat")]
152 pub persistent_torrent_completed_stat: bool,
153
154 #[serde(default = "TrackerPolicy::default_remove_peerless_torrents")]
160 pub remove_peerless_torrents: bool,
161}
162
163impl Default for TrackerPolicy {
164 fn default() -> Self {
165 Self {
166 max_peer_timeout: Self::default_max_peer_timeout(),
167 persistent_torrent_completed_stat: Self::default_persistent_torrent_completed_stat(),
168 remove_peerless_torrents: Self::default_remove_peerless_torrents(),
169 }
170 }
171}
172
173impl TrackerPolicy {
174 fn default_max_peer_timeout() -> u32 {
175 900
176 }
177
178 fn default_persistent_torrent_completed_stat() -> bool {
179 false
180 }
181
182 fn default_remove_peerless_torrents() -> bool {
183 true
184 }
185}
186
187#[derive(Debug, Default, Clone)]
189pub struct Info {
190 config_toml: Option<String>,
191 config_toml_path: String,
192}
193
194impl Info {
195 #[allow(clippy::needless_pass_by_value)]
202 pub fn new(default_config_toml_path: String) -> Result<Self, Error> {
203 let env_var_config_toml = ENV_VAR_CONFIG_TOML.to_string();
204 let env_var_config_toml_path = ENV_VAR_CONFIG_TOML_PATH.to_string();
205
206 let config_toml = if let Ok(config_toml) = env::var(env_var_config_toml) {
207 println!("Loading extra configuration from environment variable:\n {config_toml}");
208 Some(config_toml)
209 } else {
210 None
211 };
212
213 let config_toml_path = if let Ok(config_toml_path) = env::var(env_var_config_toml_path) {
214 println!("Loading extra configuration from file: `{config_toml_path}` ...");
215 config_toml_path
216 } else {
217 println!("Loading extra configuration from default configuration file: `{default_config_toml_path}` ...");
218 default_config_toml_path
219 };
220
221 Ok(Self {
222 config_toml,
223 config_toml_path,
224 })
225 }
226}
227
228#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, Constructor)]
230pub struct AnnouncePolicy {
231 #[serde(default = "AnnouncePolicy::default_interval")]
243 pub interval: u32,
244
245 #[serde(default = "AnnouncePolicy::default_interval_min")]
257 pub interval_min: u32,
258}
259
260impl Default for AnnouncePolicy {
261 fn default() -> Self {
262 Self {
263 interval: Self::default_interval(),
264 interval_min: Self::default_interval_min(),
265 }
266 }
267}
268
269impl AnnouncePolicy {
270 fn default_interval() -> u32 {
271 120
272 }
273
274 fn default_interval_min() -> u32 {
275 120
276 }
277}
278
279#[derive(Error, Debug)]
281pub enum Error {
282 #[error("Unable to load from Environmental Variable: {source}")]
286 UnableToLoadFromEnvironmentVariable {
287 source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
288 },
289
290 #[error("Unable to load from Config File: {source}")]
291 UnableToLoadFromConfigFile {
292 source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
293 },
294
295 #[error("Failed processing the configuration: {source}")]
297 ConfigError {
298 source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
299 },
300
301 #[error("The error for errors that can never happen.")]
302 Infallible,
303
304 #[error("Unsupported configuration version: {version}")]
305 UnsupportedVersion { version: Version },
306
307 #[error("Missing mandatory configuration option. Option path: {path}")]
308 MissingMandatoryOption { path: String },
309}
310
311impl From<figment::Error> for Error {
312 #[track_caller]
313 fn from(err: figment::Error) -> Self {
314 Self::ConfigError {
315 source: (Arc::new(err) as DynError).into(),
316 }
317 }
318}
319
320#[serde_as]
321#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
322pub struct TslConfig {
323 #[serde(default = "TslConfig::default_ssl_cert_path")]
325 pub ssl_cert_path: Utf8PathBuf,
326
327 #[serde(default = "TslConfig::default_ssl_key_path")]
329 pub ssl_key_path: Utf8PathBuf,
330}
331
332impl TslConfig {
333 #[allow(clippy::unnecessary_wraps)]
334 fn default_ssl_cert_path() -> Utf8PathBuf {
335 Utf8PathBuf::new()
336 }
337
338 #[allow(clippy::unnecessary_wraps)]
339 fn default_ssl_key_path() -> Utf8PathBuf {
340 Utf8PathBuf::new()
341 }
342}