yatis/
lib.rs

1#![doc = include_str!("../README.md")]
2use requestor::AnyRequestor;
3use stream::AnyStream;
4use t_types::Quotation;
5use tonic::codec::CompressionEncoding::Gzip as GZIP;
6use tonic::service::interceptor::InterceptedService;
7use tonic::service::Interceptor;
8use tonic::transport::{Channel, ClientTlsConfig};
9use tonic::{Request, Status};
10use uuid::Uuid;
11use tonic::client::Grpc;
12
13/// reused tonic's Grpc type, with implementation of [crate::InvestApi]
14/// # Examples:
15/// ```rust
16/// # #[tokio::main]
17/// # async fn main() {
18///     use yatis::*;
19///     use t_types::*;
20/// #    let token = std::env::var("TOKEN").expect("need to set env var 'TOKEN'");
21///     let api = Api::create_invest_service(token).unwrap();
22///     println!("{:?}", api.request(GetInfoRequest{}).await);;
23/// # }
24/// ``` 
25pub type Api = Grpc<IService>;
26
27/// sandbox implementation of api. Also implements [crate::InvestApi]
28/// Not that Sandbox maniputlation operations not implemented in trait [crate::InvestApi],
29/// but implemented in type SandboxApi
30/// # Examples:
31/// ```rust
32/// # #[tokio::main]
33/// # async fn main() {
34///     use yatis::*;
35///     use t_types::*;
36/// #    let token = std::env::var("SANDBOX_TOKEN").expect("need to set env var 'TOKEN'");
37///     let api = SandboxApi::create_invest_service(token).unwrap();
38///     println!("{:?}", api.request(GetInfoRequest{}).await);
39/// # }
40/// ``` 
41pub use sandbox::Sandbox as SandboxApi;
42
43pub use pool::ApiPool;
44
45pub mod t_types;
46pub mod requestor;
47pub mod stream;
48pub mod stream_response;
49pub mod pool;
50
51mod sandbox;
52mod quotation;
53
54pub type IService = InterceptedService<Channel, TokenInterceptor>;
55/// Self creator
56pub trait InvestService: Sized {
57    fn create_invest_service(token: impl ToString) -> Result<Self, tonic::transport::Error>;
58}
59
60impl InvestService for IService {
61    fn create_invest_service(token: impl ToString) -> Result<Self, tonic::transport::Error> {
62        let tls = ClientTlsConfig::new().with_native_roots();
63        let channel = Channel::from_static("https://invest-public-api.tinkoff.ru").tls_config(tls)?.connect_lazy();
64        Ok(InterceptedService::new(channel, TokenInterceptor::new(token)))
65    }
66}
67impl InvestService for Grpc<IService> {
68    fn create_invest_service(token: impl ToString) -> Result<Self, tonic::transport::Error> {
69        let serv = InterceptedService::create_invest_service(token)?;
70        let res = Grpc::new(serv).accept_compressed(GZIP).send_compressed(GZIP);
71        Ok(res)
72    }
73}
74
75#[derive(Debug, Clone)]
76pub struct TokenInterceptor {
77    token: String,
78}
79
80impl TokenInterceptor {
81    pub fn new(token: impl ToString) -> Self {
82        Self { token: token.to_string() }
83    }
84}
85
86impl Interceptor for TokenInterceptor {
87    fn call(&mut self, mut req: Request<()>) -> Result<Request<()>, Status> {
88        req.metadata_mut().append(
89            "authorization",
90            format!("bearer {}", self.token).parse().unwrap(),
91        );
92        req.metadata_mut()
93            .append("x-tracking-id", Uuid::new_v4().to_string().parse().unwrap());
94        Ok(req)
95    }
96}
97
98pub use requestor::Requestor;
99pub use stream::StartStream;
100pub use stream_response::StreamResponse;
101
102/// trait for use some methods of investing api.
103/// # Examples:
104/// ```rust
105/// # #[tokio::main]
106/// # async fn main() {
107/// #    use yatis::*;
108/// #    let token = std::env::var("SANDBOX_TOKEN").expect("need to set env var 'TOKEN'");
109/// #    let api = yatis::SandboxApi::create_invest_service(token).unwrap();
110/// #    trading(api).await;
111/// # }
112/// async fn trading(api: impl yatis::InvestApi) {
113///    use yatis::*;
114///    use t_types::*;
115///    let res = api.request(GetAccountsRequest::default()).await.unwrap();
116///    println!("{:?}", res);
117///    println!("{:?}", api.request(GetInfoRequest{}).await);
118///    let (s, mut r) = futures::channel::mpsc::channel::<StreamResponse>(10);
119///    api.start_stream(PositionsStreamRequest{ 
120///        accounts: vec![res.accounts[0].id.clone()], 
121///        with_initial_positions: true, 
122///        ping_settings: None 
123///    }, s.clone()).await.unwrap();
124///    use futures::StreamExt; // Receiver implements Stream
125///    if let Some(res) = r.next().await {
126///       println!("{res:?}");
127///    }
128/// }
129/// ``` 
130/// 
131pub trait InvestApi: AnyRequestor + AnyStream<StreamResponse> + Send + 'static {}
132impl<T> InvestApi for T where T: AnyRequestor + AnyStream<StreamResponse> + Send + 'static {}
133
134pub trait QuotationExt {
135    fn floor(&self, increment: Quotation) -> Self;
136    fn round(&self, increment: Quotation) -> Self;
137}