torrust_index/config/v2/
tracker.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6use super::{ValidationError, Validator};
7
8/// Configuration for the associated tracker.
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct Tracker {
11    /// The url of the tracker API. For example: `http://localhost:1212/`.
12    #[serde(default = "Tracker::default_api_url")]
13    pub api_url: Url,
14
15    /// Whether the tracker is running in listed mode or not.
16    #[serde(default = "Tracker::default_listed")]
17    pub listed: bool,
18
19    /// Whether the tracker is running in private mode or not.
20    #[serde(default = "Tracker::default_private")]
21    pub private: bool,
22
23    /// The token used to authenticate with the tracker API.
24    #[serde(default = "Tracker::default_token")]
25    pub token: ApiToken,
26
27    /// The amount of seconds the tracker API token is valid.
28    #[serde(default = "Tracker::default_token_valid_seconds")]
29    pub token_valid_seconds: u64,
30
31    /// Connection string for the tracker. For example: `udp://TRACKER_IP:6969`.
32    #[serde(default = "Tracker::default_url")]
33    pub url: Url,
34}
35
36impl Validator for Tracker {
37    fn validate(&self) -> Result<(), ValidationError> {
38        if self.private && (self.url.scheme() != "http" && self.url.scheme() != "https") {
39            return Err(ValidationError::UdpTrackersInPrivateModeNotSupported);
40        }
41
42        Ok(())
43    }
44}
45
46impl Default for Tracker {
47    fn default() -> Self {
48        Self {
49            url: Self::default_url(),
50            listed: Self::default_listed(),
51            private: Self::default_private(),
52            api_url: Self::default_api_url(),
53            token: Self::default_token(),
54            token_valid_seconds: Self::default_token_valid_seconds(),
55        }
56    }
57}
58
59impl Tracker {
60    pub fn override_tracker_api_token(&mut self, tracker_api_token: &ApiToken) {
61        self.token = tracker_api_token.clone();
62    }
63
64    fn default_url() -> Url {
65        Url::parse("udp://localhost:6969").unwrap()
66    }
67
68    fn default_listed() -> bool {
69        false
70    }
71
72    fn default_private() -> bool {
73        false
74    }
75
76    fn default_api_url() -> Url {
77        Url::parse("http://localhost:1212/").unwrap()
78    }
79
80    fn default_token() -> ApiToken {
81        ApiToken::new("MyAccessToken")
82    }
83
84    fn default_token_valid_seconds() -> u64 {
85        7_257_600
86    }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
90pub struct ApiToken(String);
91
92impl ApiToken {
93    /// # Panics
94    ///
95    /// Will panic if the tracker API token if empty.
96    #[must_use]
97    pub fn new(key: &str) -> Self {
98        assert!(!key.is_empty(), "tracker API token cannot be empty");
99
100        Self(key.to_owned())
101    }
102
103    #[must_use]
104    pub fn as_bytes(&self) -> &[u8] {
105        self.0.as_bytes()
106    }
107}
108
109impl fmt::Display for ApiToken {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "{}", self.0)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::ApiToken;
118
119    #[test]
120    #[should_panic(expected = "tracker API token cannot be empty")]
121    fn apai_token_can_not_be_empty() {
122        drop(ApiToken::new(""));
123    }
124}