dvb_si/descriptors/ait/
dvb_j_application_location.rs1use crate::descriptors::descriptor_body;
10use crate::error::{Error, Result};
11use crate::text::DvbText;
12use dvb_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:
99 "dvb_j_application_location_descriptor classpath_extension exceeds 255 bytes",
100 });
101 }
102 let body_len = self.serialized_len() - HEADER_LEN;
103 if body_len > u8::MAX as usize {
104 return Err(Error::InvalidDescriptor {
105 tag: TAG,
106 reason: "dvb_j_application_location_descriptor body exceeds 255 bytes",
107 });
108 }
109 let len = self.serialized_len();
110 if buf.len() < len {
111 return Err(Error::OutputBufferTooSmall {
112 need: len,
113 have: buf.len(),
114 });
115 }
116 buf[0] = TAG;
117 buf[1] = body_len as u8;
118 let mut pos = HEADER_LEN;
119 buf[pos] = self.base_directory.len() as u8;
120 pos += 1;
121 buf[pos..pos + self.base_directory.len()].copy_from_slice(self.base_directory.raw());
122 pos += self.base_directory.len();
123 buf[pos] = self.classpath_extension.len() as u8;
124 pos += 1;
125 buf[pos..pos + self.classpath_extension.len()]
126 .copy_from_slice(self.classpath_extension.raw());
127 pos += self.classpath_extension.len();
128 buf[pos..pos + self.initial_class.len()].copy_from_slice(self.initial_class.raw());
129 Ok(len)
130 }
131}
132
133impl<'a> crate::traits::DescriptorDef<'a> for DvbJApplicationLocationDescriptor<'a> {
134 const TAG: u8 = TAG;
135 const NAME: &'static str = "DVB_J_APPLICATION_LOCATION";
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn parse_full() {
144 let bytes = [
145 TAG, 10, 1, b'/', 5, b'l', b'i', b'b', b'/', b';', b'A', b'B', ];
150 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
151 assert_eq!(d.base_directory.raw(), b"/");
152 assert_eq!(d.classpath_extension.raw(), b"lib/;");
153 assert_eq!(d.initial_class.raw(), b"AB");
154 }
155
156 #[test]
157 fn parse_no_initial_class() {
158 let bytes = [
160 TAG, 4, 1, b'/', 1,
163 b';', ];
166 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
167 assert_eq!(d.base_directory.raw(), b"/");
168 assert_eq!(d.classpath_extension.raw(), b";");
169 assert!(d.initial_class.raw().is_empty());
170 }
171
172 #[test]
173 fn serialize_round_trip() {
174 let d = DvbJApplicationLocationDescriptor {
175 base_directory: DvbText::new(b"/apps"),
176 classpath_extension: DvbText::new(b"classes/"),
177 initial_class: DvbText::new(b"com.example.Main"),
178 };
179 let mut buf = vec![0u8; d.serialized_len()];
180 d.serialize_into(&mut buf).unwrap();
181 let re = DvbJApplicationLocationDescriptor::parse(&buf).unwrap();
182 assert_eq!(d, re);
183 }
184
185 #[test]
186 fn serialize_byte_identical() {
187 let bytes = [
188 TAG, 9, 1, b'/', 4, b'a', b';', b'b', b';', b'c', b'd', ];
193 let d = DvbJApplicationLocationDescriptor::parse(&bytes).unwrap();
194 let mut buf = vec![0u8; d.serialized_len()];
195 d.serialize_into(&mut buf).unwrap();
196 assert_eq!(buf.as_slice(), &bytes[..]);
197 }
198}