torrust_index/tracker/
api.rs

1use std::time::Duration;
2
3use reqwest::{Error, Response};
4use url::Url;
5pub struct ConnectionInfo {
6    /// The URL of the tracker API. Eg: <https://tracker:1212>.
7    pub url: Url,
8    /// The token used to authenticate with the tracker API.
9    pub token: String,
10}
11
12impl ConnectionInfo {
13    #[must_use]
14    pub fn new(url: Url, token: String) -> Self {
15        Self { url, token }
16    }
17}
18
19const TOKEN_PARAM_NAME: &str = "token";
20const API_PATH: &str = "api/v1";
21const TOTAL_REQUEST_TIMEOUT_IN_SECS: u64 = 5;
22
23pub struct Client {
24    pub connection_info: ConnectionInfo,
25    api_base_url: Url,
26    client: reqwest::Client,
27    token_param: [(String, String); 1],
28}
29
30impl Client {
31    /// # Errors
32    ///
33    /// Will fails if it can't build a HTTP client with a timeout.
34    ///
35    /// # Panics
36    ///
37    /// Will panic if the API base URL is not valid.
38    pub fn new(connection_info: ConnectionInfo) -> Result<Self, Error> {
39        let api_base_url = connection_info.url.join(API_PATH).expect("valid URL API path");
40        let client = reqwest::Client::builder()
41            .timeout(Duration::from_secs(TOTAL_REQUEST_TIMEOUT_IN_SECS))
42            .build()?;
43        let token_param = [(TOKEN_PARAM_NAME.to_string(), connection_info.token.to_string())];
44
45        Ok(Self {
46            connection_info,
47            api_base_url,
48            client,
49            token_param,
50        })
51    }
52
53    /// Add a torrent to the tracker whitelist.
54    ///
55    /// # Errors
56    ///
57    /// Will return an error if the HTTP request fails.
58    pub async fn whitelist_torrent(&self, info_hash: &str) -> Result<Response, Error> {
59        let request_url = format!("{}/whitelist/{}", self.api_base_url, info_hash);
60
61        self.client.post(request_url).query(&self.token_param).send().await
62    }
63
64    /// Remove a torrent from the tracker whitelist.
65    ///
66    /// # Errors
67    ///
68    /// Will return an error if the HTTP request fails.
69    pub async fn remove_torrent_from_whitelist(&self, info_hash: &str) -> Result<Response, Error> {
70        let request_url = format!("{}/whitelist/{}", self.api_base_url, info_hash);
71
72        self.client.delete(request_url).query(&self.token_param).send().await
73    }
74
75    /// Retrieve a new tracker key.
76    ///
77    /// # Errors
78    ///
79    /// Will return an error if the HTTP request fails.
80    pub async fn retrieve_new_tracker_key(&self, token_valid_seconds: u64) -> Result<Response, Error> {
81        let request_url = format!("{}/key/{}", self.api_base_url, token_valid_seconds);
82
83        self.client.post(request_url).query(&self.token_param).send().await
84    }
85
86    /// Retrieve the info for one torrent.
87    ///
88    /// # Errors
89    ///
90    /// Will return an error if the HTTP request fails.
91    pub async fn get_torrent_info(&self, info_hash: &str) -> Result<Response, Error> {
92        let request_url = format!("{}/torrent/{}", self.api_base_url, info_hash);
93
94        self.client.get(request_url).query(&self.token_param).send().await
95    }
96
97    /// Retrieve the info for multiple torrents at the same time.
98    ///
99    /// # Errors
100    ///
101    /// Will return an error if the HTTP request fails.
102    pub async fn get_torrents_info(&self, info_hashes: &[String]) -> Result<Response, Error> {
103        let request_url = format!("{}/torrents", self.api_base_url);
104
105        let mut query_params: Vec<(String, String)> = Vec::with_capacity(info_hashes.len() + 1);
106
107        query_params.push((TOKEN_PARAM_NAME.to_string(), self.connection_info.token.clone()));
108
109        for info_hash in info_hashes {
110            query_params.push(("info_hash".to_string(), info_hash.clone()));
111        }
112
113        self.client.get(request_url).query(&query_params).send().await
114    }
115}