1use crate::draft23::message::ControlMessage;
2use crate::fields::{FieldMap as Map, FieldValue as Value};
3use crate::kvp::{KeyValuePair, KvpValue};
4use crate::range_filter::RangeFilter;
5use crate::types::*;
6use crate::varint::{Moqt18 as Wire, VarInt};
7
8fn vi(v: u64) -> Value {
9 Value::Uint(v)
10}
11
12fn ns_to_json(ns: &TrackNamespace) -> Value {
13 Value::Array(
14 ns.0.iter().map(|e| Value::Text(String::from_utf8_lossy(e).into_owned())).collect(),
15 )
16}
17
18fn d20_param_name(key: u64) -> Option<&'static str> {
21 match key {
22 0x02 => Some("object_delivery_timeout"),
23 0x03 => Some("authorization_token"),
24 0x04 => Some("rendezvous_timeout"),
25 0x06 => Some("subgroup_delivery_timeout"),
26 0x08 => Some("expires"),
27 0x09 => Some("largest_object"),
28 0x0A => Some("fill_timeout"),
29 0x11 => Some("paused"),
30 0x20 => Some("subscriber_priority"),
31 0x21 => Some("location_filter"),
32 0x22 => Some("group_order"),
33 0x23 => Some("fill_parameters"),
34 0x25 => Some("subgroup_filter"),
35 0x26 => Some("objectid_filter"),
36 0x27 => Some("priority_filter"),
37 0x28 => Some("object_property_filter"),
38 0x29 => Some("track_property_filter"),
39 0x32 => Some("new_group_request"),
40 0x34 => Some("track_namespace_prefix"),
41 0x35 => Some("include_properties"),
42 0x36 => Some("subscribe_tracks_id"),
43 _ => None,
44 }
45}
46
47fn d20_option_name(key: u64) -> Option<&'static str> {
50 match key {
51 0x01 => Some("path"),
52 0x03 => Some("authorization_token"),
53 0x04 => Some("max_auth_token_cache_size"),
54 0x05 => Some("authority"),
55 0x06 => Some("max_filter_ranges"),
56 0x07 => Some("moqt_implementation"),
57 0x08 => Some("max_request_updates"),
58 0x09 => Some("max_control_message_size"),
59 _ => None,
60 }
61}
62
63fn decode_range_filter(bytes: &[u8], parameter_type: u64) -> Value {
109 let mut o = Map::new();
110 if bytes.is_empty() {
111 o.insert("removed".into(), Value::Bool(true));
112 return Value::Map(o);
113 }
114 let Ok(filter) = RangeFilter::decode_moqt_structure::<Wire>(parameter_type, bytes) else {
115 return Value::Bytes(bytes.to_vec());
116 };
117 if let Err(broken) = filter.check_its_own_types() {
118 o.insert("violates".into(), Value::Text(broken.to_string()));
119 }
120 o.insert("set_id".into(), vi(filter.set_id as u64));
121 if let Some(property_type) = filter.property_type {
122 o.insert("property_type".into(), vi(property_type));
123 }
124 let ranges = filter
125 .ranges
126 .iter()
127 .map(|range| {
128 let mut r = Map::new();
129 r.insert("start".into(), vi(range.start));
130 if let Some(end) = range.end {
131 r.insert("end".into(), vi(end));
132 }
133 Value::Map(r)
134 })
135 .collect();
136 o.insert("ranges".into(), Value::Array(ranges));
137 Value::Map(o)
138}
139
140fn decode_location_filter(bytes: &[u8]) -> Value {
152 const NAMES: [&str; 4] = ["start_group", "start_object", "end_group_delta", "end_object"];
153 let (filter_type, fields) = crate::draft23::message::decode_location_filter(bytes)
154 .expect("the decoder accepted this parameter, so its value parses");
155 let mut obj = Map::new();
156 obj.insert("filter_type".into(), vi(filter_type));
157 for (name, value) in NAMES.iter().zip(fields) {
158 obj.insert((*name).to_string(), vi(value));
159 }
160 Value::Map(obj)
161}
162
163fn decode_fill_parameters(bytes: &[u8]) -> Value {
170 let nested = crate::draft23::message::decode_fill_parameters(bytes)
171 .expect("the decoder accepted this parameter, so its value parses");
172 params_to_json(&nested)
173}
174
175fn auth_token_to_json_d20(bytes: &[u8]) -> Value {
176 let mut buf = bytes;
177 let alias_type = match VarInt::decode_moqt::<Wire>(&mut buf) {
178 Ok(v) => v,
179 Err(_) => return Value::Bytes(bytes.to_vec()),
180 };
181 let at = alias_type.into_inner();
182 let mut o = Map::new();
183 o.insert("alias_type".into(), vi(at));
184 match at {
185 0 | 2 => {
186 if let Ok(ta) = VarInt::decode_moqt::<Wire>(&mut buf) {
187 o.insert("token_alias".into(), vi(ta.into_inner()));
188 }
189 }
190 1 => {
191 if let Ok(ta) = VarInt::decode_moqt::<Wire>(&mut buf) {
192 o.insert("token_alias".into(), vi(ta.into_inner()));
193 }
194 if let Ok(tt) = VarInt::decode_moqt::<Wire>(&mut buf) {
195 o.insert("token_type".into(), vi(tt.into_inner()));
196 }
197 o.insert("token_value".into(), Value::Bytes(buf.to_vec()));
199 }
200 _ => {
201 if let Ok(tt) = VarInt::decode_moqt::<Wire>(&mut buf) {
202 o.insert("token_type".into(), vi(tt.into_inner()));
203 }
204 o.insert("token_value".into(), Value::Bytes(buf.to_vec()));
205 }
206 }
207 Value::Map(o)
208}
209
210fn decode_largest_object(bytes: &[u8]) -> Value {
223 let mut buf = bytes;
224 let Ok(group) = VarInt::decode_moqt::<Wire>(&mut buf) else {
225 return Value::Bytes(bytes.to_vec());
226 };
227 let Ok(object) = VarInt::decode_moqt::<Wire>(&mut buf) else {
228 return Value::Bytes(bytes.to_vec());
229 };
230 let (group, object) = (group.into_inner(), object.into_inner());
231 let mut obj = Map::new();
232 obj.insert("group".into(), vi(group));
233 obj.insert("object".into(), vi(object));
234 Value::Map(obj)
235}
236
237fn decode_track_namespace_prefix(bytes: &[u8]) -> Value {
238 let mut buf = bytes;
239 match TrackNamespace::decode_allow_empty_moqt::<Wire>(&mut buf) {
240 Ok(ns) => ns_to_json(&ns),
241 Err(_) => Value::Bytes(bytes.to_vec()),
242 }
243}
244
245fn params_to_json(params: &[KeyValuePair]) -> Value {
246 crate::fields::kvp_entries(params, |key, value| {
247 let Some(name) = d20_param_name(key) else {
248 return (None, None);
249 };
250 let rendered = match (value, key) {
251 (KvpValue::Bytes(b), 0x21) => decode_location_filter(b),
252 (KvpValue::Bytes(b), 0x23) => decode_fill_parameters(b),
253 (KvpValue::Bytes(b), 0x25..=0x29) => decode_range_filter(b, key),
258 (KvpValue::Bytes(b), 0x09) => decode_largest_object(b),
259 (KvpValue::Bytes(b), 0x34) => decode_track_namespace_prefix(b),
260 (KvpValue::Bytes(b), _) if name == "authorization_token" => auth_token_to_json_d20(b),
261 (KvpValue::Varint(v), _) => vi(v.into_inner()),
262 (KvpValue::Bytes(b), _) => Value::Text(String::from_utf8_lossy(b).into_owned()),
263 };
264 (Some(name), Some(rendered))
265 })
266}
267
268pub(crate) fn options_to_json(options: &[KeyValuePair]) -> Value {
269 crate::fields::kvp_entries(options, |key, value| {
270 let Some(name) = d20_option_name(key) else {
271 return (None, None);
272 };
273 let rendered = match value {
274 KvpValue::Varint(v) => vi(v.into_inner()),
275 KvpValue::Bytes(b) if name == "authorization_token" => auth_token_to_json_d20(b),
276 KvpValue::Bytes(b)
277 if key == crate::draft23::message::MAX_CONTROL_MESSAGE_SIZE_OPTION =>
278 {
279 match crate::draft23::message::decode_max_control_message_size(b) {
280 Ok(size) => vi(size),
281 Err(_) => Value::Bytes(b.to_vec()),
282 }
283 }
284 KvpValue::Bytes(b) => Value::Text(String::from_utf8_lossy(b).into_owned()),
285 };
286 (Some(name), Some(rendered))
287 })
288}
289
290fn error_details_to_json(details: &[KeyValuePair]) -> Value {
293 use crate::draft23::message::{error_detail_types as T, Redirect};
294 crate::fields::kvp_entries(details, |key, value| match (key, value) {
295 (T::REDIRECT, KvpValue::Bytes(b)) => {
296 let Ok(r) = Redirect::decode(b) else {
297 return (Some("redirect"), None);
298 };
299 let mut o = Map::new();
300 o.insert(
301 "connect_uri".into(),
302 Value::Text(String::from_utf8_lossy(&r.connect_uri).into_owned()),
303 );
304 o.insert("track_namespace".into(), ns_to_json(&r.track_namespace));
305 o.insert(
306 "track_name".into(),
307 Value::Text(String::from_utf8_lossy(&r.track_name).into_owned()),
308 );
309 (Some("redirect"), Some(Value::Map(o)))
310 }
311 (T::UNSUPPORTED_PROPERTY, KvpValue::Varint(v)) => {
312 (Some("unsupported_property"), Some(vi(v.into_inner())))
313 }
314 _ => (None, None),
315 })
316}
317
318fn d20_track_prop_name(key: u64) -> Option<&'static str> {
319 match key {
320 0x02 => Some("object_delivery_timeout"),
321 0x04 => Some("max_cache_duration"),
322 0x06 => Some("subgroup_delivery_timeout"),
323 0x0b => Some("immutable_properties"),
324 0x0e => Some("default_publisher_priority"),
325 0x22 => Some("default_publisher_group_order"),
326 0x30 => Some("dynamic_groups"),
327 _ => None,
328 }
329}
330
331fn track_props_to_json(props: &[KeyValuePair]) -> Value {
332 crate::fields::kvp_entries(props, |key, value| {
333 let name = d20_track_prop_name(key);
334 let rendered = match value {
335 KvpValue::Varint(v) => Some(vi(v.into_inner())),
336 KvpValue::Bytes(b) if key == 0x0b => crate::draft23::message::decode_property_list(b)
340 .ok()
341 .map(|nested| track_props_to_json(&nested)),
342 KvpValue::Bytes(_) => None,
346 };
347 (name, rendered)
348 })
349}
350
351pub fn message_fields(msg: &ControlMessage) -> Map {
357 let obj = match msg {
358 ControlMessage::Setup(m) => {
359 let mut o = Map::new();
360 o.insert("options".into(), options_to_json(&m.options));
361 o
362 }
363 ControlMessage::GoAway(m) => {
364 let mut o = Map::new();
365 o.insert(
366 "new_session_uri".into(),
367 Value::Text(String::from_utf8_lossy(&m.new_session_uri).into_owned()),
368 );
369 o.insert("timeout".into(), vi(m.timeout.into_inner()));
370 o
371 }
372 ControlMessage::RequestOk(m) => {
373 let mut o = Map::new();
374 o.insert("parameters".into(), params_to_json(&m.parameters));
375 o.insert("track_properties".into(), track_props_to_json(&m.track_properties));
376 o
377 }
378 ControlMessage::RequestError(m) => {
379 let mut o = Map::new();
380 o.insert("error_code".into(), vi(m.error_code.into_inner()));
381 o.insert("retry_interval".into(), vi(m.retry_interval.into_inner()));
382 o.insert(
383 "reason_phrase".into(),
384 Value::Text(String::from_utf8_lossy(&m.reason_phrase).into_owned()),
385 );
386 o.insert("error_details".into(), error_details_to_json(&m.error_details));
387 o
388 }
389 ControlMessage::Subscribe(m) => {
390 let mut o = Map::new();
391 o.insert("request_id".into(), vi(m.request_id.into_inner()));
392 o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
393 o.insert(
394 "track_name".into(),
395 Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
396 );
397 o.insert("parameters".into(), params_to_json(&m.parameters));
398 o
399 }
400 ControlMessage::SubscribeOk(m) => {
401 let mut o = Map::new();
402 o.insert("track_alias".into(), vi(m.track_alias.into_inner()));
403 o.insert("parameters".into(), params_to_json(&m.parameters));
404 o.insert("track_properties".into(), track_props_to_json(&m.track_properties));
405 o
406 }
407 ControlMessage::RequestUpdate(m) => {
408 let mut o = Map::new();
409 o.insert("request_id".into(), vi(m.request_id.into_inner()));
410 o.insert("parameters".into(), params_to_json(&m.parameters));
411 o
412 }
413 ControlMessage::Publish(m) => {
414 let mut o = Map::new();
415 o.insert("request_id".into(), vi(m.request_id.into_inner()));
416 o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
417 o.insert(
418 "track_name".into(),
419 Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
420 );
421 o.insert("track_alias".into(), vi(m.track_alias.into_inner()));
422 o.insert("parameters".into(), params_to_json(&m.parameters));
423 o.insert("track_properties".into(), track_props_to_json(&m.track_properties));
424 o
425 }
426 ControlMessage::PublishStateNotify(m) => {
427 let mut o = Map::new();
428 o.insert("parameters".into(), params_to_json(&m.parameters));
429 o
430 }
431 ControlMessage::PublishDone(m) => {
432 let mut o = Map::new();
433 o.insert("status_code".into(), vi(m.status_code.into_inner()));
434 o.insert("stream_count".into(), vi(m.stream_count.into_inner()));
435 o.insert(
436 "reason_phrase".into(),
437 Value::Text(String::from_utf8_lossy(&m.reason_phrase).into_owned()),
438 );
439 o
440 }
441 ControlMessage::PublishNamespace(m) => {
442 let mut o = Map::new();
443 o.insert("request_id".into(), vi(m.request_id.into_inner()));
444 o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
445 o.insert("parameters".into(), params_to_json(&m.parameters));
446 o
447 }
448 ControlMessage::Namespace(m) => {
449 let mut o = Map::new();
450 o.insert("namespace_suffix".into(), ns_to_json(&m.namespace_suffix));
451 o.insert("parameters".into(), params_to_json(&m.parameters));
452 o
453 }
454 ControlMessage::NamespaceDone(m) => {
455 let mut o = Map::new();
456 o.insert("namespace_suffix".into(), ns_to_json(&m.namespace_suffix));
457 o
458 }
459 ControlMessage::SubscribeNamespace(m) => {
460 let mut o = Map::new();
461 o.insert("request_id".into(), vi(m.request_id.into_inner()));
462 o.insert("namespace_prefix".into(), ns_to_json(&m.namespace_prefix));
463 o.insert("parameters".into(), params_to_json(&m.parameters));
464 o
465 }
466 ControlMessage::SubscribeTracks(m) => {
467 let mut o = Map::new();
468 o.insert("request_id".into(), vi(m.request_id.into_inner()));
469 o.insert("namespace_prefix".into(), ns_to_json(&m.namespace_prefix));
470 o.insert("parameters".into(), params_to_json(&m.parameters));
471 o
472 }
473 ControlMessage::TrackStatus(m) => {
474 let mut o = Map::new();
475 o.insert("request_id".into(), vi(m.request_id.into_inner()));
476 o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
477 o.insert(
478 "track_name".into(),
479 Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
480 );
481 o.insert("parameters".into(), params_to_json(&m.parameters));
482 o
483 }
484 ControlMessage::Fetch(m) => {
485 let mut o = Map::new();
486 o.insert("request_id".into(), vi(m.request_id.into_inner()));
487 o.insert("track_namespace".into(), ns_to_json(&m.track_namespace));
488 o.insert(
489 "track_name".into(),
490 Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
491 );
492 o.insert("parameters".into(), params_to_json(&m.parameters));
493 o
494 }
495 ControlMessage::PublishSkipped(m) => {
496 let mut o = Map::new();
497 o.insert("namespace_suffix".into(), ns_to_json(&m.namespace_suffix));
498 o.insert(
499 "track_name".into(),
500 Value::Text(String::from_utf8_lossy(&m.track_name).into_owned()),
501 );
502 o
503 }
504 };
505 obj
506}