use std::convert::TryInto;
use std::io::Read;
use std::io::Write;
use std::net::TcpStream;
use std::str::from_utf8;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed};
const HEADER_LEN: usize = 10;
const MAX_PACKET: usize = 4110;
#[repr(i32)]
pub enum PacketType {
Response,
_None,
Cmd,
Auth,
}
#[derive(Debug)]
pub struct Packet {
pub len: i32,
pub id: i32,
pub packet_type: i32,
pub body: String,
}
pub struct Client {
conn: TcpStream,
next: AtomicI32,
auth: AtomicBool,
}
impl Client {
pub fn new(host: &str, port: &str) -> Client {
let conn =
TcpStream::connect(format!("{}:{}", host, port)).expect("failed to open tcp stream");
Client {
conn,
next: AtomicI32::new(0),
auth: AtomicBool::new(false),
}
}
pub fn auth(&mut self, password: &str) -> Result<(), ()> {
self.send(password, Some(PacketType::Auth))?;
self.auth = AtomicBool::new(true);
let mut response = [0u8; MAX_PACKET];
self.conn.read(&mut response).unwrap();
let _ = Packet::decode(response.to_vec());
Ok(())
}
fn next_id(&mut self) -> i32 {
let new = self.next.load(Relaxed) + 1;
self.next.store(new, Relaxed);
new
}
pub fn send(&mut self, cmd: &str, msg_type: Option<PacketType>) -> Result<(), ()> {
let msg_type = match msg_type {
Some(v) => v,
None => PacketType::Cmd,
};
let message = Packet {
len: (cmd.len() + HEADER_LEN) as i32,
id: self.next_id(),
packet_type: msg_type as i32,
body: cmd.to_string(),
};
self.conn.write_all(&message.encode()).unwrap();
Ok(())
}
}
impl Packet {
pub fn encode(&self) -> Vec<u8> {
let mut data: Vec<u8> = Vec::new();
let p = self;
let extends = vec![
p.len.to_le_bytes(),
p.id.to_le_bytes(),
p.packet_type.to_le_bytes(),
];
for i in extends {
data.extend_from_slice(&i);
}
data.extend_from_slice(&p.body.as_bytes());
data.extend_from_slice(&[0, 0]);
data
}
pub fn decode(data: Vec<u8>) -> Packet {
let len = i32::from_le_bytes(data[0..4].try_into().unwrap());
let id = i32::from_le_bytes(data[0..4].try_into().unwrap());
let packet_type = i32::from_le_bytes(data[8..12].try_into().unwrap());
let mut body = "".to_string();
let body_len: usize = (len - 10).try_into().unwrap();
if body_len > 0 {
body = from_utf8(&data[12..12 + body_len]).unwrap().to_string();
}
Packet {
len,
id,
packet_type,
body,
}
}
}