use crate::error::{Error, Result};
use crate::objects;
use crate::reserved::{ReservedOnes, ReservedZero};
use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
pub mod tag {
use crate::tag::ApduTag;
pub const SERVICE_LIST_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
pub const SERVICE_LIST_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
pub const SERVICE_LIST_VERSION_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
pub const SERVICE_LIST_VERSION_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x03);
pub const SERVICE_LIST_CHANGED: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x04);
pub const SERVICE_DESC_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x05);
pub const SERVICE_DESC_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x06);
pub const GET_SERVICE_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x07);
pub const GET_SERVICE_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x08);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceReference {
pub original_network_id: u16,
pub service_id: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceListReq {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceListVersionReq {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceListAck {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub version_number: u8,
pub services: Vec<ServiceReference>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceListVersionAck {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub version_number: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceListChanged {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub version_number: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceDescReq {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub service: ServiceReference,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceDescAck<'a> {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub service: ServiceReference,
pub eit_schedule_flag: bool,
pub eit_present_following_flag: bool,
pub running_status: u8,
pub free_ca_mode: bool,
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub descriptors: &'a [u8],
#[cfg_attr(feature = "serde", serde(skip))]
pub reserved: ReservedOnes<6>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct GetServiceReq {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub service: Option<ServiceReference>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct GetServiceAck {
#[cfg_attr(feature = "serde", serde(skip))]
pub length_form: crate::length::LengthForm,
pub service: ServiceReference,
pub service_terminated: bool,
pub service_not_available: bool,
pub ca_service_flag: bool,
pub actual_service: u16,
#[cfg_attr(feature = "serde", serde(skip))]
pub reserved: ReservedZero<5>,
}
crate::dispatch::declare_empty_apdu!(ServiceListReq, tag::SERVICE_LIST_REQ, "ServiceListReq");
crate::dispatch::declare_empty_apdu!(
ServiceListVersionReq,
tag::SERVICE_LIST_VERSION_REQ,
"ServiceListVersionReq"
);
macro_rules! version_byte_object {
($ty:ty, $tag:expr, $what:literal) => {
impl<'a> Parse<'a> for $ty {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) = objects::parse_apdu_header(bytes, $tag, $what)?;
if body.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: 0,
what: $what,
});
}
objects::reject_trailing_body(body, 1, $what)?;
Ok(Self {
length_form,
version_number: body[0],
})
}
}
impl Serialize for $ty {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(1, self.length_form)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pos = objects::write_apdu_header($tag, 1, self.length_form, buf)?;
buf[pos] = self.version_number;
Ok(pos + 1)
}
}
};
}
version_byte_object!(
ServiceListVersionAck,
tag::SERVICE_LIST_VERSION_ACK,
"ServiceListVersionAck"
);
version_byte_object!(
ServiceListChanged,
tag::SERVICE_LIST_CHANGED,
"ServiceListChanged"
);
const SERVICE_REF_LEN: usize = 4;
const SERVICE_LIST_ACK_PREFIX: usize = 3;
impl<'a> Parse<'a> for ServiceListAck {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) =
objects::parse_apdu_header(bytes, tag::SERVICE_LIST_ACK, "ServiceListAck")?;
if body.len() < SERVICE_LIST_ACK_PREFIX {
return Err(Error::BufferTooShort {
need: SERVICE_LIST_ACK_PREFIX,
have: body.len(),
what: "ServiceListAck",
});
}
let version_number = body[0];
let count = u16::from_be_bytes([body[1], body[2]]) as usize;
let list = &body[SERVICE_LIST_ACK_PREFIX..];
if list.len() < count * SERVICE_REF_LEN {
return Err(Error::LengthMismatch {
what: "ServiceListAck services",
declared: count * SERVICE_REF_LEN,
actual: list.len(),
});
}
objects::reject_trailing_body(list, count * SERVICE_REF_LEN, "ServiceListAck")?;
let mut services = Vec::with_capacity(count);
for chunk in list[..count * SERVICE_REF_LEN].chunks_exact(SERVICE_REF_LEN) {
services.push(ServiceReference {
original_network_id: u16::from_be_bytes([chunk[0], chunk[1]]),
service_id: u16::from_be_bytes([chunk[2], chunk[3]]),
});
}
Ok(Self {
length_form,
version_number,
services,
})
}
}
impl Serialize for ServiceListAck {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(
SERVICE_LIST_ACK_PREFIX + self.services.len() * SERVICE_REF_LEN,
self.length_form,
)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if self.services.len() > u16::MAX as usize {
return Err(Error::InvalidObject {
what: "ServiceListAck",
reason: "more than 65535 services",
});
}
let body_len = SERVICE_LIST_ACK_PREFIX + self.services.len() * SERVICE_REF_LEN;
let mut pos =
objects::write_apdu_header(tag::SERVICE_LIST_ACK, body_len, self.length_form, buf)?;
buf[pos] = self.version_number;
buf[pos + 1..pos + 3].copy_from_slice(&(self.services.len() as u16).to_be_bytes());
pos += SERVICE_LIST_ACK_PREFIX;
for s in &self.services {
buf[pos..pos + 2].copy_from_slice(&s.original_network_id.to_be_bytes());
buf[pos + 2..pos + 4].copy_from_slice(&s.service_id.to_be_bytes());
pos += SERVICE_REF_LEN;
}
Ok(pos)
}
}
impl<'a> Parse<'a> for ServiceDescReq {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) =
objects::parse_apdu_header(bytes, tag::SERVICE_DESC_REQ, "ServiceDescReq")?;
if body.len() < SERVICE_REF_LEN {
return Err(Error::BufferTooShort {
need: SERVICE_REF_LEN,
have: body.len(),
what: "ServiceDescReq",
});
}
objects::reject_trailing_body(body, SERVICE_REF_LEN, "ServiceDescReq")?;
Ok(Self {
length_form,
service: ServiceReference {
original_network_id: u16::from_be_bytes([body[0], body[1]]),
service_id: u16::from_be_bytes([body[2], body[3]]),
},
})
}
}
impl Serialize for ServiceDescReq {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(SERVICE_REF_LEN, self.length_form)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pos = objects::write_apdu_header(
tag::SERVICE_DESC_REQ,
SERVICE_REF_LEN,
self.length_form,
buf,
)?;
buf[pos..pos + 2].copy_from_slice(&self.service.original_network_id.to_be_bytes());
buf[pos + 2..pos + 4].copy_from_slice(&self.service.service_id.to_be_bytes());
Ok(pos + SERVICE_REF_LEN)
}
}
const SERVICE_DESC_ACK_PREFIX: usize = SERVICE_REF_LEN + 3;
impl<'a> Parse<'a> for ServiceDescAck<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) =
objects::parse_apdu_header(bytes, tag::SERVICE_DESC_ACK, "ServiceDescAck")?;
if body.len() < SERVICE_DESC_ACK_PREFIX {
return Err(Error::BufferTooShort {
need: SERVICE_DESC_ACK_PREFIX,
have: body.len(),
what: "ServiceDescAck",
});
}
let flags = body[4];
let b5 = body[5];
let b6 = body[6];
let running_status = (b5 >> 5) & 0x07;
let free_ca_mode = (b5 & 0x10) != 0;
let loop_len = ((u16::from(b5 & 0x0F) << 8) | u16::from(b6)) as usize;
let desc_start = SERVICE_DESC_ACK_PREFIX;
let desc_end = desc_start + loop_len;
if body.len() < desc_end {
return Err(Error::LengthMismatch {
what: "ServiceDescAck descriptors",
declared: loop_len,
actual: body.len() - desc_start,
});
}
objects::reject_trailing_body(body, desc_end, "ServiceDescAck")?;
Ok(Self {
length_form,
service: ServiceReference {
original_network_id: u16::from_be_bytes([body[0], body[1]]),
service_id: u16::from_be_bytes([body[2], body[3]]),
},
eit_schedule_flag: (flags & 0x02) != 0,
eit_present_following_flag: (flags & 0x01) != 0,
running_status,
free_ca_mode,
descriptors: &body[desc_start..desc_end],
reserved: ReservedOnes::from_bits(flags >> 2),
})
}
}
impl Serialize for ServiceDescAck<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(
SERVICE_DESC_ACK_PREFIX + self.descriptors.len(),
self.length_form,
)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if self.descriptors.len() > 0x0FFF {
return Err(Error::InvalidObject {
what: "ServiceDescAck",
reason: "descriptors loop longer than 4095 bytes",
});
}
objects::fit_bits(
u64::from(self.running_status),
3,
"ServiceDescAck running_status",
)?;
let body_len = SERVICE_DESC_ACK_PREFIX + self.descriptors.len();
let mut pos =
objects::write_apdu_header(tag::SERVICE_DESC_ACK, body_len, self.length_form, buf)?;
buf[pos..pos + 2].copy_from_slice(&self.service.original_network_id.to_be_bytes());
buf[pos + 2..pos + 4].copy_from_slice(&self.service.service_id.to_be_bytes());
buf[pos + 4] = (self.reserved.get() << 2)
| (u8::from(self.eit_schedule_flag) << 1)
| u8::from(self.eit_present_following_flag);
let loop_len = self.descriptors.len() as u16;
buf[pos + 5] = (self.running_status << 5)
| (u8::from(self.free_ca_mode) << 4)
| ((loop_len >> 8) as u8 & 0x0F);
buf[pos + 6] = loop_len as u8;
pos += SERVICE_DESC_ACK_PREFIX;
buf[pos..pos + self.descriptors.len()].copy_from_slice(self.descriptors);
Ok(pos + self.descriptors.len())
}
}
impl<'a> Parse<'a> for GetServiceReq {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) =
objects::parse_apdu_header(bytes, tag::GET_SERVICE_REQ, "GetServiceReq")?;
let service = if body.is_empty() {
None
} else {
if body.len() < SERVICE_REF_LEN {
return Err(Error::BufferTooShort {
need: SERVICE_REF_LEN,
have: body.len(),
what: "GetServiceReq",
});
}
objects::reject_trailing_body(body, SERVICE_REF_LEN, "GetServiceReq")?;
Some(ServiceReference {
original_network_id: u16::from_be_bytes([body[0], body[1]]),
service_id: u16::from_be_bytes([body[2], body[3]]),
})
};
Ok(Self {
length_form,
service,
})
}
}
impl Serialize for GetServiceReq {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(
if self.service.is_some() {
SERVICE_REF_LEN
} else {
0
},
self.length_form,
)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
match self.service {
None => objects::write_apdu_header(tag::GET_SERVICE_REQ, 0, self.length_form, buf),
Some(s) => {
let pos = objects::write_apdu_header(
tag::GET_SERVICE_REQ,
SERVICE_REF_LEN,
self.length_form,
buf,
)?;
buf[pos..pos + 2].copy_from_slice(&s.original_network_id.to_be_bytes());
buf[pos + 2..pos + 4].copy_from_slice(&s.service_id.to_be_bytes());
Ok(pos + SERVICE_REF_LEN)
}
}
}
}
const GET_SERVICE_ACK_BODY: usize = SERVICE_REF_LEN + 1 + 2;
impl<'a> Parse<'a> for GetServiceAck {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let (length_form, body) =
objects::parse_apdu_header(bytes, tag::GET_SERVICE_ACK, "GetServiceAck")?;
if body.len() < GET_SERVICE_ACK_BODY {
return Err(Error::BufferTooShort {
need: GET_SERVICE_ACK_BODY,
have: body.len(),
what: "GetServiceAck",
});
}
crate::objects::reject_trailing_body(body, GET_SERVICE_ACK_BODY, "GetServiceAck")?;
let flags = body[4];
Ok(Self {
length_form,
service: ServiceReference {
original_network_id: u16::from_be_bytes([body[0], body[1]]),
service_id: u16::from_be_bytes([body[2], body[3]]),
},
service_terminated: (flags & 0x04) != 0,
service_not_available: (flags & 0x02) != 0,
ca_service_flag: (flags & 0x01) != 0,
actual_service: u16::from_be_bytes([body[5], body[6]]),
reserved: ReservedZero::from_bits(flags >> 3),
})
}
}
impl Serialize for GetServiceAck {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(GET_SERVICE_ACK_BODY, self.length_form)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pos = objects::write_apdu_header(
tag::GET_SERVICE_ACK,
GET_SERVICE_ACK_BODY,
self.length_form,
buf,
)?;
buf[pos..pos + 2].copy_from_slice(&self.service.original_network_id.to_be_bytes());
buf[pos + 2..pos + 4].copy_from_slice(&self.service.service_id.to_be_bytes());
buf[pos + 4] = (self.reserved.get() << 3)
| (u8::from(self.service_terminated) << 2)
| (u8::from(self.service_not_available) << 1)
| u8::from(self.ca_service_flag);
buf[pos + 5..pos + 7].copy_from_slice(&self.actual_service.to_be_bytes());
Ok(pos + GET_SERVICE_ACK_BODY)
}
}
crate::dispatch::declare_resource_apdus! {
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum ServiceGatewayApdu<'a> ("service_gateway") {
ServiceListReq(ServiceListReq) = tag::SERVICE_LIST_REQ,
ServiceListAck(ServiceListAck) = tag::SERVICE_LIST_ACK,
ServiceListVersionReq(ServiceListVersionReq) = tag::SERVICE_LIST_VERSION_REQ,
ServiceListVersionAck(ServiceListVersionAck) = tag::SERVICE_LIST_VERSION_ACK,
ServiceListChanged(ServiceListChanged) = tag::SERVICE_LIST_CHANGED,
ServiceDescReq(ServiceDescReq) = tag::SERVICE_DESC_REQ,
ServiceDescAck(ServiceDescAck<'a>) = tag::SERVICE_DESC_ACK,
GetServiceReq(GetServiceReq) = tag::GET_SERVICE_REQ,
GetServiceAck(GetServiceAck) = tag::GET_SERVICE_ACK,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_only_objects_round_trip() {
assert_eq!(
ServiceListReq::default().try_to_bytes().unwrap(),
[0x9F, 0x80, 0x00, 0x00]
);
assert_eq!(
ServiceListVersionReq::default().try_to_bytes().unwrap(),
[0x9F, 0x80, 0x02, 0x00]
);
assert_eq!(
ServiceListReq::parse(&[0x9F, 0x80, 0x00, 0x00]).unwrap(),
ServiceListReq::default()
);
}
#[test]
fn service_list_ack_multi_round_trips_and_bites() {
let ack = ServiceListAck {
length_form: crate::length::LengthForm::Minimal,
version_number: 0x07,
services: alloc::vec![
ServiceReference {
original_network_id: 0x0001,
service_id: 0x0064,
},
ServiceReference {
original_network_id: 0x0001,
service_id: 0x0065,
},
],
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(
bytes,
[
0x9F, 0x80, 0x01, 0x0B, 0x07, 0x00, 0x02, 0x00, 0x01, 0x00, 0x64, 0x00, 0x01, 0x00,
0x65
]
);
assert_eq!(ServiceListAck::parse(&bytes).unwrap(), ack);
let mut other = ack.clone();
other.services[1].service_id = 0x0066;
assert_ne!(bytes, other.try_to_bytes().unwrap());
}
#[test]
fn service_list_ack_empty_round_trips() {
let ack = ServiceListAck {
length_form: crate::length::LengthForm::Minimal,
version_number: 0x02,
services: alloc::vec![],
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x03, 0x02, 0x00, 0x00]);
assert_eq!(ServiceListAck::parse(&bytes).unwrap(), ack);
}
#[test]
fn version_ack_and_changed_round_trip_and_bite() {
let v = ServiceListVersionAck {
length_form: crate::length::LengthForm::Minimal,
version_number: 9,
};
let bytes = v.try_to_bytes().unwrap();
assert_eq!(bytes, [0x9F, 0x80, 0x03, 0x01, 0x09]);
assert_eq!(ServiceListVersionAck::parse(&bytes).unwrap(), v);
assert_ne!(
bytes,
ServiceListVersionAck {
length_form: crate::length::LengthForm::Minimal,
version_number: 10
}
.try_to_bytes()
.unwrap()
);
let c = ServiceListChanged {
length_form: crate::length::LengthForm::Minimal,
version_number: 3,
};
let cbytes = c.try_to_bytes().unwrap();
assert_eq!(cbytes, [0x9F, 0x80, 0x04, 0x01, 0x03]);
assert_eq!(ServiceListChanged::parse(&cbytes).unwrap(), c);
}
#[test]
fn service_desc_req_round_trips_and_bites() {
let r = ServiceDescReq {
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 0x1234,
service_id: 0x5678,
},
};
let bytes = r.try_to_bytes().unwrap();
assert_eq!(bytes, [0x9F, 0x80, 0x05, 0x04, 0x12, 0x34, 0x56, 0x78]);
assert_eq!(ServiceDescReq::parse(&bytes).unwrap(), r);
let mut other = r;
other.service.service_id = 0x5679;
assert_ne!(bytes, other.try_to_bytes().unwrap());
}
#[test]
fn service_desc_ack_round_trips_and_bites() {
let desc = [0x48, 0x02, 0xAA, 0xBB, 0x52, 0x01, 0x03];
let ack = ServiceDescAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 0x0001,
service_id: 0x0064,
},
eit_schedule_flag: true,
eit_present_following_flag: false,
running_status: 4, free_ca_mode: true,
descriptors: &desc,
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(
bytes,
[
0x9F, 0x80, 0x06, 0x0E, 0x00, 0x01, 0x00, 0x64, 0xFE, 0x90, 0x07, 0x48, 0x02, 0xAA,
0xBB, 0x52, 0x01, 0x03
]
);
let parsed = ServiceDescAck::parse(&bytes).unwrap();
assert_eq!(parsed, ack);
assert_eq!(parsed.running_status, 4);
assert!(parsed.eit_schedule_flag);
assert!(!parsed.eit_present_following_flag);
assert!(parsed.free_ca_mode);
let mut other = ack.clone();
other.running_status = 1;
assert_ne!(bytes, other.try_to_bytes().unwrap());
}
#[test]
fn service_desc_ack_empty_loop() {
let ack = ServiceDescAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 0xAAAA,
service_id: 0xBBBB,
},
eit_schedule_flag: false,
eit_present_following_flag: true,
running_status: 0,
free_ca_mode: false,
descriptors: &[],
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(
bytes,
[
0x9F, 0x80, 0x06, 0x07, 0xAA, 0xAA, 0xBB, 0xBB, 0xFD, 0x00, 0x00
]
);
assert_eq!(ServiceDescAck::parse(&bytes).unwrap(), ack);
}
#[test]
fn get_service_req_with_and_without_ref() {
let r = GetServiceReq {
length_form: crate::length::LengthForm::Minimal,
service: Some(ServiceReference {
original_network_id: 0x0001,
service_id: 0x0064,
}),
};
let bytes = r.try_to_bytes().unwrap();
assert_eq!(bytes, [0x9F, 0x80, 0x07, 0x04, 0x00, 0x01, 0x00, 0x64]);
assert_eq!(GetServiceReq::parse(&bytes).unwrap(), r);
let disconnect = GetServiceReq {
length_form: crate::length::LengthForm::Minimal,
service: None,
};
let dbytes = disconnect.try_to_bytes().unwrap();
assert_eq!(dbytes, [0x9F, 0x80, 0x07, 0x00]);
assert_eq!(GetServiceReq::parse(&dbytes).unwrap(), disconnect);
assert_ne!(bytes, dbytes);
}
#[test]
fn get_service_ack_round_trips_and_bites() {
let ack = GetServiceAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 0x0001,
service_id: 0x0064,
},
service_terminated: false,
service_not_available: false,
ca_service_flag: true,
actual_service: 0x0064,
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(
bytes,
[
0x9F, 0x80, 0x08, 0x07, 0x00, 0x01, 0x00, 0x64, 0x01, 0x00, 0x64
]
);
assert_eq!(GetServiceAck::parse(&bytes).unwrap(), ack);
let mut other = ack;
other.ca_service_flag = false;
other.service_not_available = true;
other.actual_service = 0;
assert_ne!(bytes, other.try_to_bytes().unwrap());
assert_eq!(other.try_to_bytes().unwrap()[8], 0x02);
}
#[test]
fn dispatch_routes_each_tag() {
let req = ServiceListReq::default().try_to_bytes().unwrap();
assert!(matches!(
ServiceGatewayApdu::parse(&req).unwrap(),
ServiceGatewayApdu::ServiceListReq(_)
));
let gs = GetServiceAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference::default(),
service_terminated: true,
service_not_available: false,
ca_service_flag: false,
actual_service: 0,
}
.try_to_bytes()
.unwrap();
let parsed = ServiceGatewayApdu::parse(&gs).unwrap();
assert!(matches!(parsed, ServiceGatewayApdu::GetServiceAck(_)));
assert_eq!(parsed.try_to_bytes().unwrap(), gs);
}
#[test]
fn oversized_running_status_is_rejected_not_wrapped() {
let ack = ServiceDescAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 1,
service_id: 1,
},
eit_schedule_flag: false,
eit_present_following_flag: false,
running_status: 0x0A,
free_ca_mode: false,
descriptors: &[],
};
let mut buf = [0u8; 32];
assert!(matches!(
ack.serialize_into(&mut buf),
Err(Error::InvalidObject { .. })
));
}
#[test]
fn max_running_status_still_serializes_and_round_trips() {
let ack = ServiceDescAck {
reserved: Default::default(),
length_form: crate::length::LengthForm::Minimal,
service: ServiceReference {
original_network_id: 1,
service_id: 1,
},
eit_schedule_flag: false,
eit_present_following_flag: false,
running_status: 0x07, free_ca_mode: false,
descriptors: &[],
};
let bytes = ack.try_to_bytes().unwrap();
assert_eq!(ServiceDescAck::parse(&bytes).unwrap(), ack);
}
}