dvb_si/descriptors/ait/
dvb_j_application_location.rs1use crate::descriptors::descriptor_body;
10use crate::error::{Error, Result};
11use crate::text::DvbText;
12use broadcast_common::{Parse, Serialize};
13
14pub const TAG: u8 = 0x04;
16const HEADER_LEN: usize = 2;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub struct DvbJApplicationLocationDescriptor<'a> {
22 pub base_directory: DvbText<'a>,
24 pub classpath_extension: DvbText<'a>,
26 pub initial_class: DvbText<'a>,
28}
29
30impl<'a> Parse<'a> for DvbJApplicationLocationDescriptor<'a> {
31 type Error = crate::error::Error;
32 fn parse(bytes: &'a [u8]) -> Result<Self> {
33 let body = descriptor_body(
34 bytes,
35 TAG,
36 "DvbJApplicationLocationDescriptor",
37 "unexpected tag for dvb_j_application_location_descriptor",
38 )?;
39 if body.is_empty() {
40 return Err(Error::InvalidDescriptor {
41 tag: TAG,
42 reason: "dvb_j_application_location_descriptor body is empty",
43 });
44 }
45 let base_dir_len = body[0] as usize;
46 let end = 1 + base_dir_len;
47 if body.len() < end {
48 return Err(Error::InvalidDescriptor {
49 tag: TAG,
50 reason: "dvb_j_application_location_descriptor base_directory overruns body",
51 });
52 }
53 let base_directory = &body[1..end];
54 let rest = &body[end..];
55 if rest.is_empty() {
56 return Err(Error::InvalidDescriptor {
57 tag: TAG,
58 reason: "dvb_j_application_location_descriptor missing classpath_extension",
59 });
60 }
61 let cp_len = rest[0] as usize;
62 let cp_end = 1 + cp_len;
63 if rest.len() < cp_end {
64 return Err(Error::InvalidDescriptor {
65 tag: TAG,
66 reason: "dvb_j_application_location_descriptor classpath_extension overruns body",
67 });
68 }
69 Ok(Self {
70 base_directory: DvbText::new(base_directory),
71 classpath_extension: DvbText::new(&rest[1..cp_end]),
72 initial_class: DvbText::new(&rest[cp_end..]),
73 })
74 }
75}
76
77impl Serialize for DvbJApplicationLocationDescriptor<'_> {
78 type Error = crate::error::Error;
79 fn serialized_len(&self) -> usize {
80 HEADER_LEN
81 + 1
82 + self.base_directory.len()
83 + 1
84 + self.classpath_extension.len()
85 + self.initial_class.len()
86 }
87
88 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
89 if self.base_directory.len() > u8::MAX as usize {
90 return Err(Error::InvalidDescriptor {
91 tag: TAG,
92 reason: "dvb_j_application_location_descriptor base_directory exceeds 255 bytes",
93 });
94 }
95 if self.classpath_extension.len() > u8::MAX as usize {
96 return Err(Error::InvalidDescriptor {
97 tag: TAG,
98 reason: "dvb_j_application_location_descriptor classpath_extension exceeds 255 bytes",
99 });
100 }
101 let body_len = self.serialized_len() - HEADER_LEN;
102 if body_len > u8::MAX as usize {
103 return Err(Error::InvalidDescriptor {
104 tag: TAG,
105 reason: "dvb_j_application_location_descriptor body exceeds 255 bytes",
106 });
107 }
108 let len = self.serialized_len();
109 if buf.len() < len {
110 return Err(Error::OutputBufferTooSmall {
111 need: len,
112 have: buf.len(),
113 });
114 }
115 buf[0] = TAG;
116 buf[1] = body_len as u8;
117 let mut pos = HEADER_LEN;
118 buf[pos] = self.base_directory.len() as u8;
119 pos += 1;
120 buf[pos..pos + self.base_directory.len()].copy_from_slice(self.base_directory.raw());
121 pos += self.base_directory.len();
122 buf[pos] = self.classpath_extension.len() as u8;
123 pos += 1;
124 buf[pos..pos + self.classpath_extension.len()]
125 .copy_from_slice(self.classpath_extension.raw());
126 pos += self.classpath_extension.len();
127 buf[pos..pos + self.initial_class.len()].copy_from_slice(self.initial_class.raw());
128 Ok(len)
129 }
130}
131
132impl<'a> crate::traits::DescriptorDef<'a> for DvbJApplicationLocationDescriptor<'a> {
133 const TAG: u8 = TAG;
134 const NAME: &'static str = "DVB_J_APPLICATION_LOCATION";
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn parse_full() {
143 let bytes = [
144 TAG, 10, 1, b'/', 5, b'l', b'i', b'b', b'/', b';', b'A', b'B', ];
149 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
150 assert_eq!(d.base_directory.raw(), b"/");
151 assert_eq!(d.classpath_extension.raw(), b"lib/;");
152 assert_eq!(d.initial_class.raw(), b"AB");
153 }
154
155 #[test]
156 fn parse_no_initial_class() {
157 let bytes = [
159 TAG, 4, 1, b'/', 1,
162 b';', ];
165 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
166 assert_eq!(d.base_directory.raw(), b"/");
167 assert_eq!(d.classpath_extension.raw(), b";");
168 assert!(d.initial_class.raw().is_empty());
169 }
170
171 #[test]
172 fn serialize_round_trip() {
173 let d = DvbJApplicationLocationDescriptor {
174 base_directory: DvbText::new(b"/apps"),
175 classpath_extension: DvbText::new(b"classes/"),
176 initial_class: DvbText::new(b"com.example.Main"),
177 };
178 let mut buf = vec![0u8; d.serialized_len()];
179 d.serialize_into(&mut buf).unwrap();
180 let re = DvbJApplicationLocationDescriptor::parse(&buf).unwrap();
181 assert_eq!(d, re);
182 }
183
184 #[test]
185 fn serialize_byte_identical() {
186 let bytes = [
187 TAG, 9, 1, b'/', 4, b'a', b';', b'b', b';', b'c', b'd', ];
192 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
193 let mut buf = vec![0u8; d.serialized_len()];
194 d.serialize_into(&mut buf).unwrap();
195 assert_eq!(buf.as_slice(), &bytes[..]);
196 }
197}