redisx 0.0.2

Minimal and Asynchronous Redis client for Rust.
Documentation
use crate::io;
use crate::url::Url;
use anyhow::bail;
use async_std::io::prelude::ReadExt;
use async_std::io::prelude::WriteExt;
use async_std::io::Read;
use async_std::io::Write;
use async_std::net::TcpStream;
use async_tls::TlsConnector;

pub struct RawConnection {
    // TODO: Merge these two buffers
    wbuf: Vec<u8>,
    rbuf: Vec<u8>,

    // TODO: Generalize between TLS and PLAIN
    stream: Box<dyn ReadWrite + Unpin>,
}

trait ReadWrite: Read + Write + Send {}
impl<T: Read + Write + Send> ReadWrite for T {}

impl RawConnection {
    pub async fn open(url: &str) -> crate::Result<Self> {
        let url = Url::parse(url)?;

        let tcp_stream = TcpStream::connect((url.host(), url.port())).await?;

        let stream = if url.scheme() == "rediss" || url.scheme() == "redis+tls" {
            let connector = TlsConnector::default();
            let handshake = connector.connect(url.host(), tcp_stream)?;
            let enc_stream = handshake.await?;

            Box::new(enc_stream) as Box<dyn ReadWrite + Unpin>
        } else {
            Box::new(tcp_stream) as Box<dyn ReadWrite + Unpin>
        };

        let mut conn = Self {
            stream,
            wbuf: Vec::with_capacity(4096),
            rbuf: vec![0; 4096],
        };

        if url.database() != "0" {
            conn.send(&["SELECT", url.database()]).await?;
            conn.receive().await?;
        }

        if let Some(password) = url.password() {
            conn.send(&["AUTH", password]).await?;
            conn.receive().await?;
        }

        Ok(conn)
    }

    pub(crate) async fn send(&mut self, command: &[&str]) -> crate::Result<()> {
        self.wbuf.clear();

        log::trace!("send: {:?}", command);

        io::write_array_start(&mut self.wbuf, command.len());

        for arg in command {
            io::write_str(&mut self.wbuf, arg);
        }

        self.stream.write_all(&self.wbuf).await?;
        self.stream.flush().await?;

        Ok(())
    }

    pub(crate) async fn receive(&mut self) -> crate::Result<&[u8]> {
        // TODO: Handle protocol errors

        let n = self.stream.read(&mut self.rbuf).await?;

        if n == 0 {
            // Server closed our connection
            bail!("server closed the connection (EOF)");
        }

        log::trace!("recv: {:?}", ::std::str::from_utf8(&self.rbuf[..n])?);

        Ok(&self.rbuf[..n])
    }
}