longbridge/sharelist/
context.rs1use std::sync::Arc;
2
3use longbridge_httpcli::{HttpClient, Json, Method};
4use serde::{Serialize, de::DeserializeOwned};
5use tracing::{Subscriber, dispatcher, instrument::WithSubscriber};
6
7use crate::{Config, Result, sharelist::types::*};
8
9struct InnerSharelistContext {
10 http_cli: HttpClient,
11 log_subscriber: Arc<dyn Subscriber + Send + Sync>,
12}
13
14impl Drop for InnerSharelistContext {
15 fn drop(&mut self) {
16 dispatcher::with_default(&self.log_subscriber.clone().into(), || {
17 tracing::info!("sharelist context dropped");
18 });
19 }
20}
21
22#[derive(Clone)]
24pub struct SharelistContext(Arc<InnerSharelistContext>);
25
26impl SharelistContext {
27 pub fn new(config: Arc<Config>) -> Self {
29 let log_subscriber = config.create_log_subscriber("sharelist");
30 dispatcher::with_default(&log_subscriber.clone().into(), || {
31 tracing::info!(language = ?config.language, "creating sharelist context");
32 });
33 let ctx = Self(Arc::new(InnerSharelistContext {
34 http_cli: config.create_http_client(),
35 log_subscriber,
36 }));
37 dispatcher::with_default(&ctx.0.log_subscriber.clone().into(), || {
38 tracing::info!("sharelist context created");
39 });
40 ctx
41 }
42
43 #[inline]
45 pub fn log_subscriber(&self) -> Arc<dyn Subscriber + Send + Sync> {
46 self.0.log_subscriber.clone()
47 }
48
49 async fn get<R, Q>(&self, path: &'static str, query: Q) -> Result<R>
50 where
51 R: DeserializeOwned + Send + Sync + 'static,
52 Q: Serialize + Send + Sync,
53 {
54 Ok(self
55 .0
56 .http_cli
57 .request(Method::GET, path)
58 .query_params(query)
59 .response::<Json<R>>()
60 .send()
61 .with_subscriber(self.0.log_subscriber.clone())
62 .await?
63 .0)
64 }
65
66 async fn post<R, B>(&self, path: &'static str, body: B) -> Result<R>
67 where
68 R: DeserializeOwned + Send + Sync + 'static,
69 B: std::fmt::Debug + Serialize + Send + Sync + 'static,
70 {
71 Ok(self
72 .0
73 .http_cli
74 .request(Method::POST, path)
75 .body(Json(body))
76 .response::<Json<R>>()
77 .send()
78 .with_subscriber(self.0.log_subscriber.clone())
79 .await?
80 .0)
81 }
82
83 async fn http_delete<R, Q>(&self, path: String, query: Q) -> Result<R>
84 where
85 R: DeserializeOwned + Send + Sync + 'static,
86 Q: Serialize + Send + Sync,
87 {
88 Ok(self
89 .0
90 .http_cli
91 .request(Method::DELETE, path.leak())
92 .query_params(query)
93 .response::<Json<R>>()
94 .send()
95 .with_subscriber(self.0.log_subscriber.clone())
96 .await?
97 .0)
98 }
99
100 pub async fn list(&self, count: u32) -> Result<SharelistList> {
104 #[derive(Serialize)]
105 struct Query {
106 size: u32,
107 #[serde(rename = "self")]
108 is_self: &'static str,
109 subscription: &'static str,
110 }
111 self.get(
112 "/v1/sharelists",
113 Query {
114 size: count,
115 is_self: "true",
116 subscription: "true",
117 },
118 )
119 .await
120 }
121
122 pub async fn detail(&self, id: i64) -> Result<SharelistDetail> {
126 #[derive(Serialize)]
127 struct Query {
128 constituent: &'static str,
129 quote: &'static str,
130 subscription: &'static str,
131 }
132 let path = format!("/v1/sharelists/{id}");
133 Ok(self
134 .0
135 .http_cli
136 .request(Method::GET, path.leak())
137 .query_params(Query {
138 constituent: "true",
139 quote: "true",
140 subscription: "true",
141 })
142 .response::<Json<SharelistDetail>>()
143 .send()
144 .with_subscriber(self.0.log_subscriber.clone())
145 .await?
146 .0)
147 }
148
149 pub async fn popular(&self, count: u32) -> Result<SharelistList> {
153 #[derive(Serialize)]
154 struct Query {
155 size: u32,
156 }
157 self.get("/v1/sharelists/popular", Query { size: count })
158 .await
159 }
160
161 pub async fn create(&self, name: impl Into<String>, description: Option<String>) -> Result<()> {
165 let name_str = name.into();
166 let desc = description.unwrap_or_else(|| name_str.clone());
167 self.post::<serde_json::Value, _>("/v1/sharelists", serde_json::json!({ "name": name_str, "description": desc, "cover": "https://pub.pbkrs.com/files/202107/kaJSk6BsvPt6NJ3Q/sharelist_v1.png" })).await?;
168 Ok(())
169 }
170
171 pub async fn delete(&self, id: i64) -> Result<()> {
175 let path = format!("/v1/sharelists/{id}");
176 self.0
177 .http_cli
178 .request(Method::DELETE, path.leak())
179 .response::<Json<serde_json::Value>>()
180 .send()
181 .with_subscriber(self.0.log_subscriber.clone())
182 .await?;
183 Ok(())
184 }
185
186 pub async fn add_securities(&self, id: i64, symbols: Vec<String>) -> Result<()> {
190 let syms = symbols.join(",");
191 let path = format!("/v1/sharelists/{id}/items");
192 self.post::<serde_json::Value, _>(path.leak(), serde_json::json!({ "symbols": syms }))
193 .await?;
194 Ok(())
195 }
196
197 pub async fn remove_securities(&self, id: i64, symbols: Vec<String>) -> Result<()> {
201 #[derive(Serialize)]
202 struct Query {
203 symbols: String,
204 }
205 self.http_delete::<serde_json::Value, _>(
206 format!("/v1/sharelists/{id}/items"),
207 Query {
208 symbols: symbols.join(","),
209 },
210 )
211 .await?;
212 Ok(())
213 }
214
215 pub async fn sort_securities(&self, id: i64, symbols: Vec<String>) -> Result<()> {
219 let syms = symbols.join(",");
220 let path = format!("/v1/sharelists/{id}/items/sort");
221 self.post::<serde_json::Value, _>(path.leak(), serde_json::json!({ "symbols": syms }))
222 .await?;
223 Ok(())
224 }
225}