Skip to main content

dhan_rs/api/
instruments.rs

1//! Instrument-master CSV downloads.
2
3use bytes::Bytes;
4
5use crate::client::DhanClient;
6use crate::error::{DhanError, Result};
7use crate::types::instruments::InstrumentSegment;
8
9const COMPACT_INSTRUMENTS_URL: &str = "https://images.dhan.co/api-data/api-scrip-master.csv";
10const DETAILED_INSTRUMENTS_URL: &str =
11    "https://images.dhan.co/api-data/api-scrip-master-detailed.csv";
12
13async fn download_public_csv(url: &str) -> Result<Bytes> {
14    // These CDN downloads carry no account headers, so following a bounded
15    // public redirect does not risk forwarding Dhan credentials.
16    let http = reqwest::Client::builder()
17        .redirect(reqwest::redirect::Policy::limited(5))
18        .build()?;
19    let response = http.get(url).send().await?;
20    let status = response.status();
21    let body = response
22        .bytes()
23        .await
24        .map_err(|source| DhanError::ResponseBody {
25            status,
26            source: source.without_url(),
27        })?;
28    if status.is_success() {
29        Ok(body)
30    } else {
31        Err(DhanError::HttpStatus {
32            status,
33            body: String::from_utf8_lossy(&body).into_owned(),
34        })
35    }
36}
37
38impl DhanClient {
39    /// Download Dhan's compact all-segment instrument master as raw CSV bytes.
40    pub async fn download_compact_instruments_csv() -> Result<Bytes> {
41        download_public_csv(COMPACT_INSTRUMENTS_URL).await
42    }
43
44    /// Download Dhan's detailed all-segment instrument master as raw CSV bytes.
45    pub async fn download_detailed_instruments_csv() -> Result<Bytes> {
46        download_public_csv(DETAILED_INSTRUMENTS_URL).await
47    }
48
49    /// Download the detailed instrument CSV for one exchange segment.
50    ///
51    /// **Endpoint:** `GET /v2/instrument/{exchangeSegment}`
52    pub async fn download_segment_instruments_csv(
53        &self,
54        segment: InstrumentSegment,
55    ) -> Result<Bytes> {
56        let url = format!("{}/v2/instrument/{}", self.base_url(), segment.as_str());
57        let response = self.http().get(url).send().await?;
58        let status = response.status();
59        let body = response
60            .bytes()
61            .await
62            .map_err(|source| DhanError::ResponseBody { status, source })?;
63        if status.is_success() {
64            Ok(body)
65        } else {
66            Err(self.parse_error_body(status, &String::from_utf8_lossy(&body)))
67        }
68    }
69}