1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3
4pub mod models;
5
6use std::{
7 io::{Error, Read, Write},
8 net::TcpStream,
9};
10
11use models::ConnectionType;
12use native_tls::{TlsConnector, TlsStream};
13
14const HOST: &str = "xapi.xtb.com";
15const PORT_LIVE: &str = "5112";
16const PORT_DEMO: &str = "5124";
17
18pub struct Client {
19 main_connection: TlsStream<TcpStream>,
20 stream_session_id: Option<String>,
21 }
23
24impl Client {
25 pub fn new(connection_type: ConnectionType) -> Client {
26 let connector = TlsConnector::new().unwrap();
27 let main_connection = Client::build_main_connection(&connection_type, &connector);
28
29 Client {
30 main_connection: main_connection,
31 stream_session_id: None,
32 }
34 }
35
36 pub fn login(&mut self, user_id: u64, password: String) -> Result<(), Error> {
37 let login_request = models::LoginRequest {
38 command: models::Command::login.to_string(),
39 arguments: models::LoginRequestArguments {
40 user_id: user_id,
41 password: password,
42 },
43 };
44
45 let request = serde_json::to_string(&login_request)?;
46
47 self.main_connection
48 .write_all(request.as_bytes())
49 .expect("write failed");
50
51 let mut buffer = [0; 4096];
52 self.main_connection.read(&mut buffer)?;
53
54 let data_string = String::from_utf8_lossy(&buffer);
55
56 let trimmed_data_string = &data_string.split("\n\n").next().unwrap();
57
58 let login_response: models::LoginResponse = serde_json::from_str(&trimmed_data_string)?;
59
60 self.stream_session_id = Some(login_response.stream_session_id);
61
62 Ok(())
63 }
64
65 pub fn balance(&mut self) -> Result<models::Response<models::Balance>, Error> {
66 let balance_request = models::Request {
67 command: models::Command::getMarginLevel.to_string(),
68 };
69
70 let request = serde_json::to_string(&balance_request)?;
71
72 self.main_connection
73 .write_all(request.as_bytes())
74 .expect("write failed");
75
76 let mut buffer = [0; 4096];
77 self.main_connection.read(&mut buffer)?;
78
79 let data_string = String::from_utf8_lossy(&buffer);
80
81 let trimmed_data_string = data_string.split("\n\n").next().unwrap();
82
83 let balance_response: models::Response<models::Balance> =
84 serde_json::from_str(&trimmed_data_string)?;
85
86 Ok(balance_response)
87 }
88
89 fn build_main_connection(
90 connection_type: &ConnectionType,
91 connector: &TlsConnector,
92 ) -> TlsStream<TcpStream> {
93 let addr = Client::build_addr(connection_type);
94 let stream = TcpStream::connect(addr).expect("Could not connect to the XTB API");
95
96 connector.connect(HOST, stream).unwrap()
97 }
98
99 fn build_addr(connection_type: &ConnectionType) -> String {
100 match connection_type {
101 ConnectionType::Live => format!("{}:{}", HOST, PORT_LIVE),
102 ConnectionType::Demo => format!("{}:{}", HOST, PORT_DEMO),
103 }
104 }
105}