meilies 0.2.0

The RESP and MeiliES protocol types
Documentation
use std::fmt;
use crate::stream::{StreamName, EventNumber, EventData, EventName};
use crate::resp::{RespValue, FromResp};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Response {
    Ok,
    Subscribed { stream: StreamName },
    Event { stream: StreamName, number: EventNumber, event_name: EventName, event_data: EventData },
    LastEventNumber { stream: StreamName, number: Option<EventNumber> },
    StreamNames { streams: Vec<StreamName> },
}

impl Into<RespValue> for Response {
    fn into(self) -> RespValue {
        match self {
            Response::Ok => {
                RespValue::string("OK")
            },
            Response::Subscribed { stream } => {
                RespValue::Array(vec![
                    RespValue::string("subscribed"),
                    RespValue::string(stream),
                ])
            },
            Response::Event { stream, number, event_name, event_data } => {
                RespValue::Array(vec![
                    RespValue::string("event"),
                    RespValue::string(stream),
                    RespValue::Integer(number.0 as i64),
                    RespValue::string(event_name),
                    RespValue::bulk_string(event_data.0),
                ])
            },
            Response::LastEventNumber { stream, number } => {
                let number = match number {
                    Some(number) => RespValue::Integer(number.0 as i64),
                    None => RespValue::Nil,
                };

                RespValue::Array(vec![
                    RespValue::string("last-event-number"),
                    RespValue::string(stream),
                    number,
                ])
            },
            Response::StreamNames { streams } => {
                let command = RespValue::string("stream-names");
                let streams = streams.into_iter().map(|s| RespValue::SimpleString(s.into_inner()));
                let args = Some(command).into_iter().chain(streams).collect();
                RespValue::Array(args)
            }
        }
    }
}

#[derive(Debug)]
pub enum RespResponseConvertError {
    InvalidResponseRespType,
    InvalidArgumentRespType,
    MissingTypeName,
    UnknownTypeName,
    MissingArgument,
    TooManyArguments,
}

impl fmt::Display for RespResponseConvertError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use RespResponseConvertError::*;
        match self {
            InvalidResponseRespType => write!(f, "Invalid response resp type"),
            InvalidArgumentRespType => write!(f, "Invalid argument resp type"),
            MissingTypeName => write!(f, "Missing type name"),
            UnknownTypeName => write!(f, "Unknown type name"),
            MissingArgument => write!(f, "Missing argument"),
            TooManyArguments => write!(f, "Too many arguments"),
        }
    }
}

impl FromResp for Response {
    type Error = RespResponseConvertError;

    fn from_resp(value: RespValue) -> Result<Self, Self::Error> {
        use RespResponseConvertError::*;

        let mut iter = match value {
            RespValue::SimpleString(ref text) if text == "OK" => return Ok(Response::Ok),
            RespValue::Array(array) => array.into_iter(),
            _otherwise => return Err(InvalidResponseRespType),
        };

        let response_type = iter.next().map(String::from_resp)
            .ok_or(MissingTypeName)?
            .map_err(|_| InvalidArgumentRespType)?;

        match response_type.as_str() {
            "subscribed" => {
                let stream = iter.next().map(StreamName::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                if iter.next().is_some() {
                    return Err(TooManyArguments)
                }

                Ok(Response::Subscribed { stream })
            },
            "event" => {
                let stream = iter.next().map(StreamName::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                let number = iter.next().map(EventNumber::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                let event_name = iter.next().map(EventName::from_resp)
                        .ok_or(MissingArgument)?
                        .map_err(|_| InvalidArgumentRespType)?;

                let event_data = iter.next().map(EventData::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                if iter.next().is_some() {
                    return Err(TooManyArguments)
                }

                Ok(Response::Event { stream, number, event_name, event_data })
            },
            "last-event-number" => {
                let stream = iter.next().map(StreamName::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                let number = iter.next().map(FromResp::from_resp)
                    .ok_or(MissingArgument)?
                    .map_err(|_| InvalidArgumentRespType)?;

                if iter.next().is_some() {
                    return Err(TooManyArguments)
                }

                Ok(Response::LastEventNumber { stream, number })
            },
            "stream-names" => {
                match iter.map(StreamName::from_resp).collect() {
                    Ok(streams) => Ok(Response::StreamNames { streams }),
                    Err(_) => Err(InvalidArgumentRespType),
                }
            }
            _otherwise => Err(UnknownTypeName),
        }
    }
}