use crate::{
psi::{
DescriptorRef,
PsiSectionError,
},
utils::textcode::{
TextcodeError,
TextcodeRef,
},
};
#[derive(Debug, Clone, Copy)]
pub struct ServiceDescriptorRef<'a>(&'a [u8]);
impl<'a> ServiceDescriptorRef<'a> {
pub const TAG: u8 = 0x48;
pub fn service_type(&self) -> u8 {
self.0[0]
}
fn provider_name_len(&self) -> usize {
self.0[1] as usize
}
fn service_name_len_pos(&self) -> usize {
2 + self.provider_name_len()
}
pub fn provider_name(&self) -> &'a [u8] {
let start = 2;
let end = start + self.provider_name_len();
&self.0[start .. end]
}
pub fn provider_name_text(&self) -> Result<TextcodeRef<'a>, TextcodeError> {
TextcodeRef::try_from(self.provider_name())
}
pub fn service_name(&self) -> &'a [u8] {
let len_pos = self.service_name_len_pos();
let len = self.0[len_pos] as usize;
let start = len_pos + 1;
let end = start + len;
&self.0[start .. end]
}
pub fn service_name_text(&self) -> Result<TextcodeRef<'a>, TextcodeError> {
TextcodeRef::try_from(self.service_name())
}
}
impl<'a> TryFrom<DescriptorRef<'a>> for ServiceDescriptorRef<'a> {
type Error = PsiSectionError;
fn try_from(descriptor: DescriptorRef<'a>) -> Result<Self, Self::Error> {
if descriptor.tag() != Self::TAG {
return Err(PsiSectionError::InvalidDescriptorTag);
}
let data = descriptor.data();
if data.len() < 3 {
return Err(PsiSectionError::InvalidDescriptorLength);
}
let provider_name_len = data[1] as usize;
let service_name_len_pos = 2 + provider_name_len;
if service_name_len_pos >= data.len() {
return Err(PsiSectionError::InvalidDescriptorLength);
}
let service_name_len = data[service_name_len_pos] as usize;
let service_name_end = service_name_len_pos + 1 + service_name_len;
if service_name_end != data.len() {
return Err(PsiSectionError::InvalidDescriptorLength);
}
Ok(ServiceDescriptorRef(data))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::psi::DescriptorsRef;
fn descriptor(tag: u8, payload: &[u8]) -> Vec<u8> {
let mut v = vec![tag, payload.len() as u8];
v.extend_from_slice(payload);
v
}
fn first(bytes: &[u8]) -> DescriptorRef<'_> {
DescriptorsRef::from(bytes)
.into_iter()
.next()
.unwrap()
.unwrap()
}
#[test]
fn parses_service_fields() {
let bytes = descriptor(0x48, b"\x01\x06Avalpa\x04Name");
let service = ServiceDescriptorRef::try_from(first(&bytes)).unwrap();
assert_eq!(service.service_type(), 1);
assert_eq!(service.provider_name(), b"Avalpa");
assert_eq!(service.service_name(), b"Name");
assert_eq!(service.provider_name_text().unwrap().to_string(), "Avalpa");
assert_eq!(service.service_name_text().unwrap().to_string(), "Name");
}
#[test]
fn accepts_empty_names() {
let bytes = descriptor(0x48, b"\x01\x00\x00");
let service = ServiceDescriptorRef::try_from(first(&bytes)).unwrap();
assert_eq!(service.provider_name(), b"");
assert_eq!(service.service_name(), b"");
}
#[test]
fn rejects_wrong_tag() {
let bytes = descriptor(0x49, b"\x01\x00\x00");
assert!(ServiceDescriptorRef::try_from(first(&bytes)).is_err());
}
#[test]
fn rejects_overflowing_provider_name() {
let bytes = descriptor(0x48, b"\x01\x06Aval");
assert!(ServiceDescriptorRef::try_from(first(&bytes)).is_err());
}
#[test]
fn rejects_overflowing_service_name() {
let bytes = descriptor(0x48, b"\x01\x00\x04Na");
assert!(ServiceDescriptorRef::try_from(first(&bytes)).is_err());
}
#[test]
fn rejects_trailing_bytes() {
let bytes = descriptor(0x48, b"\x01\x00\x00x");
assert!(ServiceDescriptorRef::try_from(first(&bytes)).is_err());
}
}