pub mod v2_0_0;
pub mod validator;
use std::collections::HashMap;
use std::env;
use std::sync::Arc;
use std::time::Duration;
use camino::Utf8PathBuf;
use derive_more::{Constructor, Display};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use thiserror::Error;
use torrust_tracker_located_error::{DynError, LocatedError};
pub const TORRENT_PEERS_LIMIT: usize = 74;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
const ENV_VAR_CONFIG_TOML: &str = "TORRUST_TRACKER_CONFIG_TOML";
pub const ENV_VAR_CONFIG_TOML_PATH: &str = "TORRUST_TRACKER_CONFIG_TOML_PATH";
pub type Configuration = v2_0_0::Configuration;
pub type Core = v2_0_0::core::Core;
pub type HealthCheckApi = v2_0_0::health_check_api::HealthCheckApi;
pub type HttpApi = v2_0_0::tracker_api::HttpApi;
pub type HttpTracker = v2_0_0::http_tracker::HttpTracker;
pub type UdpTracker = v2_0_0::udp_tracker::UdpTracker;
pub type Database = v2_0_0::database::Database;
pub type Driver = v2_0_0::database::Driver;
pub type Threshold = v2_0_0::logging::Threshold;
pub type AccessTokens = HashMap<String, String>;
pub const LATEST_VERSION: &str = "2.0.0";
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
#[display("Metadata(app: {app}, purpose: {purpose}, schema_version: {schema_version})")]
pub struct Metadata {
#[serde(default = "Metadata::default_app")]
app: App,
#[serde(default = "Metadata::default_purpose")]
purpose: Purpose,
#[serde(default = "Metadata::default_schema_version")]
#[serde(flatten)]
schema_version: Version,
}
impl Default for Metadata {
fn default() -> Self {
Self {
app: Self::default_app(),
purpose: Self::default_purpose(),
schema_version: Self::default_schema_version(),
}
}
}
impl Metadata {
fn default_app() -> App {
App::TorrustTracker
}
fn default_purpose() -> Purpose {
Purpose::Configuration
}
fn default_schema_version() -> Version {
Version::latest()
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
#[serde(rename_all = "kebab-case")]
pub enum App {
TorrustTracker,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
#[serde(rename_all = "lowercase")]
pub enum Purpose {
Configuration,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)]
#[serde(rename_all = "lowercase")]
pub struct Version {
#[serde(default = "Version::default_semver")]
schema_version: String,
}
impl Default for Version {
fn default() -> Self {
Self {
schema_version: Self::default_semver(),
}
}
}
impl Version {
fn new(semver: &str) -> Self {
Self {
schema_version: semver.to_owned(),
}
}
fn latest() -> Self {
Self {
schema_version: LATEST_VERSION.to_string(),
}
}
fn default_semver() -> String {
LATEST_VERSION.to_string()
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Constructor)]
pub struct TrackerPolicy {
#[serde(default = "TrackerPolicy::default_max_peer_timeout")]
pub max_peer_timeout: u32,
#[serde(default = "TrackerPolicy::default_persistent_torrent_completed_stat")]
pub persistent_torrent_completed_stat: bool,
#[serde(default = "TrackerPolicy::default_remove_peerless_torrents")]
pub remove_peerless_torrents: bool,
}
impl Default for TrackerPolicy {
fn default() -> Self {
Self {
max_peer_timeout: Self::default_max_peer_timeout(),
persistent_torrent_completed_stat: Self::default_persistent_torrent_completed_stat(),
remove_peerless_torrents: Self::default_remove_peerless_torrents(),
}
}
}
impl TrackerPolicy {
fn default_max_peer_timeout() -> u32 {
900
}
fn default_persistent_torrent_completed_stat() -> bool {
false
}
fn default_remove_peerless_torrents() -> bool {
true
}
}
#[derive(Debug, Default, Clone)]
pub struct Info {
config_toml: Option<String>,
config_toml_path: String,
}
impl Info {
#[allow(clippy::needless_pass_by_value)]
pub fn new(default_config_toml_path: String) -> Result<Self, Error> {
let env_var_config_toml = ENV_VAR_CONFIG_TOML.to_string();
let env_var_config_toml_path = ENV_VAR_CONFIG_TOML_PATH.to_string();
let config_toml = if let Ok(config_toml) = env::var(env_var_config_toml) {
println!("Loading extra configuration from environment variable:\n {config_toml}");
Some(config_toml)
} else {
None
};
let config_toml_path = if let Ok(config_toml_path) = env::var(env_var_config_toml_path) {
println!("Loading extra configuration from file: `{config_toml_path}` ...");
config_toml_path
} else {
println!("Loading extra configuration from default configuration file: `{default_config_toml_path}` ...");
default_config_toml_path
};
Ok(Self {
config_toml,
config_toml_path,
})
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, Constructor)]
pub struct AnnouncePolicy {
#[serde(default = "AnnouncePolicy::default_interval")]
pub interval: u32,
#[serde(default = "AnnouncePolicy::default_interval_min")]
pub interval_min: u32,
}
impl Default for AnnouncePolicy {
fn default() -> Self {
Self {
interval: Self::default_interval(),
interval_min: Self::default_interval_min(),
}
}
}
impl AnnouncePolicy {
fn default_interval() -> u32 {
120
}
fn default_interval_min() -> u32 {
120
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Unable to load from Environmental Variable: {source}")]
UnableToLoadFromEnvironmentVariable {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
},
#[error("Unable to load from Config File: {source}")]
UnableToLoadFromConfigFile {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
},
#[error("Failed processing the configuration: {source}")]
ConfigError {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
},
#[error("The error for errors that can never happen.")]
Infallible,
#[error("Unsupported configuration version: {version}")]
UnsupportedVersion { version: Version },
#[error("Missing mandatory configuration option. Option path: {path}")]
MissingMandatoryOption { path: String },
}
impl From<figment::Error> for Error {
#[track_caller]
fn from(err: figment::Error) -> Self {
Self::ConfigError {
source: (Arc::new(err) as DynError).into(),
}
}
}
#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
pub struct TslConfig {
#[serde(default = "TslConfig::default_ssl_cert_path")]
pub ssl_cert_path: Utf8PathBuf,
#[serde(default = "TslConfig::default_ssl_key_path")]
pub ssl_key_path: Utf8PathBuf,
}
impl TslConfig {
#[allow(clippy::unnecessary_wraps)]
fn default_ssl_cert_path() -> Utf8PathBuf {
Utf8PathBuf::new()
}
#[allow(clippy::unnecessary_wraps)]
fn default_ssl_key_path() -> Utf8PathBuf {
Utf8PathBuf::new()
}
}