use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
use crate::{coding, data, message, setup};
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub time: f64,
pub name: String,
pub data: EventData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event_type")]
pub enum EventData {
#[serde(rename = "control_message_parsed")]
ControlMessageParsed(ControlMessageParsed),
#[serde(rename = "control_message_created")]
ControlMessageCreated(ControlMessageCreated),
#[serde(rename = "subgroup_header_parsed")]
SubgroupHeaderParsed(SubgroupHeaderParsed),
#[serde(rename = "subgroup_header_created")]
SubgroupHeaderCreated(SubgroupHeaderCreated),
#[serde(rename = "subgroup_object_parsed")]
SubgroupObjectParsed(SubgroupObjectParsed),
#[serde(rename = "subgroup_object_created")]
SubgroupObjectCreated(SubgroupObjectCreated),
#[serde(rename = "object_datagram_parsed")]
ObjectDatagramParsed(ObjectDatagramParsed),
#[serde(rename = "object_datagram_created")]
ObjectDatagramCreated(ObjectDatagramCreated),
#[serde(rename = "loglevel")]
LogLevel(LogLevelEvent),
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControlMessageParsed {
pub stream_id: u64,
pub message_type: String,
#[serde(flatten)]
pub message: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControlMessageCreated {
pub stream_id: u64,
pub message_type: String,
#[serde(flatten)]
pub message: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubgroupHeaderParsed {
pub stream_id: u64,
#[serde(flatten)]
pub header: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubgroupHeaderCreated {
pub stream_id: u64,
#[serde(flatten)]
pub header: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubgroupObjectParsed {
pub stream_id: u64,
#[serde(flatten)]
pub object: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubgroupObjectCreated {
pub stream_id: u64,
#[serde(flatten)]
pub object: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObjectDatagramParsed {
pub stream_id: u64,
#[serde(flatten)]
pub object: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObjectDatagramCreated {
pub stream_id: u64,
#[serde(flatten)]
pub object: JsonValue,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogLevelEvent {
pub message: String,
}
fn key_value_pairs_to_vec(kvps: &[coding::KeyValuePair]) -> Vec<(String, String)> {
kvps.iter()
.map(|kvp| (kvp.key.to_string(), format!("{:?}", kvp.value)))
.collect()
}
fn create_control_message_event(
time: f64,
stream_id: u64,
is_parsed: bool,
msg_type: &str,
message: JsonValue,
) -> Event {
if is_parsed {
Event {
time,
name: "moqt:control_message_parsed".to_string(),
data: EventData::ControlMessageParsed(ControlMessageParsed {
stream_id,
message_type: msg_type.to_string(),
message,
}),
}
} else {
Event {
time,
name: "moqt:control_message_created".to_string(),
data: EventData::ControlMessageCreated(ControlMessageCreated {
stream_id,
message_type: msg_type.to_string(),
message,
}),
}
}
}
pub fn client_setup_parsed(time: f64, stream_id: u64, msg: &setup::Client) -> Event {
let versions: Vec<String> = msg.versions.0.iter().map(|v| format!("{:?}", v)).collect();
create_control_message_event(
time,
stream_id,
true,
"client_setup",
json!(
{
"number_of_supported_versions": msg.versions.0.len(),
"supported_versions": versions,
"parameters": key_value_pairs_to_vec(&msg.params.0),
}),
)
}
pub fn server_setup_created(time: f64, stream_id: u64, msg: &setup::Server) -> Event {
create_control_message_event(
time,
stream_id,
false,
"server_setup",
json!(
{
"selected_version": format!("{:?}", msg.version),
"parameters": key_value_pairs_to_vec(&msg.params.0),
}),
)
}
fn subscribe_to_json(msg: &message::Subscribe) -> JsonValue {
let mut json = json!({
"subscribe_id": msg.id,
"track_namespace": msg.track_namespace.to_string(),
"track_name": &msg.track_name,
"subscriber_priority": msg.subscriber_priority,
"group_order": format!("{:?}", msg.group_order),
"filter_type": format!("{:?}", msg.filter_type),
"parameters": key_value_pairs_to_vec(&msg.params.0),
});
if let Some(start_loc) = &msg.start_location {
json["start_group"] = json!(start_loc.group_id);
json["start_object"] = json!(start_loc.object_id);
}
if let Some(end_group) = msg.end_group_id {
json["end_group"] = json!(end_group);
}
json
}
pub fn subscribe_parsed(time: f64, stream_id: u64, msg: &message::Subscribe) -> Event {
create_control_message_event(time, stream_id, true, "subscribe", subscribe_to_json(msg))
}
pub fn subscribe_created(time: f64, stream_id: u64, msg: &message::Subscribe) -> Event {
create_control_message_event(time, stream_id, false, "subscribe", subscribe_to_json(msg))
}
fn subscribe_ok_to_json(msg: &message::SubscribeOk) -> JsonValue {
let mut json = json!({
"subscribe_id": msg.id,
"track_alias": msg.track_alias,
"expires": msg.expires,
"group_order": format!("{:?}", msg.group_order),
"content_exists": msg.content_exists,
"parameters": key_value_pairs_to_vec(&msg.params.0),
});
if msg.content_exists {
if let Some(largest) = &msg.largest_location {
json["largest_group_id"] = json!(largest.group_id);
json["largest_object_id"] = json!(largest.object_id);
}
}
json
}
pub fn subscribe_ok_parsed(time: f64, stream_id: u64, msg: &message::SubscribeOk) -> Event {
create_control_message_event(
time,
stream_id,
true,
"subscribe_ok",
subscribe_ok_to_json(msg),
)
}
pub fn subscribe_ok_created(time: f64, stream_id: u64, msg: &message::SubscribeOk) -> Event {
create_control_message_event(
time,
stream_id,
false,
"subscribe_ok",
subscribe_ok_to_json(msg),
)
}
fn subscribe_error_to_json(msg: &message::SubscribeError) -> JsonValue {
json!({
"subscribe_id": msg.id,
"error_code": msg.error_code,
"reason_phrase": &msg.reason_phrase.0,
})
}
pub fn subscribe_error_parsed(time: f64, stream_id: u64, msg: &message::SubscribeError) -> Event {
create_control_message_event(
time,
stream_id,
true,
"subscribe_error",
subscribe_error_to_json(msg),
)
}
pub fn subscribe_error_created(time: f64, stream_id: u64, msg: &message::SubscribeError) -> Event {
create_control_message_event(
time,
stream_id,
false,
"subscribe_error",
subscribe_error_to_json(msg),
)
}
fn publish_namespace_to_json(msg: &message::PublishNamespace) -> JsonValue {
json!({
"request_id": msg.id,
"track_namespace": msg.track_namespace.to_string(),
"parameters": key_value_pairs_to_vec(&msg.params.0),
})
}
pub fn publish_namespace_parsed(
time: f64,
stream_id: u64,
msg: &message::PublishNamespace,
) -> Event {
create_control_message_event(
time,
stream_id,
true,
"publish_namespace",
publish_namespace_to_json(msg),
)
}
pub fn publish_namespace_created(
time: f64,
stream_id: u64,
msg: &message::PublishNamespace,
) -> Event {
create_control_message_event(
time,
stream_id,
false,
"publish_namespace",
publish_namespace_to_json(msg),
)
}
fn publish_namespace_ok_to_json(msg: &message::PublishNamespaceOk) -> JsonValue {
json!({
"request_id": msg.id,
})
}
pub fn publish_namespace_ok_parsed(
time: f64,
stream_id: u64,
msg: &message::PublishNamespaceOk,
) -> Event {
create_control_message_event(
time,
stream_id,
true,
"publish_namespace_ok",
publish_namespace_ok_to_json(msg),
)
}
pub fn publish_namespace_ok_created(
time: f64,
stream_id: u64,
msg: &message::PublishNamespaceOk,
) -> Event {
create_control_message_event(
time,
stream_id,
false,
"publish_namespace_ok",
publish_namespace_ok_to_json(msg),
)
}
fn publish_namespace_error_to_json(msg: &message::PublishNamespaceError) -> JsonValue {
json!({
"request_id": msg.id,
"error_code": msg.error_code,
"reason_phrase": &msg.reason_phrase.0,
})
}
pub fn publish_namespace_error_parsed(
time: f64,
stream_id: u64,
msg: &message::PublishNamespaceError,
) -> Event {
create_control_message_event(
time,
stream_id,
true,
"publish_namespace_error",
publish_namespace_error_to_json(msg),
)
}
pub fn publish_namespace_error_created(
time: f64,
stream_id: u64,
msg: &message::PublishNamespaceError,
) -> Event {
create_control_message_event(
time,
stream_id,
false,
"publish_namespace_error",
publish_namespace_error_to_json(msg),
)
}
pub fn unsubscribe_parsed(time: f64, stream_id: u64, msg: &message::Unsubscribe) -> Event {
create_control_message_event(
time,
stream_id,
true,
"unsubscribe",
json!({
"subscribe_id": msg.id,
}),
)
}
pub fn unsubscribe_created(time: f64, stream_id: u64, msg: &message::Unsubscribe) -> Event {
create_control_message_event(
time,
stream_id,
false,
"unsubscribe",
json!({
"subscribe_id": msg.id,
}),
)
}
pub fn go_away_parsed(time: f64, stream_id: u64, msg: &message::GoAway) -> Event {
create_control_message_event(
time,
stream_id,
true,
"goaway",
json!({
"new_session_uri": &msg.uri.0,
}),
)
}
pub fn go_away_created(time: f64, stream_id: u64, msg: &message::GoAway) -> Event {
create_control_message_event(
time,
stream_id,
false,
"goaway",
json!({
"new_session_uri": &msg.uri.0,
}),
)
}
fn subgroup_header_to_json(header: &data::SubgroupHeader) -> JsonValue {
let mut json = json!({
"header_type": format!("{:?}", header.header_type),
"track_alias": header.track_alias,
"group_id": header.group_id,
"publisher_priority": header.publisher_priority,
});
if let Some(subgroup_id) = header.subgroup_id {
json["subgroup_id"] = json!(subgroup_id);
}
json
}
pub fn subgroup_header_parsed(time: f64, stream_id: u64, header: &data::SubgroupHeader) -> Event {
Event {
time,
name: "moqt:subgroup_header_parsed".to_string(),
data: EventData::SubgroupHeaderParsed(SubgroupHeaderParsed {
stream_id,
header: subgroup_header_to_json(header),
}),
}
}
pub fn subgroup_header_created(time: f64, stream_id: u64, header: &data::SubgroupHeader) -> Event {
Event {
time,
name: "moqt:subgroup_header_created".to_string(),
data: EventData::SubgroupHeaderCreated(SubgroupHeaderCreated {
stream_id,
header: subgroup_header_to_json(header),
}),
}
}
fn subgroup_object_to_json(
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObject,
) -> JsonValue {
let mut object_data = json!({
"group_id": group_id,
"subgroup_id": subgroup_id,
"object_id": object_id,
"object_payload_length": object.payload_length,
});
if let Some(status) = object.status {
object_data["object_status"] = json!(format!("{:?}", status));
}
object_data
}
pub fn subgroup_object_parsed(
time: f64,
stream_id: u64,
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObject,
) -> Event {
Event {
time,
name: "moqt:subgroup_object_parsed".to_string(),
data: EventData::SubgroupObjectParsed(SubgroupObjectParsed {
stream_id,
object: subgroup_object_to_json(group_id, subgroup_id, object_id, object),
}),
}
}
pub fn subgroup_object_created(
time: f64,
stream_id: u64,
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObject,
) -> Event {
Event {
time,
name: "moqt:subgroup_object_created".to_string(),
data: EventData::SubgroupObjectCreated(SubgroupObjectCreated {
stream_id,
object: subgroup_object_to_json(group_id, subgroup_id, object_id, object),
}),
}
}
fn subgroup_object_ext_to_json(
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObjectExt,
) -> JsonValue {
let mut object_data = json!({
"group_id": group_id,
"subgroup_id": subgroup_id,
"object_id": object_id,
"extension_headers": key_value_pairs_to_vec(&object.extension_headers.0),
"object_payload_length": object.payload_length,
});
if let Some(status) = object.status {
object_data["object_status"] = json!(format!("{:?}", status));
}
object_data
}
pub fn subgroup_object_ext_parsed(
time: f64,
stream_id: u64,
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObjectExt,
) -> Event {
Event {
time,
name: "moqt:subgroup_object_parsed".to_string(),
data: EventData::SubgroupObjectParsed(SubgroupObjectParsed {
stream_id,
object: subgroup_object_ext_to_json(group_id, subgroup_id, object_id, object),
}),
}
}
pub fn subgroup_object_ext_created(
time: f64,
stream_id: u64,
group_id: u64,
subgroup_id: u64,
object_id: u64,
object: &data::SubgroupObjectExt,
) -> Event {
Event {
time,
name: "moqt:subgroup_object_created".to_string(),
data: EventData::SubgroupObjectCreated(SubgroupObjectCreated {
stream_id,
object: subgroup_object_ext_to_json(group_id, subgroup_id, object_id, object),
}),
}
}
fn object_datagram_to_json(datagram: &data::Datagram) -> JsonValue {
let mut json = json!({
"datagram_type": format!("{:?}", datagram.datagram_type),
"track_alias": datagram.track_alias,
"group_id": datagram.group_id,
"object_id": datagram.object_id.unwrap_or(0),
"publisher_priority": datagram.publisher_priority,
"payload_length": datagram.payload.as_ref().map_or(0, |p| p.len()),
});
if let Some(extension_headers) = &datagram.extension_headers {
json["extension_headers"] = json!(key_value_pairs_to_vec(&extension_headers.0));
}
if let Some(status) = datagram.status {
json["object_status"] = json!(format!("{:?}", status));
}
json
}
pub fn object_datagram_parsed(time: f64, stream_id: u64, datagram: &data::Datagram) -> Event {
Event {
time,
name: "moqt:object_datagram_parsed".to_string(),
data: EventData::ObjectDatagramParsed(ObjectDatagramParsed {
stream_id,
object: object_datagram_to_json(datagram),
}),
}
}
pub fn object_datagram_created(time: f64, stream_id: u64, datagram: &data::Datagram) -> Event {
Event {
time,
name: "moqt:object_datagram_created".to_string(),
data: EventData::ObjectDatagramCreated(ObjectDatagramCreated {
stream_id,
object: object_datagram_to_json(datagram),
}),
}
}
#[derive(Debug, Clone, Copy)]
pub enum LogLevel {
Fatal,
Error,
Warn,
Info,
Debug,
Verbose,
}
impl LogLevel {
fn as_str(&self) -> &'static str {
match self {
LogLevel::Fatal => "fatal",
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Verbose => "verbose",
}
}
}
pub fn loglevel_event(time: f64, level: LogLevel, message: String) -> Event {
Event {
time,
name: format!("loglevel:{}", level.as_str()),
data: EventData::LogLevel(LogLevelEvent { message }),
}
}