gitignore_template_generator/http_client/
impls.rs

1use std::{collections::HashMap, time::Duration};
2
3use ureq::Agent;
4
5use crate::{ExitKind, ProgramExit, constant, http_client::api::HttpClient};
6
7/// Http client implementation relying on [`ureq`].
8#[derive(Default)]
9pub struct UreqHttpClient {
10    /// The base url of the HTTP server to reach.
11    ///
12    /// Used as base url when calling [`UreqHttpClient::get`] method.
13    pub server_url: String,
14
15    /// The timeout for the entire HTTP call.
16    pub global_timeout: Option<Duration>,
17}
18
19/// Http client implementation to mock a response.
20pub struct MockHttpClient {
21    /// The mocked response to be returned when calling [`MockHttpClient::get`]
22    /// method.
23    pub response: Result<String, ProgramExit>,
24}
25
26/// Http client implementation to mock a response.
27pub struct MockEndpointHttpClient<'a> {
28    /// The mocked response to be returned when calling [`MockHttpClient::get`]
29    /// method.
30    pub response: HashMap<&'a str, Result<String, ProgramExit>>,
31}
32
33impl HttpClient for UreqHttpClient {
34    /// Make a GET HTTP call using a [`ureq`] client.
35    ///
36    /// The server base url (i.e. https://localhost:8080) should be provided
37    /// as part of [`UreqHttpClient::server_url] field.
38    ///
39    /// See [`HttpClient::get`] for more infos.
40    fn get(&self, url: &str) -> Result<String, ProgramExit> {
41        let full_url = format!("{}{url}", self.server_url);
42        let agent: Agent = Agent::config_builder()
43            .timeout_global(Some(self.global_timeout.unwrap_or(
44                Duration::from_secs(
45                    constant::template_manager::TIMEOUT.parse().expect(
46                        constant::error_messages::FAILED_U64_CONVERSION,
47                    ),
48                ),
49            )))
50            .build()
51            .into();
52
53        let result = agent.get(full_url).call();
54
55        match result {
56            Ok(mut response) => match response.body_mut().read_to_string() {
57                Ok(body) => Ok(body.trim().to_string()),
58                Err(error) => Err(ProgramExit {
59                    message: error.to_string(),
60                    exit_status: constant::exit_status::HTTP_CLIENT_ERROR,
61                    styled_message: None,
62                    kind: ExitKind::Error,
63                }),
64            },
65            Err(error) => Err(ProgramExit {
66                message: constant::error_messages::API_CALL_FAILURE
67                    .replace("{error}", &error.to_string()),
68                exit_status: constant::exit_status::GENERIC,
69                styled_message: None,
70                kind: ExitKind::Error,
71            }),
72        }
73    }
74}
75
76impl HttpClient for MockHttpClient {
77    /// Returns the result linked to this instance.
78    ///
79    /// The given `_url` will not be used, simply a clone of linked
80    /// result will be returned.
81    fn get(&self, _url: &str) -> Result<String, ProgramExit> {
82        self.response.clone()
83    }
84}
85
86impl HttpClient for MockEndpointHttpClient<'_> {
87    /// Returns the result linked to the given url.
88    ///
89    /// The given `url` will only be used to get proper result from linked
90    /// hashmap.
91    fn get(&self, url: &str) -> Result<String, ProgramExit> {
92        self.response[url].clone()
93    }
94}