1use oauth2::reqwest::http_client;
2use oauth2::AccessToken;
3use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, TokenResponse, TokenUrl};
4
5pub mod api;
6const PRODUCTION_BASE_URL: &str = "https://digital.iservices.rte-france.com/";
9
10#[derive(Debug)]
11enum ApiException {
12 InvalidToken,
14 TooManyRequests,
16 ApplicationNotRegistered,
18 UnknownError,
19}
20
21pub trait ApiClient {
22 fn http_get(
23 &self,
24 path: &str,
25 query_string: &[(String, String)],
26 ) -> Result<String, anyhow::Error>;
28}
29
30#[derive(Debug)]
31pub struct RteApi {
32 client_id: ClientId,
33 client_secret: ClientSecret,
34 base_url: String,
35
36 token: Option<AccessToken>,
37}
38
39impl RteApi {
40 pub fn new(client_id: String, client_secret: String) -> Self {
41 RteApi {
42 client_id: ClientId::new(client_id),
43 client_secret: ClientSecret::new(client_secret),
44 base_url: PRODUCTION_BASE_URL.to_string(),
45 token: None,
46 }
47 }
48
49 pub fn from_env_values() -> Self {
50 let client_id = std::env::var("CLIENT_ID").expect("CLIENT_ID must be set");
51 let client_secret = std::env::var("CLIENT_SECRET").expect("CLIENT_SECRET must be set");
52
53 RteApi::new(client_id, client_secret)
54 }
55
56 pub fn with_base_url(mut self, base_url: String) -> Self {
57 self.base_url = base_url;
58 self
59 }
60
61 pub fn authenticate(&mut self) -> anyhow::Result<()> {
62 let auth_url = format!("{}/oauth/authorize", self.base_url);
63 let token_url = format!("{}/oauth/token", self.base_url);
64 let client = BasicClient::new(
65 self.client_id.clone(),
66 Some(self.client_secret.clone()),
67 AuthUrl::new(auth_url)?,
68 Some(TokenUrl::new(token_url)?),
69 );
70
71 let token_result = client.exchange_client_credentials().request(http_client)?;
72
73 self.token = Some(token_result.access_token().clone());
74
75 Ok(())
76 }
77
78 pub fn get_token(&self) -> &String {
79 self.token.as_ref().unwrap().secret()
80 }
81}
82
83impl ApiClient for RteApi {
84 fn http_get(
85 &self,
86 path: &str,
87 query_string: &[(String, String)],
88 ) -> Result<String, anyhow::Error> {
89 let url = format!("{}{}", self.base_url, path);
90 let token = self.token.as_ref().unwrap().secret();
91
92 let http_client = reqwest::blocking::Client::new();
93
94 println!("url: {:?}", url);
95 let response = http_client
96 .get(&url)
97 .query(&query_string)
98 .bearer_auth(token)
99 .send()?;
100
101 let status_code = response.status();
102
103 let body = response.text()?;
104 println!("response: {:?}", body);
105 if !status_code.is_success() {
106 let status = match status_code.as_u16() {
107 401 => ApiException::InvalidToken,
108 429 => ApiException::TooManyRequests,
109 403 => ApiException::ApplicationNotRegistered,
110 _ => ApiException::UnknownError,
111 };
112 eprintln!(
113 "Error HTTP {} ({:?}): {}",
114 status_code.as_str(),
115 status,
116 body
117 );
118 return Err(anyhow::Error::msg(format!("Request failed: {}", 42)));
119 }
120
121 Ok(body)
122 }
123}