use crate::error::{Error, Result};
use crate::objects;
use crate::tag::ApduTag;
use dvb_common::{Parse, Serialize};
pub mod tag {
use crate::tag::ApduTag;
pub const EVENT_REQUEST: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
pub const EVENT_REQUEST_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
pub const EVENT_NOTIFICATION: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum EventType {
Timer,
Reserved(u8),
}
impl EventType {
#[must_use]
pub fn from_u8(v: u8) -> Self {
match v {
0 => Self::Timer,
other => Self::Reserved(other),
}
}
#[must_use]
pub const fn to_u8(self) -> u8 {
match self {
Self::Timer => 0,
Self::Reserved(v) => v,
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Timer => "Timer",
Self::Reserved(_) => "reserved",
}
}
}
dvb_common::impl_spec_display!(EventType, Reserved);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum EventReply {
BookedOk,
TypeNotSupported,
ResourcesConsumed,
Reserved(u8),
}
impl EventReply {
#[must_use]
pub fn from_u8(v: u8) -> Self {
match v {
0 => Self::BookedOk,
1 => Self::TypeNotSupported,
2 => Self::ResourcesConsumed,
other => Self::Reserved(other),
}
}
#[must_use]
pub const fn to_u8(self) -> u8 {
match self {
Self::BookedOk => 0,
Self::TypeNotSupported => 1,
Self::ResourcesConsumed => 2,
Self::Reserved(v) => v,
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::BookedOk => "Event booked OK",
Self::TypeNotSupported => "Event type not supported",
Self::ResourcesConsumed => "Event resources consumed",
Self::Reserved(_) => "reserved",
}
}
}
dvb_common::impl_spec_display!(EventReply, Reserved);
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EventRequest<'a> {
pub event_type: EventType,
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub event_desc: &'a [u8],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EventRequestAck {
pub event_type: EventType,
pub reply: EventReply,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct EventNotification {
pub event_type: EventType,
}
const EVENT_REQUEST_PREFIX: usize = 1;
impl<'a> Parse<'a> for EventRequest<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = objects::parse_apdu_header(bytes, tag::EVENT_REQUEST, "event_request")?;
if body.len() < EVENT_REQUEST_PREFIX {
return Err(Error::BufferTooShort {
need: EVENT_REQUEST_PREFIX,
have: body.len(),
what: "event_request",
});
}
Ok(Self {
event_type: EventType::from_u8(body[0]),
event_desc: &body[EVENT_REQUEST_PREFIX..],
})
}
}
impl Serialize for EventRequest<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(EVENT_REQUEST_PREFIX + self.event_desc.len())
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let body_len = EVENT_REQUEST_PREFIX + self.event_desc.len();
let mut pos = objects::write_apdu_header(tag::EVENT_REQUEST, body_len, buf)?;
buf[pos] = self.event_type.to_u8();
pos += EVENT_REQUEST_PREFIX;
buf[pos..pos + self.event_desc.len()].copy_from_slice(self.event_desc);
Ok(pos + self.event_desc.len())
}
}
const EVENT_REQUEST_ACK_BODY: usize = 2;
impl<'a> Parse<'a> for EventRequestAck {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = objects::parse_apdu_header(bytes, tag::EVENT_REQUEST_ACK, "event_request_ack")?;
if body.len() < EVENT_REQUEST_ACK_BODY {
return Err(Error::BufferTooShort {
need: EVENT_REQUEST_ACK_BODY,
have: body.len(),
what: "event_request_ack",
});
}
Ok(Self {
event_type: EventType::from_u8(body[0]),
reply: EventReply::from_u8(body[1]),
})
}
}
impl Serialize for EventRequestAck {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(EVENT_REQUEST_ACK_BODY)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pos = objects::write_apdu_header(tag::EVENT_REQUEST_ACK, EVENT_REQUEST_ACK_BODY, buf)?;
buf[pos] = self.event_type.to_u8();
buf[pos + 1] = self.reply.to_u8();
Ok(pos + EVENT_REQUEST_ACK_BODY)
}
}
const EVENT_NOTIFICATION_BODY: usize = 1;
impl<'a> Parse<'a> for EventNotification {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body =
objects::parse_apdu_header(bytes, tag::EVENT_NOTIFICATION, "event_notification")?;
if body.len() < EVENT_NOTIFICATION_BODY {
return Err(Error::BufferTooShort {
need: EVENT_NOTIFICATION_BODY,
have: body.len(),
what: "event_notification",
});
}
Ok(Self {
event_type: EventType::from_u8(body[0]),
})
}
}
impl Serialize for EventNotification {
type Error = Error;
fn serialized_len(&self) -> usize {
objects::apdu_len(EVENT_NOTIFICATION_BODY)
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let pos =
objects::write_apdu_header(tag::EVENT_NOTIFICATION, EVENT_NOTIFICATION_BODY, buf)?;
buf[pos] = self.event_type.to_u8();
Ok(pos + EVENT_NOTIFICATION_BODY)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum EventManagerApdu<'a> {
EventRequest(EventRequest<'a>),
EventRequestAck(EventRequestAck),
EventNotification(EventNotification),
}
impl<'a> EventManagerApdu<'a> {
pub fn parse(body: &'a [u8]) -> Result<Self> {
if body.len() < 3 {
return Err(Error::BufferTooShort {
need: 3,
have: body.len(),
what: "event_manager apdu_tag",
});
}
let t = ApduTag::from_bytes(body[0], body[1], body[2]);
match t {
tag::EVENT_REQUEST => Ok(Self::EventRequest(EventRequest::parse(body)?)),
tag::EVENT_REQUEST_ACK => Ok(Self::EventRequestAck(EventRequestAck::parse(body)?)),
tag::EVENT_NOTIFICATION => Ok(Self::EventNotification(EventNotification::parse(body)?)),
_ => Err(Error::UnexpectedApduTag {
got: t.as_u24(),
expected: tag::EVENT_REQUEST.as_u24(),
what: "event_manager",
}),
}
}
}
impl Serialize for EventManagerApdu<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
match self {
Self::EventRequest(o) => o.serialized_len(),
Self::EventRequestAck(o) => o.serialized_len(),
Self::EventNotification(o) => o.serialized_len(),
}
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
match self {
Self::EventRequest(o) => o.serialize_into(buf),
Self::EventRequestAck(o) => o.serialize_into(buf),
Self::EventNotification(o) => o.serialize_into(buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn event_request_timer_round_trips_and_bites() {
let desc = [0x11, 0x22, 0x33, 0x44, 0x55, 0x06, 0x07, 0x08];
let r = EventRequest {
event_type: EventType::Timer,
event_desc: &desc,
};
let bytes = r.to_bytes();
assert_eq!(
bytes,
[0x9F, 0x80, 0x00, 0x09, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x06, 0x07, 0x08]
);
assert_eq!(EventRequest::parse(&bytes).unwrap(), r);
let mut desc2 = desc;
desc2[0] = 0xFF;
let other = EventRequest {
event_type: EventType::Timer,
event_desc: &desc2,
};
assert_ne!(bytes, other.to_bytes());
}
#[test]
fn event_request_cancel_empty_desc() {
let r = EventRequest {
event_type: EventType::Timer,
event_desc: &[],
};
let bytes = r.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x00, 0x01, 0x00]);
assert_eq!(EventRequest::parse(&bytes).unwrap(), r);
}
#[test]
fn event_request_ack_round_trips_and_bites() {
let a = EventRequestAck {
event_type: EventType::Timer,
reply: EventReply::ResourcesConsumed,
};
let bytes = a.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x02, 0x00, 0x02]);
assert_eq!(EventRequestAck::parse(&bytes).unwrap(), a);
assert_eq!(a.reply.name(), "Event resources consumed");
let mut other = a;
other.reply = EventReply::BookedOk;
assert_ne!(bytes, other.to_bytes());
}
#[test]
fn event_notification_round_trips() {
let n = EventNotification {
event_type: EventType::Timer,
};
let bytes = n.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x02, 0x01, 0x00]);
assert_eq!(EventNotification::parse(&bytes).unwrap(), n);
}
#[test]
fn dispatch_routes_each_tag() {
let req = EventRequest {
event_type: EventType::Timer,
event_desc: &[],
}
.to_bytes();
assert!(matches!(
EventManagerApdu::parse(&req).unwrap(),
EventManagerApdu::EventRequest(_)
));
let notif = EventNotification {
event_type: EventType::Timer,
}
.to_bytes();
let parsed = EventManagerApdu::parse(¬if).unwrap();
assert!(matches!(parsed, EventManagerApdu::EventNotification(_)));
assert_eq!(parsed.to_bytes(), notif);
}
}