use core::fmt;
mod clock;
mod conn;
pub mod timers;
pub use clock::{Instant, Millis};
pub use conn::{Connection, ConnectionError, MAX_PENDING_FRAMES};
pub use timers::{Timer, Timers};
#[cfg(feature = "iso2")]
#[cfg_attr(docsrs, doc(cfg(feature = "iso2")))]
pub mod iso2;
#[cfg(feature = "iso20-common")]
#[cfg_attr(docsrs, doc(cfg(feature = "iso20-common")))]
pub mod iso20;
#[cfg(any(feature = "iso2", feature = "iso20-common"))]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Flow {
#[cfg(feature = "iso2")]
#[cfg_attr(docsrs, doc(cfg(feature = "iso2")))]
Iso2(iso2::Sequencer),
#[cfg(feature = "iso20-common")]
#[cfg_attr(docsrs, doc(cfg(feature = "iso20-common")))]
Iso20(iso20::Sequencer),
}
#[cfg(any(feature = "iso2", feature = "iso20-common"))]
impl Flow {
#[must_use]
#[allow(unused_variables, reason = "every arm is behind a feature")]
pub fn new(protocol: crate::Protocol) -> Option<Self> {
match protocol {
#[cfg(feature = "iso2")]
crate::Protocol::Iso2 => Some(Self::Iso2(iso2::Sequencer::new())),
#[cfg(feature = "iso20-common")]
crate::Protocol::Iso20 => Some(Self::Iso20(iso20::Sequencer::new())),
_ => None,
}
}
#[allow(unused_variables, reason = "every arm is behind a feature")]
pub fn accept(&mut self, message: &crate::message::Message) -> Result<(), FlowError> {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => {
let request = iso2::Request::of(message).ok_or(FlowError::NotARequest)?;
s.accept(request)?;
}
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => {
let request = iso20::Request::of(message).ok_or(FlowError::NotARequest)?;
s.accept(request)?;
}
}
Ok(())
}
#[allow(unused_variables, reason = "every arm is behind a feature")]
pub fn permits(&self, message: &crate::message::Message) -> Result<(), FlowError> {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => {
let request = iso2::Request::of(message).ok_or(FlowError::NotARequest)?;
if !s.permits(request) {
return Err(FlowError::Sequence(s.refusal(request)));
}
}
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => {
let request = iso20::Request::of(message).ok_or(FlowError::NotARequest)?;
if !s.permits(request) {
return Err(FlowError::Sequence(s.refusal(request)));
}
}
}
Ok(())
}
#[must_use]
pub fn is_finished(&self) -> bool {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.is_finished(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.is_finished(),
}
}
#[must_use]
pub fn is_paused(&self) -> bool {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.is_paused(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.is_paused(),
}
}
pub fn failed(&mut self) {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.failed(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.failed(),
}
}
#[must_use]
pub fn is_failed(&self) -> bool {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.is_failed(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.is_failed(),
}
}
#[cfg(feature = "iso20-common")]
#[cfg_attr(docsrs, doc(cfg(feature = "iso20-common")))]
pub fn resume(&mut self, service: iso20::Service) -> bool {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(_) => false,
Self::Iso20(s) => {
s.resume(service);
true
}
}
}
#[must_use]
pub const fn loop_timeout(&self) -> Option<Millis> {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.loop_timeout(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.loop_timeout(),
}
}
#[must_use]
pub const fn phase_name(&self) -> &'static str {
match self {
#[cfg(feature = "iso2")]
Self::Iso2(s) => s.phase_name(),
#[cfg(feature = "iso20-common")]
Self::Iso20(s) => s.phase_name(),
}
}
}
#[cfg(any(feature = "iso2", feature = "iso20-common"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum FlowError {
Sequence(SequenceError),
NotARequest,
}
#[cfg(any(feature = "iso2", feature = "iso20-common"))]
impl From<SequenceError> for FlowError {
fn from(e: SequenceError) -> Self {
Self::Sequence(e)
}
}
#[cfg(any(feature = "iso2", feature = "iso20-common"))]
impl fmt::Display for FlowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Sequence(e) => write!(f, "{e}"),
Self::NotARequest => f.write_str("expected a request"),
}
}
}
#[cfg(all(feature = "std", any(feature = "iso2", feature = "iso20-common")))]
impl std::error::Error for FlowError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SequenceError {
pub got: &'static str,
pub phase: &'static str,
pub response_code: u8,
}
impl fmt::Display for SequenceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} is out of sequence in phase {}", self.got, self.phase)
}
}
#[cfg(feature = "std")]
impl std::error::Error for SequenceError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SessionId([u8; Self::LEN]);
impl SessionId {
pub const LEN: usize = 8;
pub const NONE: Self = Self([0; Self::LEN]);
#[must_use]
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
#[must_use]
pub fn is_none(&self) -> bool {
self.0 == [0; Self::LEN]
}
pub fn from_slice(bytes: &[u8]) -> Result<Self, SessionIdError> {
if bytes.len() > Self::LEN {
return Err(SessionIdError { len: bytes.len() });
}
let mut out = [0u8; Self::LEN];
out[..bytes.len()].copy_from_slice(bytes);
Ok(Self(out))
}
}
impl fmt::Display for SessionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
write!(f, "{byte:02X}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SessionIdError {
pub len: usize,
}
impl fmt::Display for SessionIdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SessionID is {} bytes, the schema allows at most {}", self.len, SessionId::LEN)
}
}
#[cfg(feature = "std")]
impl std::error::Error for SessionIdError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_fresh_session_has_no_id() {
assert!(SessionId::NONE.is_none());
assert!(!SessionId::new([1, 0, 0, 0, 0, 0, 0, 0]).is_none());
}
#[test]
fn a_short_session_id_is_zero_extended() {
let short = SessionId::from_slice(&[0xAB, 0xCD]).unwrap();
assert_eq!(short.as_bytes(), &[0xAB, 0xCD, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn an_over_long_session_id_is_refused() {
assert_eq!(SessionId::from_slice(&[0; 9]), Err(SessionIdError { len: 9 }));
}
#[test]
fn session_ids_display_as_hex() {
extern crate alloc;
use alloc::string::ToString;
assert_eq!(
SessionId::new([0x3D, 0x4C, 0xBF, 0x93, 0x37, 0x4E, 0xD8, 0x9B]).to_string(),
"3D4CBF93374ED89B"
);
}
}