use std::fmt;
use thiserror::Error;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RedispatchDocumentKind {
Activation,
PlannedResourceSchedule,
Acknowledgement,
Stammdaten,
StatusRequest,
Unavailability,
Kaskade,
NetworkConstraint,
Kostenblatt,
}
impl fmt::Display for RedispatchDocumentKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Activation => write!(f, "ActivationDocument"),
Self::PlannedResourceSchedule => write!(f, "PlannedResourceScheduleDocument"),
Self::Acknowledgement => write!(f, "AcknowledgementDocument"),
Self::Stammdaten => write!(f, "Stammdaten"),
Self::StatusRequest => write!(f, "StatusRequest_MarketDocument"),
Self::Unavailability => write!(f, "Unavailability_MarketDocument"),
Self::Kaskade => write!(f, "Kaskade"),
Self::NetworkConstraint => write!(f, "NetworkConstraintDocument"),
Self::Kostenblatt => write!(f, "Kostenblatt"),
}
}
}
#[derive(Debug, Error)]
#[error("no Redispatch workflow registered for document kind {doc_kind}")]
pub struct RoutingError {
pub doc_kind: RedispatchDocumentKind,
}
#[derive(Debug, Default, Clone)]
pub struct RedispatchRouter {
entries: [Option<&'static str>; Self::TABLE_SIZE],
}
impl RedispatchRouter {
const TABLE_SIZE: usize = 16;
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, doc_kind: RedispatchDocumentKind, workflow_name: &'static str) {
let idx = doc_kind as usize;
debug_assert!(
idx < Self::TABLE_SIZE,
"RedispatchDocumentKind discriminant {idx} exceeds RedispatchRouter table size; \
increase TABLE_SIZE"
);
if idx < Self::TABLE_SIZE {
self.entries[idx] = Some(workflow_name);
}
}
pub fn route(&self, doc_kind: RedispatchDocumentKind) -> Result<&'static str, RoutingError> {
let idx = doc_kind as usize;
if idx < Self::TABLE_SIZE {
self.entries[idx].ok_or(RoutingError { doc_kind })
} else {
Err(RoutingError { doc_kind })
}
}
#[must_use]
pub fn is_registered(&self, doc_kind: RedispatchDocumentKind) -> bool {
self.route(doc_kind).is_ok()
}
pub fn iter(&self) -> impl Iterator<Item = (RedispatchDocumentKind, &'static str)> + '_ {
ALL_DOC_KINDS
.iter()
.filter_map(|&dk| self.entries[dk as usize].map(|name| (dk, name)))
}
}
const ALL_DOC_KINDS: &[RedispatchDocumentKind] = &[
RedispatchDocumentKind::Activation,
RedispatchDocumentKind::PlannedResourceSchedule,
RedispatchDocumentKind::Acknowledgement,
RedispatchDocumentKind::Stammdaten,
RedispatchDocumentKind::StatusRequest,
RedispatchDocumentKind::Unavailability,
RedispatchDocumentKind::Kaskade,
RedispatchDocumentKind::NetworkConstraint,
RedispatchDocumentKind::Kostenblatt,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_and_route_roundtrip() {
let mut router = RedispatchRouter::new();
router.register(RedispatchDocumentKind::Activation, "redispatch-aktivierung");
router.register(RedispatchDocumentKind::Stammdaten, "redispatch-stammdaten");
assert_eq!(
router.route(RedispatchDocumentKind::Activation).unwrap(),
"redispatch-aktivierung"
);
assert_eq!(
router.route(RedispatchDocumentKind::Stammdaten).unwrap(),
"redispatch-stammdaten"
);
}
#[test]
fn unregistered_doc_kind_returns_error() {
let router = RedispatchRouter::new();
assert!(router.route(RedispatchDocumentKind::Kostenblatt).is_err());
}
#[test]
fn duplicate_registration_overwrites() {
let mut router = RedispatchRouter::new();
router.register(RedispatchDocumentKind::Activation, "first");
router.register(RedispatchDocumentKind::Activation, "second");
assert_eq!(
router.route(RedispatchDocumentKind::Activation).unwrap(),
"second"
);
}
#[test]
fn is_registered_reflects_state() {
let mut router = RedispatchRouter::new();
assert!(!router.is_registered(RedispatchDocumentKind::Activation));
router.register(RedispatchDocumentKind::Activation, "redispatch-aktivierung");
assert!(router.is_registered(RedispatchDocumentKind::Activation));
}
#[test]
fn iter_returns_only_registered() {
let mut router = RedispatchRouter::new();
router.register(RedispatchDocumentKind::Activation, "redispatch-aktivierung");
router.register(RedispatchDocumentKind::Stammdaten, "redispatch-stammdaten");
let pairs: Vec<_> = router.iter().collect();
assert_eq!(pairs.len(), 2);
assert!(
pairs
.iter()
.any(|(dk, _)| *dk == RedispatchDocumentKind::Activation)
);
assert!(
pairs
.iter()
.any(|(dk, _)| *dk == RedispatchDocumentKind::Stammdaten)
);
}
}