use broadcast_common::Parse;
pub mod tag {
use crate::tag::ApduTag;
pub const CAP_REQUEST: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
pub const CAP_RESPONSE: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
pub const CAP_NOTIFICATION: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CaPipelineRequest<'a> {
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub ca_specific_data: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CaPipelineResponse<'a> {
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub ca_specific_data: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CaPipelineNotification<'a> {
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub ca_specific_data: &'a [u8],
}
crate::dispatch::declare_opaque_apdu!(
CaPipelineRequest,
ca_specific_data,
tag::CAP_REQUEST,
"CAPipelineRequest"
);
crate::dispatch::declare_opaque_apdu!(
CaPipelineResponse,
ca_specific_data,
tag::CAP_RESPONSE,
"CAPipelineResponse"
);
crate::dispatch::declare_opaque_apdu!(
CaPipelineNotification,
ca_specific_data,
tag::CAP_NOTIFICATION,
"CAPipelineNotification"
);
crate::dispatch::declare_resource_apdus! {
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum CaPipelineApdu<'a> ("ca_pipeline") {
Request(CaPipelineRequest<'a>) = tag::CAP_REQUEST,
Response(CaPipelineResponse<'a>) = tag::CAP_RESPONSE,
Notification(CaPipelineNotification<'a>) = tag::CAP_NOTIFICATION,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
use broadcast_common::Serialize;
#[test]
fn request_round_trips_and_bites() {
let req = CaPipelineRequest {
ca_specific_data: &[0xAA, 0xBB, 0xCC],
};
let bytes = req.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x00, 0x03, 0xAA, 0xBB, 0xCC]);
assert_eq!(CaPipelineRequest::parse(&bytes).unwrap(), req);
let other = CaPipelineRequest {
ca_specific_data: &[0xAA, 0xBB, 0xCD],
};
assert_ne!(bytes, other.to_bytes());
}
#[test]
fn response_and_notification_round_trip() {
let resp = CaPipelineResponse {
ca_specific_data: &[0x01],
};
let bytes = resp.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x01, 0x01, 0x01]);
assert_eq!(CaPipelineResponse::parse(&bytes).unwrap(), resp);
let note = CaPipelineNotification {
ca_specific_data: &[],
};
let bytes = note.to_bytes();
assert_eq!(bytes, [0x9F, 0x80, 0x02, 0x00]);
assert_eq!(CaPipelineNotification::parse(&bytes).unwrap(), note);
}
#[test]
fn dispatch_routes_each_tag() {
let req = CaPipelineRequest {
ca_specific_data: &[0x10],
}
.to_bytes();
assert!(matches!(
CaPipelineApdu::parse(&req).unwrap(),
CaPipelineApdu::Request(_)
));
let resp = CaPipelineResponse {
ca_specific_data: &[0x20],
}
.to_bytes();
assert!(matches!(
CaPipelineApdu::parse(&resp).unwrap(),
CaPipelineApdu::Response(_)
));
let note = CaPipelineNotification {
ca_specific_data: &[0x30],
}
.to_bytes();
let parsed = CaPipelineApdu::parse(¬e).unwrap();
assert!(matches!(parsed, CaPipelineApdu::Notification(_)));
assert_eq!(parsed.to_bytes(), note);
}
#[test]
fn unexpected_tag_errors() {
let bad = [0x9F, 0x80, 0x09, 0x00];
assert!(matches!(
CaPipelineApdu::parse(&bad),
Err(Error::UnexpectedApduTag { .. })
));
}
}