rust/janus: Switch to a more exact JSON message data type
Currently all messages sent out are created via the json!
macro and all received messages could make use of more enums instead of using Option
s all over the place.
I've created the below as part of another project (untested so far). Just putting this here for comment. CC @philn
It's not complete with all possible messages (and also only videoroom), and also does not contain all possible fields, but should be sufficient.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "janus")]
#[serde(rename_all = "lowercase")]
pub enum Message {
Create {
transaction: String,
},
Destroy {
transaction: String,
session_id: u64,
},
Attach {
transaction: String,
session_id: u64,
plugin: String,
},
Detach {
transaction: String,
session_id: u64,
handle_id: u64,
},
Keepalive {
transaction: String,
session_id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
handle_id: Option<u64>,
},
Trickle {
transaction: String,
session_id: u64,
#[serde(skip_serializing_if = "Option::is_none")]
handle_id: Option<u64>,
#[serde(flatten)]
candidates: IceCandidates,
},
Message {
transaction: String,
#[serde(skip_serializing_if = "Option::is_none")]
sender: Option<u64>,
session_id: u64,
handle_id: u64,
body: MessageBody,
#[serde(skip_serializing_if = "Option::is_none")]
jsep: Option<Jsep>,
},
Success {
transaction: String,
#[serde(skip_serializing_if = "Option::is_none")]
sender: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
session_id: Option<u64>,
data: Option<SuccessData>,
plugindata: Option<Plugindata>,
},
Error {
transaction: String,
error: ErrorMessage,
},
Event {
#[serde(skip_serializing_if = "Option::is_none")]
transaction: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
session_id: Option<u64>,
sender: u64,
plugindata: Plugindata,
#[serde(skip_serializing_if = "Option::is_none")]
jsep: Option<Jsep>,
},
Ack {
transaction: String,
#[serde(skip_serializing_if = "Option::is_none")]
session_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
sender: Option<u64>,
},
WebRTCUp {
session_id: Option<u64>,
sender: Option<u64>,
},
Media {
session_id: Option<u64>,
sender: Option<u64>,
#[serde(rename = "type")]
type_: String,
receiving: bool,
},
SlowLink {
session_id: Option<u64>,
sender: Option<u64>,
uplink: bool,
nacks: u64,
},
HangUp {
session_id: Option<u64>,
sender: Option<u64>,
reason: String,
},
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub struct ErrorMessage {
pub code: u32,
pub reason: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub struct SuccessData {
pub id: u64,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "plugin")]
#[serde(rename_all = "lowercase")]
pub enum Plugindata {
#[serde(rename = "janus.plugin.videoroom")]
VideoRoom { data: VideoRoomData },
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
#[serde(rename_all = "lowercase")]
pub enum Jsep {
Candidate(IceCandidates),
Offer {
sdp: String,
#[serde(skip_serializing_if = "Option::is_none")]
trickle: Option<bool>,
},
Answer {
sdp: String,
#[serde(skip_serializing_if = "Option::is_none")]
trickle: Option<bool>,
},
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
#[serde(rename_all = "lowercase")]
pub enum IceCandidates {
Candidate { candidate: IceCandidate },
Candidates { candidates: Vec<IceCandidate> },
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
#[serde(rename_all = "lowercase")]
pub enum IceCandidate {
Candidate {
candidate: String,
#[serde(rename = "sdpMLineIndex")]
sdp_mline_index: u32,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "sdpMid")]
sdp_mid: Option<String>,
},
Completed {
completed: bool,
},
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "videoroom")]
#[serde(rename_all = "lowercase")]
pub enum VideoRoomData {
Created {
room: u64,
permanent: bool,
},
Destroyed {
room: u64,
},
Joined {
room: u64,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
id: u64,
},
Attached {
room: u64,
},
Success {
room: Option<u64>,
},
Event(Event),
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "request")]
#[serde(rename_all = "lowercase")]
pub enum MessageBody {
Create {
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
publishers: u32,
#[serde(skip_serializing_if = "Option::is_none")]
audiocodec: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
videocodec: Option<String>,
},
Destroy {
room: u64,
},
Join(Join),
Publish {
#[serde(skip_serializing_if = "Option::is_none")]
display: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
audiocodec: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
videocodec: Option<String>,
},
Unpublish,
Leave,
Start,
}
// FIXME: This is only for videoroom. How to distinguish the different plugins?
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
#[serde(rename_all = "lowercase")]
pub enum Event {
Error {
error_code: u32,
error: String,
},
Joining {
room: u64,
joining: Joining,
},
Configured {
configured: String,
},
Unpublished {
#[serde(skip_serializing_if = "Option::is_none")]
room: Option<u64>,
unpublished: serde_json::Value,
},
Started {
started: String,
},
Leaving {
#[serde(skip_serializing_if = "Option::is_none")]
room: Option<u64>,
leaving: serde_json::Value,
},
Left {
left: String,
},
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "ptype")]
#[serde(rename_all = "lowercase")]
pub enum Join {
Publisher {
room: u64,
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
display: Option<String>,
},
Subscriber {
room: u64,
feed: u64,
streams: Vec<SubscriberStream>,
},
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub struct SubscriberStream {
pub feed_id: u64,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub struct Joining {
pub id: u64,
pub display: String,
}