extern crate self as ts3;
mod client;
pub mod event;
pub mod request;
pub mod response;
pub mod shared;
mod types;
pub use async_trait::async_trait;
pub use client::Client;
pub use ts3_derive::Decode;
use std::{
convert::{Infallible, TryFrom},
fmt::{Debug, Write},
io,
num::ParseIntError,
str::{from_utf8, Utf8Error},
};
use thiserror::Error;
#[derive(Debug, Error)]
#[error(transparent)]
pub struct Error(ErrorKind);
impl From<Infallible> for Error {
fn from(value: Infallible) -> Self {
match value {}
}
}
#[derive(Debug, Error)]
enum ErrorKind {
#[error("TS3 error {id}: {msg}")]
TS3 { id: u16, msg: String },
#[error("io: {0}")]
Io(#[from] io::Error),
#[error("failed to decode stream: {0}")]
Decode(#[from] DecodeError),
#[error("failed to parse integer: {0}")]
ParseInt(#[from] ParseIntError),
#[error("recevied invalid utf8: {0}")]
Utf8(#[from] Utf8Error),
#[error("send error")]
SendError,
#[error("no field")]
NoField,
}
#[derive(Debug, Error)]
enum DecodeError {
#[error("unexpected eof")]
UnexpectedEof,
#[error("unexpected byte: {0}")]
UnexpectedByte(u8),
#[error("invalid reasonid: {0}")]
InvalidReasonId(u8),
#[error("invalid apikey scope: {0}")]
InvalidApiKeyScope(String),
}
pub trait Decode: Sized {
type Error: std::error::Error;
fn decode(buf: &[u8]) -> Result<Self, Self::Error>;
}
pub trait Encode {
fn encode(&self, buf: &mut String);
}
macro_rules! impl_serialize {
($t:ty) => {
impl crate::Encode for $t {
fn encode(&self, writer: &mut ::std::string::String) {
write!(writer, "{}", self).unwrap();
}
}
};
}
macro_rules! impl_decode {
($t:ty) => {
impl Decode for $t {
type Error = Error;
fn decode(buf: &[u8]) -> std::result::Result<$t, Self::Error> {
Ok(from_utf8(buf)
.map_err(|e| Error(ErrorKind::Utf8(e)))?
.parse()
.map_err(|e| Error(ErrorKind::ParseInt(e)))?)
}
}
};
}
impl Decode for () {
type Error = Infallible;
fn decode(_: &[u8]) -> Result<(), Self::Error> {
Ok(())
}
}
impl Decode for String {
type Error = Error;
fn decode(buf: &[u8]) -> Result<String, Self::Error> {
let mut string = String::with_capacity(buf.len());
let mut iter = buf.into_iter().peekable();
while let Some(b) = iter.next() {
match b {
b'\\' => {
match iter.peek() {
Some(c) => match c {
b'\\' => string.push('\\'),
b'/' => string.push('/'),
b's' => string.push(' '),
b'p' => string.push('|'),
b'a' => string.push(7u8 as char),
b'b' => string.push(8u8 as char),
b'f' => string.push(12u8 as char),
b'n' => string.push(10u8 as char),
b'r' => string.push(13u8 as char),
b't' => string.push(9u8 as char),
b'v' => string.push(11u8 as char),
_ => {
return Err(Error(ErrorKind::Decode(DecodeError::UnexpectedByte(
**c,
))))
}
},
None => {
return Err(Error(ErrorKind::Decode(DecodeError::UnexpectedEof.into())))
}
}
iter.next();
}
_ => string.push(char::try_from(*b).unwrap()),
}
}
string.shrink_to_fit();
Ok(string)
}
}
impl Encode for &str {
fn encode(&self, writer: &mut String) {
for c in self.chars() {
match c {
'\\' => writer.write_str("\\\\").unwrap(),
'/' => writer.write_str("\\/").unwrap(),
' ' => writer.write_str("\\s").unwrap(),
'|' => writer.write_str("\\p").unwrap(),
c if c == 7u8 as char => writer.write_str("\\a").unwrap(),
c if c == 8u8 as char => writer.write_str("\\b").unwrap(),
c if c == 12u8 as char => writer.write_str("\\f").unwrap(),
c if c == 10u8 as char => writer.write_str("\\n").unwrap(),
c if c == 13u8 as char => writer.write_str("\\r").unwrap(),
c if c == 9u8 as char => writer.write_str("\\t").unwrap(),
c if c == 11u8 as char => writer.write_str("\\v").unwrap(),
_ => writer.write_char(c).unwrap(),
}
}
}
}
impl Encode for bool {
fn encode(&self, writer: &mut String) {
write!(
writer,
"{}",
match self {
false => b'0',
true => b'1',
}
)
.unwrap();
}
}
impl Decode for bool {
type Error = Error;
fn decode(buf: &[u8]) -> Result<bool, Self::Error> {
match buf.get(0) {
Some(b) => match b {
b'0' => Ok(true),
b'1' => Ok(false),
_ => Err(Error(ErrorKind::Decode(
DecodeError::UnexpectedByte(*b).into(),
))),
},
None => Err(Error(ErrorKind::Decode(DecodeError::UnexpectedEof.into()))),
}
}
}
impl_decode!(isize);
impl_decode!(i8);
impl_decode!(i16);
impl_decode!(i32);
impl_decode!(i64);
impl_decode!(i128);
impl_decode!(usize);
impl_decode!(u8);
impl_decode!(u16);
impl_decode!(u32);
impl_decode!(u64);
impl_decode!(u128);
impl_serialize!(isize);
impl_serialize!(i8);
impl_serialize!(i16);
impl_serialize!(i32);
impl_serialize!(i64);
impl_serialize!(i128);
impl_serialize!(usize);
impl_serialize!(u8);
impl_serialize!(u16);
impl_serialize!(u32);
impl_serialize!(u64);
impl_serialize!(u128);
impl Error {
fn decode(buf: &[u8]) -> Result<Error, Error> {
let (mut id, mut msg) = (0, String::new());
for s in buf.split(|c| *c == b' ') {
if s == b"error" {
continue;
}
let parts: Vec<&[u8]> = s.splitn(2, |c| *c == b'=').collect();
match parts.get(0) {
Some(key) => {
let val = match parts.get(1) {
Some(val) => val,
None => return Err(Error(ErrorKind::Decode(DecodeError::UnexpectedEof))),
};
match *key {
b"id" => {
id = u16::decode(val)?;
}
b"msg" => {
msg = String::decode(val)?;
}
_ => (),
}
}
None => return Err(Error(ErrorKind::Decode(DecodeError::UnexpectedEof))),
}
}
Ok(Error(ErrorKind::TS3 { id, msg }))
}
}
#[cfg(test)]
mod tests {
use super::{Decode, Error, ErrorKind};
#[test]
fn test_string_decode() {
let buf = b"Hello\\sWorld!";
assert_eq!(String::decode(buf).unwrap(), "Hello World!".to_owned());
}
#[test]
fn test_error_decode() {
let buf = b"error id=0 msg=ok";
let (id, msg) = match Error::decode(buf).unwrap().0 {
ErrorKind::TS3 { id, msg } => (id, msg),
_ => unreachable!(),
};
assert!(id == 0 && msg == "ok".to_owned());
}
}