Skip to main content

tse_client/
request.rs

1//! HTTP request layer, replacing the JS `rq` object built on `node-fetch`.
2
3use std::time::Duration;
4
5use crate::error::{Error, Result};
6
7pub const DEFAULT_API_URL: &str = "http://service.tsetmc.com/tsev2/data/TseClient2.aspx";
8
9/// Thin client wrapping `reqwest` for the TseClient2 endpoint.
10#[derive(Debug, Clone)]
11pub struct Requester {
12    client: reqwest::Client,
13    api_url: String,
14}
15
16impl Requester {
17    pub fn new(api_url: String) -> Self {
18        let client = reqwest::Client::builder()
19            .timeout(Duration::from_secs(120))
20            .build()
21            .expect("failed to build reqwest client");
22        Requester { client, api_url }
23    }
24
25    async fn make_request(&self, params: &[(&str, &str)]) -> Result<String> {
26        let res = self.client.get(&self.api_url).query(params).send().await?;
27        let status = res.status();
28        if status.as_u16() == 200 {
29            Ok(res.text().await?)
30        } else {
31            Err(Error::InvalidResponse(format!(
32                "{} {}",
33                status.as_u16(),
34                status.canonical_reason().unwrap_or("")
35            )))
36        }
37    }
38
39    pub async fn instrument(&self, deven: &str) -> Result<String> {
40        self.make_request(&[("t", "Instrument"), ("a", deven)])
41            .await
42    }
43
44    pub async fn instrument_and_share(&self, deven: &str, last_id: i64) -> Result<String> {
45        let last_id = last_id.to_string();
46        self.make_request(&[("t", "InstrumentAndShare"), ("a", deven), ("a2", &last_id)])
47            .await
48    }
49
50    pub async fn last_possible_deven(&self) -> Result<String> {
51        self.make_request(&[("t", "LastPossibleDeven")]).await
52    }
53
54    pub async fn closing_prices(&self, ins_codes: &str) -> Result<String> {
55        self.make_request(&[("t", "ClosingPrices"), ("a", ins_codes)])
56            .await
57    }
58}
59
60/// Build an intraday URL, mirroring the JS `INTRADAY_URL` closure.
61pub fn intraday_url(server: i32, inscode: &str, deven: &str) -> String {
62    let host = if server > 0 {
63        format!("cdn{server}.")
64    } else if server < 0 {
65        String::new()
66    } else {
67        "cdn.".to_string()
68    };
69    format!("http://{host}tsetmc.com/Loader.aspx?ParTree=15131P&i={inscode}&d={deven}")
70}