Skip to main content

longbridge/calendar/
context.rs

1use std::sync::Arc;
2
3use longbridge_httpcli::{HttpClient, Json, Method};
4use serde::Serialize;
5use tracing::{Subscriber, dispatcher, instrument::WithSubscriber};
6
7use crate::{Config, Result, calendar::types::*};
8
9struct InnerCalendarContext {
10    http_cli: HttpClient,
11    log_subscriber: Arc<dyn Subscriber + Send + Sync>,
12}
13
14impl Drop for InnerCalendarContext {
15    fn drop(&mut self) {
16        dispatcher::with_default(&self.log_subscriber.clone().into(), || {
17            tracing::info!("calendar context dropped");
18        });
19    }
20}
21
22/// Financial calendar context — earnings, dividends, splits, IPOs, macro data.
23#[derive(Clone)]
24pub struct CalendarContext(Arc<InnerCalendarContext>);
25
26impl CalendarContext {
27    /// Create a [`CalendarContext`]
28    pub fn new(config: Arc<Config>) -> Self {
29        let log_subscriber = config.create_log_subscriber("calendar");
30        dispatcher::with_default(&log_subscriber.clone().into(), || {
31            tracing::info!(language = ?config.language, "creating calendar context");
32        });
33        let ctx = Self(Arc::new(InnerCalendarContext {
34            http_cli: config.create_http_client(),
35            log_subscriber,
36        }));
37        dispatcher::with_default(&ctx.0.log_subscriber.clone().into(), || {
38            tracing::info!("calendar context created");
39        });
40        ctx
41    }
42
43    /// Returns the log subscriber
44    #[inline]
45    pub fn log_subscriber(&self) -> Arc<dyn Subscriber + Send + Sync> {
46        self.0.log_subscriber.clone()
47    }
48
49    /// Get financial calendar events.
50    ///
51    /// Path: `GET /v1/quote/finance_calendar`
52    pub async fn finance_calendar(
53        &self,
54        category: CalendarCategory,
55        start: impl Into<String>,
56        end: impl Into<String>,
57        market: Option<String>,
58    ) -> Result<CalendarEventsResponse> {
59        let cat_str = match category {
60            CalendarCategory::Report => "report",
61            CalendarCategory::Dividend => "dividend",
62            CalendarCategory::Split => "split",
63            CalendarCategory::Ipo => "ipo",
64            CalendarCategory::MacroData => "macrodata",
65            CalendarCategory::Closed => "closed",
66            CalendarCategory::Meeting => "meeting",
67            CalendarCategory::Merge => "merge",
68        };
69        #[derive(Serialize)]
70        struct Query {
71            date: String,
72            date_end: String,
73            #[serde(rename = "types[]")]
74            types: &'static str,
75            #[serde(rename = "markets[]", skip_serializing_if = "Option::is_none")]
76            markets: Option<String>,
77        }
78        Ok(self
79            .0
80            .http_cli
81            .request(Method::GET, "/v1/quote/finance_calendar")
82            .query_params(Query {
83                date: start.into(),
84                date_end: end.into(),
85                types: cat_str,
86                markets: market,
87            })
88            .response::<Json<CalendarEventsResponse>>()
89            .send()
90            .with_subscriber(self.0.log_subscriber.clone())
91            .await?
92            .0)
93    }
94}