dvb_si/descriptors/
service_identifier.rs1use super::descriptor_body;
9use crate::error::{Error, Result};
10use crate::text::DvbText;
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x71;
15const HEADER_LEN: usize = 2;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
21pub struct ServiceIdentifierDescriptor<'a> {
22 pub textual_service_identifier: DvbText<'a>,
24}
25
26impl<'a> Parse<'a> for ServiceIdentifierDescriptor<'a> {
27 type Error = crate::error::Error;
28 fn parse(bytes: &'a [u8]) -> Result<Self> {
29 let body = descriptor_body(
30 bytes,
31 TAG,
32 "ServiceIdentifierDescriptor",
33 "unexpected tag for service_identifier_descriptor",
34 )?;
35 Ok(Self {
36 textual_service_identifier: DvbText::new(body),
37 })
38 }
39}
40
41impl Serialize for ServiceIdentifierDescriptor<'_> {
42 type Error = crate::error::Error;
43 fn serialized_len(&self) -> usize {
44 HEADER_LEN + self.textual_service_identifier.raw().len()
45 }
46
47 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
48 if self.textual_service_identifier.raw().len() > u8::MAX as usize {
49 return Err(Error::InvalidDescriptor {
50 tag: TAG,
51 reason: "textual_service_identifier exceeds 255 bytes",
52 });
53 }
54 let len = self.serialized_len();
55 if buf.len() < len {
56 return Err(Error::OutputBufferTooSmall {
57 need: len,
58 have: buf.len(),
59 });
60 }
61 buf[0] = TAG;
62 buf[1] = self.textual_service_identifier.raw().len() as u8;
63 buf[HEADER_LEN..len].copy_from_slice(self.textual_service_identifier.raw());
64 Ok(len)
65 }
66}
67impl<'a> crate::traits::DescriptorDef<'a> for ServiceIdentifierDescriptor<'a> {
68 const TAG: u8 = TAG;
69 const NAME: &'static str = "SERVICE_IDENTIFIER";
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn parse_extracts_identifier_text() {
78 let bytes = [TAG, 6, b'B', b'B', b'C', b'O', b'N', b'E'];
79 let d = ServiceIdentifierDescriptor::parse(&bytes).unwrap();
80 assert_eq!(d.textual_service_identifier.raw(), b"BBCONE");
81 assert_eq!(d.textual_service_identifier.decode(), "BBCONE");
82 }
83
84 #[test]
85 fn parse_rejects_wrong_tag() {
86 assert!(matches!(
87 ServiceIdentifierDescriptor::parse(&[0x70, 1, 0]).unwrap_err(),
88 Error::InvalidDescriptor { tag: 0x70, .. }
89 ));
90 }
91
92 #[test]
93 fn parse_rejects_short_header() {
94 assert!(matches!(
95 ServiceIdentifierDescriptor::parse(&[TAG]).unwrap_err(),
96 Error::BufferTooShort { .. }
97 ));
98 }
99
100 #[test]
101 fn parse_rejects_length_overrunning_buffer() {
102 let bytes = [TAG, 5, 1, 2, 3];
103 assert!(matches!(
104 ServiceIdentifierDescriptor::parse(&bytes).unwrap_err(),
105 Error::BufferTooShort { .. }
106 ));
107 }
108
109 #[test]
110 fn empty_identifier_is_valid() {
111 let bytes = [TAG, 0];
112 let d = ServiceIdentifierDescriptor::parse(&bytes).unwrap();
113 assert!(d.textual_service_identifier.raw().is_empty());
114 }
115
116 #[test]
117 fn serialize_round_trip() {
118 let d = ServiceIdentifierDescriptor {
119 textual_service_identifier: DvbText::new(b"CH4-HD"),
120 };
121 let mut buf = vec![0u8; d.serialized_len()];
122 d.serialize_into(&mut buf).unwrap();
123 assert_eq!(ServiceIdentifierDescriptor::parse(&buf).unwrap(), d);
124 }
125
126 #[test]
127 fn serialize_rejects_too_small_buffer() {
128 let d = ServiceIdentifierDescriptor {
129 textual_service_identifier: DvbText::new(b"test"),
130 };
131 let mut buf = vec![0u8; 1];
132 assert!(matches!(
133 d.serialize_into(&mut buf).unwrap_err(),
134 Error::OutputBufferTooSmall { .. }
135 ));
136 }
137
138 #[cfg(feature = "serde")]
139 #[test]
140 fn serde_serializes_to_stable_json() {
141 let d = ServiceIdentifierDescriptor {
142 textual_service_identifier: DvbText::new(b"ITV1"),
143 };
144 let j = serde_json::to_string(&d).unwrap();
145 let _v: serde_json::Value = serde_json::from_str(&j).unwrap();
146 assert!(j.contains("textual_service_identifier"));
147 }
148}