rust_cast 0.3.0

Library that allows you to communicate with Google Cast enabled devices (eg. Chromecast).
Documentation
use std::borrow::Cow;
use std::cell::RefCell;
use std::io::Write;
use std::rc::Rc;

use serde_json::Value;
use serde_json::value::from_value;

use cast::cast_channel;
use errors::Error;
use message_manager::MessageManager;

const CHANNEL_NAMESPACE: &'static str = "urn:x-cast:com.google.cast.media";

const MESSAGE_TYPE_LOAD: &'static str = "LOAD";

const REPLY_TYPE_MEDIA_STATUS: &'static str = "MEDIA_STATUS";
const REPLY_TYPE_LOAD_CANCELLED: &'static str = "LOAD_CANCELLED";

pub enum StreamType {
    None,
    Buffered,
    Live,
}

#[derive(Serialize, Debug)]
pub struct MediaRequest<'a> {
    #[serde(rename="requestId")]
    pub request_id: i32,

    #[serde(rename="sessionId")]
    pub session_id: Cow<'a, str>,

    #[serde(rename="type")]
    pub typ: String,

    pub media: Media<'a>,

    #[serde(rename="currentTime")]
    pub current_time: f64,

    #[serde(rename="customData")]
    pub custom_data: CustomData,

    pub autoplay: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Media<'a> {
    #[serde(rename="contentId")]
    pub content_id: Cow<'a, str>,

    #[serde(rename="streamType", default)]
    pub stream_type: Cow<'a, str>,

    #[serde(rename="contentType")]
    pub content_type: Cow<'a, str>,
}

#[derive(Serialize, Debug)]
pub struct CustomData {
    #[serde(skip_serializing)]
    private: (),
}

impl CustomData {
    pub fn new() -> CustomData {
        CustomData { private: () }
    }
}

#[derive(Deserialize, Debug)]
pub struct MediaStatus<'a> {
    #[serde(default)]
    pub media: Option<Media<'a>>,
}

#[derive(Deserialize, Debug)]
pub struct MediaStatusReply<'a> {
    #[serde(rename="requestId", default)]
    pub request_id: i32,

    #[serde(rename="type")]
    pub typ: String,

    pub status: Vec<MediaStatus<'a>>,
}

#[derive(Deserialize, Debug)]
pub struct LoadCancelledReply {
    #[serde(rename="requestId")]
    pub request_id: i32,

    #[serde(rename="type")]
    typ: String,
}

#[derive(Debug)]
pub enum MediaResponse<'a> {
    MediaStatus(MediaStatusReply<'a>),
    LoadCancelled(LoadCancelledReply),
    Unknown,
}

pub struct MediaChannel<'a, W> where W: Write {
    sender: Cow<'a, str>,
    writer: Rc<RefCell<W>>,
}

impl<'a, W> MediaChannel<'a, W> where W: Write {
    pub fn new<S>(sender: S, writer: Rc<RefCell<W>>)
        -> MediaChannel<'a, W> where S: Into<Cow<'a, str>> {
        MediaChannel {
            sender: sender.into(),
            writer: writer,
        }
    }

    pub fn load<S>(&self, receiver: S, session_id: S, content_id: S, content_type: S,
                   stream_type: StreamType) -> Result<(), Error> where S: Into<Cow<'a, str>> {

        let stream_type_string = match stream_type {
            StreamType::None => "NONE",
            StreamType::Buffered => "BUFFERED",
            StreamType::Live => "LIVE",
        };

        let payload = MediaRequest {
            request_id: 1,
            session_id: session_id.into(),
            typ: MESSAGE_TYPE_LOAD.to_owned(),

            media: Media {
                content_id: content_id.into(),
                stream_type: stream_type_string.into(),
                content_type: content_type.into(),
            },

            current_time: 0_f64,
            autoplay: true,
            custom_data: CustomData::new(),
        };

        let message = try!(MessageManager::create(CHANNEL_NAMESPACE.to_owned(),
                                                  self.sender.to_string(),
                                                  receiver.into().to_string(),
                                                  Some(payload)));

        MessageManager::send(&mut *self.writer.borrow_mut(), message)
    }

    pub fn can_handle(&self, message: &cast_channel::CastMessage) -> bool {
        message.get_namespace() == CHANNEL_NAMESPACE
    }

    pub fn parse(&self, message: &cast_channel::CastMessage) -> Result<MediaResponse, Error> {
        let reply: Value = try!(MessageManager::parse_payload(message));

        let message_type = match reply.as_object()
            .and_then(|object| object.get("type"))
            .and_then(|property| property.as_string()) {
            None => return Err(Error::Internal("Unexpected reply format".to_owned())),
            Some(string) => string.to_owned()
        };

        let reply = match &message_type as &str {
            REPLY_TYPE_MEDIA_STATUS => MediaResponse::MediaStatus(try!(from_value(reply))),
            REPLY_TYPE_LOAD_CANCELLED => MediaResponse::LoadCancelled(try!(from_value(reply))),
            _ => MediaResponse::Unknown,
        };

        Ok(reply)
    }
}