opencomputers-stem 0.2.0

Library for Stem bridge
Documentation
#![allow(dead_code)]
#![allow(unused_variables)]


use tokio::net::{TcpStream, ToSocketAddrs, tcp::OwnedReadHalf, tcp::OwnedWriteHalf};
use tokio_util::codec::{Framed, FramedRead, FramedWrite, Decoder, Encoder};
use bytes::{BufMut, BytesMut, Buf};


mod default_server;


pub struct Stem {
    stream: TcpStream
}

impl Stem {
    pub async fn new<T: ToSocketAddrs>(addr: T) -> std::io::Result<Stem> {
        Ok(Stem {
            stream: TcpStream::connect(addr).await?
        })
    }
    pub async fn with_server() -> std::io::Result<Stem> {
        Ok(Stem {
            stream: TcpStream::connect(default_server::DEFAULT_SERVER).await?
        })
    }

    pub fn framed_split(self) -> (FramedRead<OwnedReadHalf, StemCodec>, FramedWrite<OwnedWriteHalf, StemCodec>) {
        let (read, write) = self.stream.into_split();
        (FramedRead::new(read, StemCodec), FramedWrite::new(write, StemCodec))
    }

    pub fn framed(self) -> Framed<TcpStream, StemCodec> {
        Framed::new(self.stream, StemCodec {})
    }
}

pub struct StemCodec;

impl Decoder for StemCodec {

    type Item = StemPacket;
    type Error = std::io::Error;

    fn decode(&mut self, src: &mut bytes::BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if src.len() < 2 {
            return Ok(None)
        }
        let size = src.get_u16();
        let id = src.get_u8();
        match id {
            0x00 => {
                let channel_len = src.get_u8();

                let mut buf = vec![0; channel_len as usize];
                src.copy_to_slice(buf.as_mut_slice());
                let channel = String::from_utf8(buf).unwrap();

                let mut buf = vec![0; (size - 3 - channel_len as u16) as usize];
                src.copy_to_slice(buf.as_mut_slice());
                let message = String::from_utf8(buf).unwrap();

                Ok(Some(StemPacket::Message(channel, message)))
            }
            0x01 | 0x02 => {
                let channel_len = src.get_u8();

                let mut buf = vec![0; channel_len as usize];
                src.copy_to_slice(buf.as_mut_slice());
                let channel = String::from_utf8(buf).unwrap();

                if id == 0x01 {
                    Ok(Some(StemPacket::Sub(channel)))
                } else {
                    Ok(Some(StemPacket::UnSub(channel)))
                }
            }
            0x03 | 0x04 => {
                let mut buf = vec![0; usize::from(size - 1)];
                src.copy_to_slice(buf.as_mut_slice());
                if id == 0x03 {
                    Ok(Some(StemPacket::Ping(buf)))
                } else {
                    Ok(Some(StemPacket::Pong(buf)))
                }
            }
            _ => {
                Ok(Some(StemPacket::Unknown))
            }
        }
    }
    
}

impl Encoder<StemPacket> for StemCodec {

    type Error = std::io::Error;

    fn encode(&mut self, item: StemPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
        match item {
            StemPacket::Message(channel, message) => {
                let size = 2 + channel.len() + message.len();
                dst.put_u16(size as u16);
                dst.put_u8(0x00);
                dst.put_u8(channel.len() as u8);
                dst.put_slice(channel.as_bytes());
                dst.put_slice(message.as_bytes());
            }
            StemPacket::Sub(channel) => {
                let size = 2 + channel.len();
                dst.put_u16(size as u16);
                dst.put_u8(0x01);
                dst.put_u8(channel.len() as u8);
                dst.put_slice(channel.as_bytes());
            }
            StemPacket::UnSub(channel) => {
                let size = 2 + channel.len();
                dst.put_u16(size as u16);
                dst.put_u8(0x02);
                dst.put_u8(channel.len() as u8);
                dst.put_slice(channel.as_bytes());
            }
            StemPacket::Ping(data) => {
                let size = 1 + data.len();
                dst.put_u16(size as u16);
                dst.put_u8(0x03);
                dst.put_slice(data.as_slice());
            }
            StemPacket::Pong(data) => {
                let size = 1 + data.len();
                dst.put_u16(size as u16);
                dst.put_u8(0x03);
                dst.put_slice(data.as_slice());
            }
            _ => {}
        }
        Ok(())
    }

}

#[derive(Debug)]
pub enum StemPacket {
    Message(String, String),
    Sub(String),
    UnSub(String),
    Ping(Vec<u8>),
    Pong(Vec<u8>),
    Unknown
}