whatsappweb 0.0.2

WIP Whatsapp Web API Client
Documentation
use std::str;
use std::borrow::Cow;
use std::borrow::Borrow;
use std::ops::Deref;

use ws::Message;
use json;
use json::JsonValue;

#[derive(Copy, Clone, PartialEq)]
#[allow(dead_code)]
pub enum WebsocketMessageMetric {
    None = 0,
    DebugLog = 1,
    QueryResume = 2,
    QueryReceipt = 3,
    QueryMedia = 4,
    QueryChat = 5,
    QueryContacts = 6,
    QueryMessages = 7,
    Presence = 8,
    PresenceSubscribe = 9,
    Group = 10,
    Read = 11,
    Chat = 12,
    Received = 13,
    Pic = 14,
    Status = 15,
    Message = 16,
    QueryActions = 17,
    Block = 18,
    QueryGroup = 19,
    QueryPreview = 20,
    QueryEmoji = 21,
    QueryMessageInfo = 22,
    Spam = 23,
    QuerySearch = 24,
    QueryIdentity = 25,
    QueryUrl = 26,
    Profile = 27,
    Contact = 28,
    QueryVcard = 29,
    QueryStatus = 30,
    QueryStatusUpdate = 31,
    PrivacyStatus = 32,
    QueryLiveLocations = 33,
    LiveLocation = 34,
    QueryVname = 35,
    QueryLabels = 36,
    Call = 37,
    QueryCall = 38,
    QueryQuickReplies = 39
}

pub struct WebsocketMessage<'a> {
    pub tag: Cow<'a, str>,
    pub payload: WebsocketMessagePayload<'a>
}

pub enum WebsocketMessagePayload<'a> {
    Json(JsonValue),
    BinarySimple(&'a [u8]),
    BinaryEphemeral(WebsocketMessageMetric, &'a [u8]),
    Empty,
    Pong
}


impl<'a> WebsocketMessage<'a> {
    #[inline]
    pub fn serialize(&self) -> Message {
        match self.payload {
            WebsocketMessagePayload::Json(ref json) => {
                Message::Text([self.tag.deref(), ",", json.to_string().as_str()].concat())
            }
            WebsocketMessagePayload::BinarySimple(ref binary) => {
                Message::Binary([self.tag.deref().as_bytes(), b",", binary].concat())
            }
            WebsocketMessagePayload::BinaryEphemeral(metric, ref binary) => {
                if metric != WebsocketMessageMetric::None {
                    Message::Binary([self.tag.deref().as_bytes(), b",", &[metric as u8], b"\x80", binary].concat())
                } else {
                    Message::Binary([self.tag.deref().as_bytes(), b",,", binary].concat())
                }
            }
            WebsocketMessagePayload::Empty => {
                Message::Text([self.tag.borrow(), ","].concat())
            }
            WebsocketMessagePayload::Pong => unimplemented!()
        }
    }

    #[inline]
    pub fn deserialize(message: &'a Message) -> Result<WebsocketMessage<'a>, ()> {
        match *message {
            Message::Text(ref message) => {
                if let Some(sep) = message.find(',') {
                    let (tag_str, payload) = message.split_at(sep + 1);
                    let tag = Cow::Borrowed(tag_str.split_at(sep).0);

                    Ok(if payload.is_empty() {
                        WebsocketMessage { tag, payload: WebsocketMessagePayload::Empty }
                    } else {
                        WebsocketMessage { tag, payload: WebsocketMessagePayload::Json(json::parse(payload).map_err(|_| ())?) }
                    })
                } else if message.get(0..1).map_or(false, |first| first == "!") {
                    Ok(WebsocketMessage { tag: Cow::Borrowed(""), payload: WebsocketMessagePayload::Pong })
                } else {
                    Err(())
                }
            }
            Message::Binary(ref message) => {
                if let Some(sep) = message.iter().position(|x| x == &b',') {
                    Ok(WebsocketMessage {
                        tag: Cow::Borrowed(str::from_utf8(&message[..sep]).map_err(|_| ())?),
                        payload: WebsocketMessagePayload::BinarySimple(&message[(sep + 1)..])
                    })
                } else {
                    Err(())
                }
            }
        }
    }
}