use {Status, ApplicationId, Error, ErrorKind};
use wire;
use protobuf;
use json;
const PROTOCOL_VERSION: wire::CastMessage_ProtocolVersion = wire::CastMessage_ProtocolVersion::CASTV2_1_0;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Namespace(pub String);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EndpointName(pub String);
#[derive(Clone, Debug)]
pub struct Message
{
pub source: EndpointName,
pub destination: EndpointName,
pub namespace: Namespace,
pub kind: MessageKind,
}
#[derive(Clone, Debug)]
pub enum MessageKind
{
Connect,
Close,
Ping,
Pong,
GetStatus,
Launch {
app_id: ApplicationId,
request_id: i64,
},
ReceiverStatus(Status),
}
impl Message
{
pub fn from_raw_bytes(data: &[u8]) -> Result<Self, Error> {
let wire_message: wire::CastMessage = protobuf::parse_from_bytes(data)?;
let message = Message::from_wire_message(&wire_message)?;
Ok(message)
}
pub fn from_wire_message(message: &wire::CastMessage) -> Result<Self, Error> {
let kind = match message.get_payload_type() {
wire::CastMessage_PayloadType::STRING => {
let data = json::parse(message.get_payload_utf8()).unwrap();
let type_name = data["type"].as_str().unwrap();
match type_name {
"CONNECT" => MessageKind::Connect,
"CLOSE" => MessageKind::Close,
"PING" => MessageKind::Ping,
"PONG" => MessageKind::Pong,
"GET_STATUS" => MessageKind::GetStatus,
"LAUNCH" => unimplemented!(),
"RECEIVER_STATUS" => {
let status_data = &data["status"];
let status = Status::from_json(&status_data)?;
MessageKind::ReceiverStatus(status)
},
_ => return Err(ErrorKind::UnknownMessageType(type_name.to_owned()).into()),
}
},
wire::CastMessage_PayloadType::BINARY => {
return Err(ErrorKind::UnknownMessageType("binary message".to_owned()).into());
},
};
Ok(Message {
source: EndpointName(message.get_source_id().to_owned()),
destination: EndpointName(message.get_destination_id().to_owned()),
namespace: Namespace(message.get_namespace().to_owned()),
kind: kind,
})
}
pub fn as_raw_bytes(&self) -> Result<Vec<u8>, Error> {
use protobuf::Message;
let bytes = self.as_wire_message().write_to_bytes()?;
Ok(bytes)
}
pub fn as_wire_message(&self) -> wire::CastMessage {
let mut message = wire::CastMessage::new();
message.set_protocol_version(PROTOCOL_VERSION);
message.set_source_id(self.source.0.clone());
message.set_destination_id(self.destination.0.clone());
message.set_namespace(self.namespace.0.clone());
match self.kind {
MessageKind::Connect => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8("{ \"type\": \"CONNECT\" }".to_owned());
},
MessageKind::Close => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8("{ \"type\": \"CLOSE\" }".to_owned());
},
MessageKind::Ping => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8("{\"type\":\"PING\"}".to_owned());
},
MessageKind::Pong => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8("{\"type\":\"PONG\"}".to_owned());
},
MessageKind::GetStatus => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8("{ \"type\": \"GET_STATUS\" }".to_owned());
},
MessageKind::Launch { ref app_id, request_id } => {
message.set_payload_type(wire::CastMessage_PayloadType::STRING);
message.set_payload_utf8(json::stringify(object! {
"type" => "LAUNCH",
"appId" => &app_id.0[..],
"requestId" => request_id
}));
},
MessageKind::ReceiverStatus(..) => unimplemented!(),
}
message
}
}
impl EndpointName
{
pub fn is_broadcast(&self) -> bool { self.0 == "*" }
}