use tokio::fs::File;
use tokio::io::AsyncReadExt;
type Error = Box<dyn std::error::Error + Send + Sync>;
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum StatusCode {
OkieDokie = 0,
Message = 1,
File = 2,
Success = 3,
Error = 4,
}
#[derive(Clone)]
pub struct BtpPackage {
pub header: BtpHeader,
pub body1: String,
pub body2: Option<String>,
}
#[derive(Clone)]
pub struct BtpHeader {
pub file_size: [u8; 4],
pub file_name_size: [u8; 2],
pub sc: [u8; 1],
pub version: [u8; 3],
}
impl BtpHeader {
pub fn from(
file_size: [u8; 4],
file_name_size: [u8; 2],
sc: [u8; 1],
version: [u8; 3],
) -> Self {
BtpHeader {
file_size,
file_name_size,
sc,
version,
}
}
pub fn from_raw(raw: [u8; 10]) -> Result<Self, Error> {
let res = BtpHeader {
file_size: raw[..4].try_into()?,
sc: raw[4..5].try_into()?,
version: raw[5..8].try_into()?,
file_name_size: raw[8..].try_into()?,
};
Ok(res)
}
pub fn get_sc(&self) -> StatusCode {
match self.sc {
[0] => StatusCode::OkieDokie,
[1] => StatusCode::Message,
[2] => StatusCode::File,
[3] => StatusCode::Success,
[4] => StatusCode::Error,
_ => StatusCode::Error,
}
}
pub fn get_len(&self) -> usize {
u32::from_be_bytes(self.file_size) as usize
}
pub fn get_file_name_len(&self) -> usize {
u16::from_be_bytes(self.file_name_size) as usize
}
pub fn as_raw(self: Self) -> [u8; 10] {
let mut header = [0u8; 10];
header[..4].copy_from_slice(&self.file_size);
header[4..5].copy_from_slice(&self.sc);
header[5..8].copy_from_slice(&self.version);
header[8..].copy_from_slice(&self.file_name_size);
header
}
}
impl BtpPackage {
pub fn from(
len: [u8; 4],
sc: [u8; 1],
version: [u8; 3],
body1: String,
body2: Option<String>,
) -> Self {
let header = BtpHeader::from(len, [0, 0], sc, version);
BtpPackage::from_header(header, body1, body2)
}
pub fn from_header(header: BtpHeader, body1: String, body2: Option<String>) -> Self {
BtpPackage {
header,
body1,
body2,
}
}
pub fn from_str(body: &str) -> Self {
let len = body.len() as u32;
BtpPackage::from(
len.to_be_bytes(),
[StatusCode::Message as u8],
super::VERSION,
body.to_string(),
None,
)
}
pub async fn from_file(file: &mut File, filename: String) -> Result<Self, Error> {
let mut file_content = String::new();
let file_len = file.read_to_string(&mut file_content).await? as u32;
Ok(BtpPackage {
header: BtpHeader::from(
file_len.to_be_bytes(),
(filename.len() as u16).to_be_bytes(),
[StatusCode::File as u8],
super::VERSION,
),
body1: file_content,
body2: Some(filename),
})
}
pub fn as_vec(self: Self) -> Vec<u8> {
let mut raw = Vec::new();
raw.extend_from_slice(&self.header.as_raw());
raw.extend_from_slice(self.body1.as_bytes());
match self.body2 {
Some(body2) => raw.extend_from_slice(body2.as_bytes()),
None => {}
}
raw
}
pub fn okie(ver: [u8; 3]) -> Self {
let okie = String::from("okie");
let len = (okie.len() as u32).to_be_bytes();
let header = BtpHeader::from(len, [0, 0], [0], ver);
BtpPackage::from_header(header, okie, None)
}
pub fn dokie(ver: [u8; 3]) -> Self {
let dokie = String::from("dokie");
let len = (dokie.len() as u32).to_be_bytes();
let header = BtpHeader::from(len, [0, 0], [0], ver);
BtpPackage::from_header(header, dokie, None)
}
}