use std::borrow::Cow;
use std::convert::Into;
use std::io::{Read, Write};
use std::rc::Rc;
use std::str::FromStr;
use std::string::ToString;
use serde_json;
use cast::proxies;
use errors::Error;
use message_manager::{CastMessage, CastMessagePayload, MessageManager};
const CHANNEL_NAMESPACE: &str = "urn:x-cast:com.google.cast.receiver";
const MESSAGE_TYPE_LAUNCH: &str = "LAUNCH";
const MESSAGE_TYPE_STOP: &str = "STOP";
const MESSAGE_TYPE_GET_STATUS: &str = "GET_STATUS";
const MESSAGE_TYPE_SET_VOLUME: &str = "SET_VOLUME";
const MESSAGE_TYPE_RECEIVER_STATUS: &str = "RECEIVER_STATUS";
const MESSAGE_TYPE_LAUNCH_ERROR: &str = "LAUNCH_ERROR";
const MESSAGE_TYPE_INVALID_REQUEST: &str = "INVALID_REQUEST";
const APP_DEFAULT_MEDIA_RECEIVER_ID: &str = "CC1AD845";
const APP_BACKDROP_ID: &str = "E8C28D3C";
const APP_YOUTUBE_ID: &str = "233637DE";
#[derive(Debug)]
pub struct Volume {
pub level: Option<f32>,
pub muted: Option<bool>,
}
impl Into<Volume> for f32 {
fn into(self) -> Volume {
Volume {
level: Some(self),
muted: None,
}
}
}
impl Into<Volume> for bool {
fn into(self) -> Volume {
Volume {
level: None,
muted: Some(self),
}
}
}
impl Into<Volume> for (f32, bool) {
fn into(self) -> Volume {
Volume {
level: Some(self.0),
muted: Some(self.1),
}
}
}
#[derive(Debug)]
pub struct Application {
pub app_id: String,
pub session_id: String,
pub transport_id: String,
pub namespaces: Vec<String>,
pub display_name: String,
pub status_text: String,
}
#[derive(Debug)]
pub struct Status {
pub request_id: i32,
pub applications: Vec<Application>,
pub is_active_input: bool,
pub is_stand_by: bool,
pub volume: Volume,
}
#[derive(Debug)]
pub struct LaunchError {
pub request_id: i32,
pub reason: Option<String>,
}
#[derive(Debug)]
pub struct InvalidRequest {
pub request_id: i32,
pub reason: Option<String>,
}
#[derive(Debug)]
pub enum ReceiverResponse {
Status(Status),
LaunchError(LaunchError),
InvalidRequest(InvalidRequest),
NotImplemented(String, serde_json::Value),
}
#[derive(Debug, PartialEq, Clone)]
pub enum CastDeviceApp {
DefaultMediaReceiver,
Backdrop,
YouTube,
Custom(String),
}
impl FromStr for CastDeviceApp {
type Err = ();
fn from_str(s: &str) -> Result<CastDeviceApp, ()> {
let app = match s {
APP_DEFAULT_MEDIA_RECEIVER_ID | "default" => CastDeviceApp::DefaultMediaReceiver,
APP_BACKDROP_ID | "backdrop" => CastDeviceApp::Backdrop,
APP_YOUTUBE_ID | "youtube" => CastDeviceApp::YouTube,
custom => CastDeviceApp::Custom(custom.to_string()),
};
Ok(app)
}
}
impl ToString for CastDeviceApp {
fn to_string(&self) -> String {
match *self {
CastDeviceApp::DefaultMediaReceiver => APP_DEFAULT_MEDIA_RECEIVER_ID.to_string(),
CastDeviceApp::Backdrop => APP_BACKDROP_ID.to_string(),
CastDeviceApp::YouTube => APP_YOUTUBE_ID.to_string(),
CastDeviceApp::Custom(ref app_id) => app_id.to_string(),
}
}
}
pub struct ReceiverChannel<'a, W>
where
W: Write + Read,
{
sender: Cow<'a, str>,
receiver: Cow<'a, str>,
message_manager: Rc<MessageManager<W>>,
}
impl<'a, W> ReceiverChannel<'a, W>
where
W: Write + Read,
{
pub fn new<S>(
sender: S,
receiver: S,
message_manager: Rc<MessageManager<W>>,
) -> ReceiverChannel<'a, W>
where
S: Into<Cow<'a, str>>,
{
ReceiverChannel {
sender: sender.into(),
receiver: receiver.into(),
message_manager: message_manager,
}
}
pub fn launch_app(&self, app: &CastDeviceApp) -> Result<Application, Error> {
let request_id = self.message_manager.generate_request_id();
let payload = serde_json::to_string(&proxies::receiver::AppLaunchRequest {
typ: MESSAGE_TYPE_LAUNCH.to_string(),
request_id: request_id,
app_id: app.to_string(),
})?;
self.message_manager.send(CastMessage {
namespace: CHANNEL_NAMESPACE.to_string(),
source: self.sender.to_string(),
destination: self.receiver.to_string(),
payload: CastMessagePayload::String(payload),
})?;
self.message_manager.receive_find_map(|message| {
if !self.can_handle(message) {
return Ok(None);
}
match self.parse(message)? {
ReceiverResponse::Status(mut status) => if status.request_id == request_id {
return Ok(Some(status.applications.remove(0)));
},
ReceiverResponse::LaunchError(error) => if error.request_id == request_id {
return Err(Error::Internal(format!(
"Could not run application ({}).",
error.reason.unwrap_or_else(|| "Unknown".to_string())
)));
},
_ => {}
}
Ok(None)
})
}
pub fn stop_app<S>(&self, session_id: S) -> Result<(), Error>
where
S: Into<Cow<'a, str>>,
{
let request_id = self.message_manager.generate_request_id();
let payload = serde_json::to_string(&proxies::receiver::AppStopRequest {
typ: MESSAGE_TYPE_STOP.to_string(),
request_id: request_id,
session_id: session_id.into(),
})?;
self.message_manager.send(CastMessage {
namespace: CHANNEL_NAMESPACE.to_string(),
source: self.sender.to_string(),
destination: self.receiver.to_string(),
payload: CastMessagePayload::String(payload),
})?;
self.message_manager.receive_find_map(|message| {
if !self.can_handle(message) {
return Ok(None);
}
match self.parse(message)? {
ReceiverResponse::Status(status) => if status.request_id == request_id {
return Ok(Some(()));
},
ReceiverResponse::InvalidRequest(error) => if error.request_id == request_id {
return Err(Error::Internal(format!(
"Invalid request ({}).",
error.reason.unwrap_or_else(|| "Unknown".to_string())
)));
},
_ => {}
}
Ok(None)
})
}
pub fn get_status(&self) -> Result<Status, Error> {
let request_id = self.message_manager.generate_request_id();
let payload = serde_json::to_string(&proxies::receiver::GetStatusRequest {
typ: MESSAGE_TYPE_GET_STATUS.to_string(),
request_id: request_id,
})?;
self.message_manager.send(CastMessage {
namespace: CHANNEL_NAMESPACE.to_string(),
source: self.sender.to_string(),
destination: self.receiver.to_string(),
payload: CastMessagePayload::String(payload),
})?;
self.message_manager.receive_find_map(|message| {
if !self.can_handle(message) {
return Ok(None);
}
let message = self.parse(message)?;
if let ReceiverResponse::Status(status) = message {
if status.request_id == request_id {
return Ok(Some(status));
}
}
Ok(None)
})
}
pub fn set_volume<T>(&self, volume: T) -> Result<Volume, Error>
where
T: Into<Volume>,
{
let request_id = self.message_manager.generate_request_id();
let volume = volume.into();
let payload = serde_json::to_string(&proxies::receiver::SetVolumeRequest {
typ: MESSAGE_TYPE_SET_VOLUME.to_string(),
request_id: request_id,
volume: proxies::receiver::Volume {
level: volume.level,
muted: volume.muted,
},
})?;
self.message_manager.send(CastMessage {
namespace: CHANNEL_NAMESPACE.to_string(),
source: self.sender.to_string(),
destination: self.receiver.to_string(),
payload: CastMessagePayload::String(payload),
})?;
self.message_manager.receive_find_map(|message| {
if !self.can_handle(message) {
return Ok(None);
}
let message = self.parse(message)?;
if let ReceiverResponse::Status(status) = message {
if status.request_id == request_id {
return Ok(Some(status.volume));
}
}
Ok(None)
})
}
pub fn can_handle(&self, message: &CastMessage) -> bool {
message.namespace == CHANNEL_NAMESPACE
}
pub fn parse(&self, message: &CastMessage) -> Result<ReceiverResponse, Error> {
let reply = match message.payload {
CastMessagePayload::String(ref payload) => {
serde_json::from_str::<serde_json::Value>(payload)?
}
_ => {
return Err(Error::Internal(
"Binary payload is not supported!".to_string(),
))
}
};
let message_type = reply
.as_object()
.and_then(|object| object.get("type"))
.and_then(|property| property.as_str())
.unwrap_or("")
.to_string();
let response = match message_type.as_ref() {
MESSAGE_TYPE_RECEIVER_STATUS => {
let status_reply: proxies::receiver::StatusReply =
serde_json::value::from_value(reply)?;
let status = Status {
request_id: status_reply.request_id,
applications: status_reply
.status
.applications
.iter()
.map(|app| {
Application {
app_id: app.app_id.clone(),
session_id: app.session_id.clone(),
transport_id: app.transport_id.clone(),
namespaces: app.namespaces
.iter()
.map(|ns| ns.name.clone())
.collect::<Vec<String>>(),
display_name: app.display_name.clone(),
status_text: app.status_text.clone(),
}
})
.collect::<Vec<Application>>(),
is_active_input: status_reply.status.is_active_input,
is_stand_by: status_reply.status.is_stand_by,
volume: Volume {
level: status_reply.status.volume.level,
muted: status_reply.status.volume.muted,
},
};
ReceiverResponse::Status(status)
}
MESSAGE_TYPE_LAUNCH_ERROR => {
let reply: proxies::receiver::LaunchErrorReply =
serde_json::value::from_value(reply)?;
ReceiverResponse::LaunchError(LaunchError {
request_id: reply.request_id,
reason: reply.reason,
})
}
MESSAGE_TYPE_INVALID_REQUEST => {
let reply: proxies::receiver::InvalidRequestReply =
serde_json::value::from_value(reply)?;
ReceiverResponse::InvalidRequest(InvalidRequest {
request_id: reply.request_id,
reason: reply.reason,
})
}
_ => ReceiverResponse::NotImplemented(message_type.to_string(), reply),
};
Ok(response)
}
}