invest_api_rust_sdk/
lib.rs1use paste::paste;
2pub(crate) mod prelude;
3use contracts::instruments_service_client::InstrumentsServiceClient;
4use contracts::market_data_service_client::MarketDataServiceClient;
5use contracts::market_data_stream_service_client::MarketDataStreamServiceClient;
6use contracts::operations_service_client::OperationsServiceClient;
7use contracts::operations_stream_service_client::OperationsStreamServiceClient;
8use contracts::orders_service_client::OrdersServiceClient;
9use contracts::orders_stream_service_client::OrdersStreamServiceClient;
10use contracts::sandbox_service_client::SandboxServiceClient;
11use contracts::signal_service_client::SignalServiceClient;
12use contracts::stop_orders_service_client::StopOrdersServiceClient;
13use contracts::users_service_client::UsersServiceClient;
14pub use prelude::contracts;
15use std::time::Duration;
16use tonic::metadata::{MetadataMap, MetadataValue};
17use tonic::service::interceptor::InterceptedService;
18use tonic::transport::{Channel, ClientTlsConfig, Endpoint};
19use tonic::Request;
20
21pub const PROD_ENDPOINT: &'static str = "https://invest-public-api.tinkoff.ru:443";
23pub const SANDBOX_ENDPOINT: &'static str = "https://sandbox-invest-public-api.tinkoff.ru:443";
25const DEFAULT_USER_AGENT: &'static str = "sillent/invest-api-rust-sdk";
26
27pub struct ServiceFactoryBuilder {
29 base_url: Option<String>,
30 token: Option<String>,
31 user_agent: Option<String>,
32 headers: Vec<(&'static str, String)>,
33 rate_limit: Option<(u64, Duration)>,
34 timeout: Option<Duration>,
35 connect_timeout: Option<Duration>,
36 tcp_keepalive: Option<Duration>,
37}
38
39impl ServiceFactoryBuilder {
40 pub fn new() -> Self {
42 Self {
43 base_url: None,
44 token: None,
45 user_agent: None,
46 headers: vec![],
47 rate_limit: None,
48 timeout: None,
49 connect_timeout: None,
50 tcp_keepalive: None,
51 }
52 }
53
54 pub fn base_url<S: Into<String>>(self, base_url: S) -> Self {
56 Self {
57 base_url: Some(base_url.into()),
58 ..self
59 }
60 }
61
62 pub fn token<S: Into<String>>(self, token: S) -> Self {
64 Self {
65 token: Some(token.into()),
66 ..self
67 }
68 }
69
70 pub fn user_agent<S: Into<String>>(self, user_agent: S) -> Self {
72 Self {
73 user_agent: Some(user_agent.into()),
74 ..self
75 }
76 }
77
78 pub fn headers(self, headers: Vec<(&'static str, String)>) -> Self {
80 Self { headers, ..self }
81 }
82
83 pub fn rate_limit(self, rate_limit: (u64, Duration)) -> Self {
85 Self {
86 rate_limit: Some(rate_limit),
87 ..self
88 }
89 }
90
91 pub fn timeout(self, timeout: Duration) -> Self {
93 Self {
94 timeout: Some(timeout),
95 ..self
96 }
97 }
98
99 pub fn connect_timeout(self, timeout: Duration) -> Self {
101 Self {
102 connect_timeout: Some(timeout),
103 ..self
104 }
105 }
106
107 pub fn tcp_keepalive(self, tcp_keepalive: Duration) -> Self {
109 Self {
110 tcp_keepalive: Some(tcp_keepalive),
111 ..self
112 }
113 }
114
115 pub fn build(self) -> Result<ServiceFactory, Box<dyn std::error::Error>> {
117 let ServiceFactoryBuilder {
118 base_url,
119 token,
120 user_agent,
121 headers,
122 rate_limit,
123 timeout,
124 connect_timeout,
125 tcp_keepalive,
126 } = self;
127 let base_url = match base_url {
128 Some(base_url) => base_url,
129 None => PROD_ENDPOINT.to_owned(),
130 };
131 let tls_config = ClientTlsConfig::new().with_native_roots();
132 let mut metadata: MetadataMap = MetadataMap::new();
133 for header in headers {
134 metadata.insert(header.0, header.1.parse()?);
135 }
136 if let Some(token) = token {
137 let token: MetadataValue<_> = format!("Bearer {}", token).parse()?;
138 metadata.insert("authorization", token);
139 }
140 let user_agent = match user_agent {
141 Some(user_agent) => user_agent,
142 None => DEFAULT_USER_AGENT.to_owned(),
143 };
144 let mut endpoint = Endpoint::from_shared(base_url)?
145 .tls_config(tls_config)?
146 .user_agent(user_agent)?
147 .tcp_keepalive(tcp_keepalive);
148 if let Some(rate_limit) = rate_limit {
149 endpoint = endpoint.rate_limit(rate_limit.0, rate_limit.1);
150 }
151 if let Some(timeout) = timeout {
152 endpoint = endpoint.timeout(timeout);
153 }
154 if let Some(connect_timeout) = connect_timeout {
155 endpoint = endpoint.connect_timeout(connect_timeout);
156 }
157 let channel = endpoint.connect_lazy();
158 Ok(ServiceFactory { channel, metadata })
159 }
160}
161
162#[derive(Clone)]
164pub struct ServiceFactory {
165 metadata: MetadataMap,
166 channel: Channel,
167}
168
169impl ServiceFactory {
170 pub fn builder() -> ServiceFactoryBuilder {
172 ServiceFactoryBuilder::new()
173 }
174
175 service_gen!(users_service:UsersServiceClient);
176 service_gen!(orders_service:OrdersServiceClient);
177 service_gen!(orders_stream_service:OrdersStreamServiceClient);
178 service_gen!(stop_orders_service:StopOrdersServiceClient);
179 service_gen!(operations_service:OperationsServiceClient);
180 service_gen!(operations_stream_service:OperationsStreamServiceClient);
181 service_gen!(instruments_service:InstrumentsServiceClient);
182 service_gen!(marketdata_service:MarketDataServiceClient);
183 service_gen!(marketdata_stream_service:MarketDataStreamServiceClient);
184 service_gen!(sandbox_service:SandboxServiceClient);
185 service_gen!(signal_service:SignalServiceClient);
186}
187
188#[macro_export]
189macro_rules! service_gen {
190 ($name:ident:$service:ident) => {
191 pub fn $name(
193 &self,
194 ) -> $service<
195 InterceptedService<
196 Channel,
197 impl FnMut(Request<()>) -> Result<Request<()>, tonic::Status>,
198 >,
199 > {
200 let channel = self.channel.clone();
201 let metadata = self.metadata.clone();
202 $service::with_interceptor(channel, move |mut req: Request<()>| {
203 metadata
204 .iter()
205 .map(|x| match x {
206 tonic::metadata::KeyAndValueRef::Ascii(k, v) => {
207 req.metadata_mut().insert(k, v.into());
208 }
209 tonic::metadata::KeyAndValueRef::Binary(k, v) => {
210 req.metadata_mut().insert_bin(k, v.into());
211 }
212 })
213 .count();
214 Ok(req)
215 })
216 }
217
218 paste! {
219 pub fn [<$name _with_interceptor>](&self, mut interceptor: impl FnMut(Request<()>)-> Result<Request<()>, tonic::Status>) -> $service<
221 InterceptedService<
222 Channel,
223 impl FnMut(Request<()>) -> Result<Request<()>, tonic::Status>,
224 >,
225 > {
226 let channel = self.channel.clone();
227 let metadata = self.metadata.clone();
228 $service::with_interceptor(channel, move |mut req: Request<()>| {
229 metadata
230 .iter()
231 .map(|x| match x {
232 tonic::metadata::KeyAndValueRef::Ascii(k, v) => {
233 req.metadata_mut().insert(k, v.into());
234 }
235 tonic::metadata::KeyAndValueRef::Binary(k, v) => {
236 req.metadata_mut().insert_bin(k, v.into());
237 }
238 })
239 .count();
240 interceptor(req)
241 })
242 }
243 }
244 };
245}