#![deny(unsafe_code)]
pub mod mako {
pub const PROCESS_INITIATED: &str = "de.mako.process.initiated";
pub const PROCESS_COMPLETED: &str = "de.mako.process.completed";
pub const PROCESS_FAILED: &str = "de.mako.process.failed";
pub const APERAK_ACCEPTED: &str = "de.mako.aperak.accepted";
pub const APERAK_REJECTED: &str = "de.mako.aperak.rejected";
pub const APERAK_TIMEOUT: &str = "de.mako.aperak.timeout";
pub const CONTRL_RECEIVED: &str = "de.mako.contrl.received";
pub const MALO_IDENTIFIED: &str = "de.mako.malo.identified";
pub const ABMELDEANFRAGE_BEANTWORTET: &str = "de.mako.abmeldeanfrage.beantwortet";
pub const EDIFACT_OUTBOUND: &str = "de.mako.edifact.outbound";
pub const NETZZUGANG_UEBERMITTLUNGSBEDARF: &str = "de.mako.netzzugang.uebermittlungsbedarf";
}
pub mod markt {
pub const MALO_UPDATED: &str = "de.markt.malo.updated";
pub const MALO_STAMMDATEN_GEAENDERT: &str = "de.markt.malo.stammdaten-geaendert";
pub const STAMMDATEN_GEAENDERT: &str = "de.markt.stammdaten.geaendert";
pub const MELO_UPDATED: &str = "de.markt.melo.updated";
pub const PARTNER_UPDATED: &str = "de.markt.partner.updated";
pub const NB_CONTRACT_UPDATED: &str = "de.markt.nb-contract.updated";
pub const MSB_RAHMENVERTRAG_GAS_UPDATED: &str = "de.markt.msb-rahmenvertrag-gas.updated";
pub const GERAET_KONFIGURATION_UPDATED: &str = "de.markt.geraet.konfiguration.updated";
pub const SR_KONFIGURATIONSPRODUKT_UPDATED: &str = "de.markt.sr.konfigurationsprodukt.updated";
pub const NETZZUGANG_ANTRAG_UPDATED: &str = "de.markt.netzzugang.antrag.updated";
pub const EINWILLIGUNG_ERTEILT: &str = "de.markt.einwilligung.erteilt";
pub const EINWILLIGUNG_WIDERRUFEN: &str = "de.markt.einwilligung.widerrufen";
pub const VERSORGUNG_CHANGED: &str = "de.markt.versorgung.changed";
pub const VERSORGUNG_BELIEFERT: &str = "de.markt.versorgung.beliefert";
pub const VERSORGUNG_GAP_DETECTED: &str = "de.markt.versorgung.gap-detected";
pub const VERSORGUNG_EOG_BEGONNEN: &str = "de.markt.versorgung.eog-begonnen";
pub const VERSORGUNG_ERSATZ_AUSLAUFEND: &str = "de.markt.versorgung.ersatz-auslaufend";
pub const PRICAT_PUBLISHED: &str = "de.markt.pricat.published";
pub const MMMA_IMPORT_SUCCESS: &str = "de.markt.mmma.import.success";
pub const MMMA_IMPORT_FAILED: &str = "de.markt.mmma.import.failed";
pub const SUBSCRIPTION_TEST: &str = "de.markt.subscription.test";
}
pub mod billing {
pub const RECHNUNG_ERSTELLT: &str = "de.billing.rechnung.erstellt";
pub const ABRECHNUNGSINFORMATION_MONATLICH: &str =
"de.billing.abrechnungsinformation.monatlich";
pub const XRECHNUNG_B2G_READY: &str = "de.billing.xrechnung.b2g.ready";
}
pub mod invoic {
pub const RECEIPT_DISPUTED: &str = "de.invoic.receipt.disputed";
pub const RECEIPT_DISPATCHED: &str = "de.invoic.receipt.dispatched";
pub const RECEIPT_SETTLED: &str = "de.invoic.receipt.settled";
pub const PAYMENT_OVERDUE: &str = "de.invoic.payment.overdue";
}
pub mod eeg {
pub const VERGUETUNG_BERECHNET: &str = "de.eeg.verguetung.berechnet";
pub const MARKTPRAEMIE_BERECHNET: &str = "de.eeg.marktpraemie.berechnet";
pub const SETTLEMENT_BATCH_DUE: &str = "de.eeg.settlement.batch-due";
pub const ANLAGE_FOERDERUNG_AUSLAUFEND: &str = "de.eeg.anlage.foerderung-auslaufend";
pub const ANLAGE_MASTR_REGISTRIERT: &str = "de.eeg.anlage.mastr-registriert";
pub const VERAEUSSERUNGSFORM_GEWECHSELT: &str = "de.eeg.veraeusserungsform.gewechselt";
}
pub mod accounting {
pub const MAHNUNG_ISSUED: &str = "de.accounting.mahnung.issued";
pub const ABSCHLAG_POSTED: &str = "de.accounting.abschlag.posted";
pub const PAYMENT_DUE: &str = "de.accounting.payment.due";
pub const PAYMENT_IMPORTED: &str = "de.accounting.payment.imported";
pub const ERSTATTUNG_FAELLIG: &str = "de.accounting.erstattung.faellig";
pub const JAHRESABSCHLUSS_ABGESCHLOSSEN: &str = "de.accounting.jahresabschluss.abgeschlossen";
pub const INTEREST_CHARGED: &str = "de.accounting.interest.charged";
pub const EEG_PAYOUT_REJECTED: &str = "de.accounting.eeg.payout.rejected";
pub const SPERRANDROHUNG: &str = "de.accounting.sperrandrohung";
pub const SPERRANKUENDIGUNG: &str = "de.accounting.sperrankuendigung";
pub const SPERRAUFTRAG: &str = "de.accounting.sperrauftrag";
pub const ENTSPERRAUFTRAG: &str = "de.accounting.entsperrauftrag";
pub const ABWENDUNG_ANGEBOTEN: &str = "de.accounting.abwendung.angeboten";
pub const ABWENDUNG_GEBROCHEN: &str = "de.accounting.abwendung.gebrochen";
pub const BANKRUECKLAST: &str = "de.accounting.bankruecklast";
pub const SEPA_COLLECTION_REJECTED: &str = "de.accounting.sepa.collection-rejected";
pub const SEPA_REVERSAL_ISSUED: &str = "de.accounting.sepa.reversal-issued";
pub const SEPA_RECALL_REQUESTED: &str = "de.accounting.sepa.recall-requested";
pub const SEPA_RECALL_RESOLVED: &str = "de.accounting.sepa.recall-resolved";
pub const PAYEE_VERIFICATION_MISMATCH: &str = "de.accounting.payee.verification-mismatch";
}
pub mod netzbilanz {
pub const INVOIC_DRAFTED: &str = "de.netzbilanz.invoic.drafted";
pub const INVOIC_DISPATCHED: &str = "de.netzbilanz.invoic.dispatched";
pub const INVOIC_DISPATCH_OVERDUE: &str = "de.netzbilanz.invoic.dispatch-overdue";
pub const INVOIC_PAID: &str = "de.netzbilanz.invoic.paid";
pub const INVOIC_DISPUTED: &str = "de.netzbilanz.invoic.disputed";
pub const KOSTENBLATT_COMPUTED: &str = "de.netzbilanz.kostenblatt.computed";
pub const KOSTENBLATT_DEADLINE_APPROACHING: &str =
"de.netzbilanz.kostenblatt.deadline-approaching";
}
pub mod messwert {
pub const READING_QUALITY_WARNING: &str = "de.messwert.reading.quality.warning";
pub const READING_DIRECT_STORED: &str = "de.messwert.reading.direct.stored";
pub const READING_ORDER_FAILED: &str = "de.messwert.reading.order.failed";
pub const READING_CONFIRMATION_OVERDUE: &str = "de.messwert.reading.confirmation.overdue";
pub const READING_DELIVERY_OVERDUE: &str = "de.messwert.reading.delivery.overdue";
pub const READING_DELIVERY_RESUMED: &str = "de.messwert.reading.delivery.resumed";
pub const CLS_COMPLIANCE_ISSUE: &str = "de.messwert.cls.compliance-issue";
pub const CLS_COMPLIANCE_RESOLVED: &str = "de.messwert.cls.compliance-resolved";
pub const ESA_TYP2_DELIVERY_OVERDUE: &str = "de.messwert.esa.typ2.delivery.overdue";
pub const ESA_TYP2_DELIVERY_RESUMED: &str = "de.messwert.esa.typ2.delivery.resumed";
pub const SMGW_CERT_EXPIRY_WARNING: &str = "de.messwert.smgw.cert.expiry-warning";
}
pub mod tarif {
pub const PRODUCT_UPDATED: &str = "de.tarif.product.updated";
pub const ANGEBOT_ANGENOMMEN: &str = "de.tarif.angebot.angenommen";
pub const ANGEBOT_ABGELAUFEN: &str = "de.tarif.angebot.abgelaufen";
pub const EPEX_MISSING: &str = "de.tarif.epex.missing";
}
pub mod vertrag {
pub const AKTIV: &str = "de.vertrag.aktiv";
pub const GEKUENDIGT: &str = "de.vertrag.gekuendigt";
pub const KUENDIGUNG: &str = "de.vertrag.kuendigung";
pub const KUENDIGUNG_WIDERRUFEN: &str = "de.vertrag.kuendigung-widerrufen";
pub const TARIFWECHSEL: &str = "de.vertrag.tarifwechsel";
pub const TARIFWECHSEL_GEPLANT: &str = "de.vertrag.tarifwechsel-geplant";
pub const PREISGARANTIE_HINTERLEGT: &str = "de.vertrag.preisgarantie-hinterlegt";
pub const PREISAENDERUNG_ANKUENDIGUNG: &str = "de.vertrag.preisaenderung.ankuendigung";
pub const AUTOERNEUERUNG_ANKUENDIGUNG: &str = "de.vertrag.autoerneuerung.ankuendigung";
pub const ABLAUF_ANKUENDIGUNG: &str = "de.vertrag.ablauf.ankuendigung";
pub const ABGESCHLOSSEN: &str = "de.vertrag.abgeschlossen";
}
pub mod vpp {
pub const DISPATCH_CONFIRMED: &str = "de.vpp.dispatch.confirmed";
pub const SETTLEMENT_BERECHNET: &str = "de.vpp.settlement.berechnet";
}
pub mod agent {
pub const DECISION_MADE: &str = "de.agent.decision.made";
}
pub mod gabi {
pub const MEASUREMENT_RECEIVED: &str = "de.gabi.measurement.received";
pub const ALLOCATION_COMPLETED: &str = "de.gabi.allocation.completed";
pub const NOMINATION_CREATED: &str = "de.gabi.nomination.created";
pub const NOMINATION_CONFIRMED: &str = "de.gabi.nomination.confirmed";
pub const NOMINATION_CURTAILED: &str = "de.gabi.nomination.curtailed";
pub const NOMINATION_REJECTED: &str = "de.gabi.nomination.rejected";
pub const NOMRES_MISSING: &str = "de.gabi.nomres.missing";
pub const IMBALANCE_CALCULATED: &str = "de.gabi.imbalance.calculated";
pub const CORRECTION_CREATED: &str = "de.gabi.correction.created";
pub const INVOIC_MMM_RECEIVED: &str = "de.gabi.invoic.mmm.received";
pub const INVOIC_KAPAZITAET_RECEIVED: &str = "de.gabi.invoic.kapazitaet.received";
pub const ALOCAT_MISSING: &str = "de.gabi.alocat.missing";
pub const GAS_QUALITY_VIOLATION: &str = "de.gabi.quality.violation";
pub const FINAL_ALOCAT_DEADLINE: &str = "de.gabi.alocat.final.deadline";
}
pub mod obs {
pub const STP_PARITY_ALERT: &str = "de.obs.stp.parity.alert";
pub const DEADLINE_APPROACHING: &str = "de.obs.deadline.approaching";
}
pub mod sperr {
pub const AUFTRAG_EINGEGANGEN: &str = "de.sperr.auftrag.eingegangen";
pub const AUSGEFUEHRT: &str = "de.sperr.ausgefuehrt";
pub const FEHLGESCHLAGEN: &str = "de.sperr.fehlgeschlagen";
pub const VERSUCH_GESCHEITERT: &str = "de.sperr.versuch.gescheitert";
pub const STORNIERT: &str = "de.sperr.storniert";
pub const AUSFUEHRUNG_UEBERFAELLIG: &str = "de.sperr.ausfuehrung.ueberfaellig";
pub const IFTSTA_AUSSTEHEND: &str = "de.sperr.iftsta.ausstehend";
}
pub mod mabis {
pub const SUBMISSION_FAILED: &str = "de.mabis.submission.failed";
pub const KORREKTURBEDARF_OPENED: &str = "de.mabis.korrekturbedarf.opened";
}
#[must_use]
pub fn all() -> &'static [&'static str] {
&[
mako::PROCESS_INITIATED,
mako::PROCESS_COMPLETED,
mako::PROCESS_FAILED,
mako::APERAK_ACCEPTED,
mako::APERAK_REJECTED,
mako::APERAK_TIMEOUT,
mako::CONTRL_RECEIVED,
mako::MALO_IDENTIFIED,
mako::ABMELDEANFRAGE_BEANTWORTET,
mako::EDIFACT_OUTBOUND,
mako::NETZZUGANG_UEBERMITTLUNGSBEDARF,
markt::MALO_UPDATED,
markt::MALO_STAMMDATEN_GEAENDERT,
markt::STAMMDATEN_GEAENDERT,
markt::MELO_UPDATED,
markt::PARTNER_UPDATED,
markt::NB_CONTRACT_UPDATED,
markt::MSB_RAHMENVERTRAG_GAS_UPDATED,
markt::GERAET_KONFIGURATION_UPDATED,
markt::SR_KONFIGURATIONSPRODUKT_UPDATED,
markt::NETZZUGANG_ANTRAG_UPDATED,
markt::EINWILLIGUNG_ERTEILT,
markt::EINWILLIGUNG_WIDERRUFEN,
markt::VERSORGUNG_CHANGED,
markt::VERSORGUNG_BELIEFERT,
markt::VERSORGUNG_GAP_DETECTED,
markt::VERSORGUNG_EOG_BEGONNEN,
markt::VERSORGUNG_ERSATZ_AUSLAUFEND,
markt::PRICAT_PUBLISHED,
markt::MMMA_IMPORT_SUCCESS,
markt::MMMA_IMPORT_FAILED,
markt::SUBSCRIPTION_TEST,
billing::RECHNUNG_ERSTELLT,
billing::ABRECHNUNGSINFORMATION_MONATLICH,
billing::XRECHNUNG_B2G_READY,
invoic::RECEIPT_DISPUTED,
invoic::RECEIPT_DISPATCHED,
invoic::RECEIPT_SETTLED,
invoic::PAYMENT_OVERDUE,
eeg::VERGUETUNG_BERECHNET,
eeg::MARKTPRAEMIE_BERECHNET,
eeg::SETTLEMENT_BATCH_DUE,
eeg::ANLAGE_FOERDERUNG_AUSLAUFEND,
eeg::ANLAGE_MASTR_REGISTRIERT,
eeg::VERAEUSSERUNGSFORM_GEWECHSELT,
accounting::MAHNUNG_ISSUED,
accounting::ABSCHLAG_POSTED,
accounting::PAYMENT_DUE,
accounting::PAYMENT_IMPORTED,
accounting::ERSTATTUNG_FAELLIG,
accounting::INTEREST_CHARGED,
accounting::EEG_PAYOUT_REJECTED,
accounting::JAHRESABSCHLUSS_ABGESCHLOSSEN,
accounting::SPERRANDROHUNG,
accounting::SPERRANKUENDIGUNG,
accounting::SPERRAUFTRAG,
accounting::ENTSPERRAUFTRAG,
accounting::ABWENDUNG_ANGEBOTEN,
accounting::ABWENDUNG_GEBROCHEN,
accounting::BANKRUECKLAST,
accounting::SEPA_COLLECTION_REJECTED,
accounting::SEPA_REVERSAL_ISSUED,
accounting::SEPA_RECALL_REQUESTED,
accounting::SEPA_RECALL_RESOLVED,
accounting::PAYEE_VERIFICATION_MISMATCH,
netzbilanz::INVOIC_DRAFTED,
netzbilanz::INVOIC_DISPATCHED,
netzbilanz::INVOIC_DISPATCH_OVERDUE,
netzbilanz::INVOIC_PAID,
netzbilanz::INVOIC_DISPUTED,
netzbilanz::KOSTENBLATT_COMPUTED,
netzbilanz::KOSTENBLATT_DEADLINE_APPROACHING,
messwert::READING_QUALITY_WARNING,
messwert::READING_DIRECT_STORED,
messwert::READING_ORDER_FAILED,
messwert::READING_CONFIRMATION_OVERDUE,
messwert::READING_DELIVERY_OVERDUE,
messwert::READING_DELIVERY_RESUMED,
messwert::ESA_TYP2_DELIVERY_OVERDUE,
messwert::ESA_TYP2_DELIVERY_RESUMED,
messwert::CLS_COMPLIANCE_ISSUE,
messwert::CLS_COMPLIANCE_RESOLVED,
messwert::SMGW_CERT_EXPIRY_WARNING,
tarif::PRODUCT_UPDATED,
tarif::ANGEBOT_ANGENOMMEN,
tarif::ANGEBOT_ABGELAUFEN,
tarif::EPEX_MISSING,
vertrag::ABGESCHLOSSEN,
vertrag::AKTIV,
vertrag::GEKUENDIGT,
vertrag::KUENDIGUNG,
vertrag::KUENDIGUNG_WIDERRUFEN,
vertrag::TARIFWECHSEL,
vertrag::TARIFWECHSEL_GEPLANT,
vertrag::PREISGARANTIE_HINTERLEGT,
vertrag::PREISAENDERUNG_ANKUENDIGUNG,
vertrag::AUTOERNEUERUNG_ANKUENDIGUNG,
vertrag::ABLAUF_ANKUENDIGUNG,
vpp::DISPATCH_CONFIRMED,
vpp::SETTLEMENT_BERECHNET,
agent::DECISION_MADE,
gabi::MEASUREMENT_RECEIVED,
gabi::ALLOCATION_COMPLETED,
gabi::NOMINATION_CREATED,
gabi::NOMINATION_CURTAILED,
gabi::NOMINATION_REJECTED,
gabi::NOMRES_MISSING,
gabi::NOMINATION_CONFIRMED,
gabi::IMBALANCE_CALCULATED,
gabi::CORRECTION_CREATED,
gabi::INVOIC_MMM_RECEIVED,
gabi::INVOIC_KAPAZITAET_RECEIVED,
gabi::ALOCAT_MISSING,
gabi::GAS_QUALITY_VIOLATION,
gabi::FINAL_ALOCAT_DEADLINE,
obs::STP_PARITY_ALERT,
obs::DEADLINE_APPROACHING,
sperr::AUFTRAG_EINGEGANGEN,
sperr::VERSUCH_GESCHEITERT,
sperr::AUSFUEHRUNG_UEBERFAELLIG,
sperr::AUSGEFUEHRT,
sperr::FEHLGESCHLAGEN,
sperr::STORNIERT,
sperr::IFTSTA_AUSSTEHEND,
mabis::SUBMISSION_FAILED,
mabis::KORREKTURBEDARF_OPENED,
]
}
#[must_use]
pub fn matches(pattern: &str, event_type: &str) -> bool {
if pattern == "*" {
return true;
}
let p: Vec<char> = pattern.chars().collect();
let v: Vec<char> = event_type.chars().collect();
let mut pi = 0usize;
let mut vi = 0usize;
let mut star_pi: Option<usize> = None;
let mut star_vi = 0usize;
while vi < v.len() {
if pi < p.len() && (p[pi] == '?' || p[pi] == v[vi]) {
pi += 1;
vi += 1;
} else if pi < p.len() && p[pi] == '*' {
star_pi = Some(pi);
star_vi = vi;
pi += 1;
} else if let Some(sp) = star_pi {
pi = sp + 1;
star_vi += 1;
vi = star_vi;
} else {
return false;
}
}
while pi < p.len() && p[pi] == '*' {
pi += 1;
}
pi == p.len()
}
#[cfg(test)]
mod matcher_tests {
use super::matches;
#[test]
fn exact_and_bare_star() {
assert!(matches(
super::mako::PROCESS_INITIATED,
super::mako::PROCESS_INITIATED
));
assert!(!matches(
super::mako::PROCESS_INITIATED,
super::mako::PROCESS_COMPLETED
));
assert!(matches("*", "de.anything.at.all"));
}
#[test]
fn trailing_star_is_prefix_match() {
assert!(matches("de.mako.*", "de.mako.process.initiated"));
assert!(matches("de.markt.*", "de.markt.malo.updated"));
assert!(!matches("de.mako.*", "de.markt.malo.updated"));
assert!(matches("de.mako.process.*", "de.mako.process.failed"));
}
#[test]
fn mid_glob_and_question_mark() {
assert!(matches(
"de.*.rechnung.erstellt",
"de.billing.rechnung.erstellt"
));
assert!(matches(
"de.e?g.verguetung.berechnet",
"de.eeg.verguetung.berechnet"
));
assert!(!matches(
"de.e?g.verguetung.berechnet",
"de.eeeg.verguetung.berechnet"
));
}
#[test]
fn empty_pattern_matches_only_empty() {
assert!(matches("", ""));
assert!(!matches("", "de.x"));
}
}
#[cfg(test)]
mod tests {
use super::all;
#[test]
fn every_type_starts_with_de_prefix() {
for ty in all() {
assert!(ty.starts_with("de."), "{ty} must start with `de.`");
}
}
#[test]
fn every_declared_constant_is_in_the_catalogue() {
let src = include_str!("lib.rs");
let catalog = src.split("#[cfg(test)]").next().expect("catalog section");
let declared: Vec<&str> = catalog
.lines()
.filter_map(|line| {
let rest = line.trim().strip_prefix("pub const ")?;
let (_, value) = rest.split_once("= \"")?;
value.split('"').next()
})
.filter(|v| v.starts_with("de."))
.collect();
assert!(
declared.len() > 80,
"parsed only {} constants — the parser broke, not the catalog",
declared.len()
);
let missing: Vec<&&str> = declared.iter().filter(|v| !all().contains(v)).collect();
assert!(
missing.is_empty(),
"these event types are declared and absent from `all()`, so nothing can \
subscribe to them and no inventory counts them: {missing:#?}"
);
}
#[test]
fn unreferenced_constants_are_marked_phantom() {
let src = include_str!("lib.rs");
let catalog = src.split("#[cfg(test)]").next().expect("catalog section");
let lines: Vec<&str> = catalog.lines().collect();
let mut entries: Vec<Entry> = Vec::new();
let mut module = String::new();
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("pub mod ") {
module = rest.trim_end_matches(" {").trim().to_owned();
continue;
}
let Some(rest) = trimmed.strip_prefix("pub const ") else {
continue;
};
let Some(name) = rest.split(':').next() else {
continue;
};
let Some(value) = rest
.split_once("= \"")
.and_then(|(_, v)| v.split('"').next())
else {
continue;
};
let mut phantom = false;
for prev in lines[..i].iter().rev() {
let t = prev.trim();
if t.starts_with("///") {
if t.contains("⚠ phantom:") {
phantom = true;
}
} else if !t.is_empty() {
break;
}
}
entries.push(Entry {
module: module.clone(),
name: name.trim().to_owned(),
value: value.to_owned(),
phantom,
});
}
assert!(
entries.len() > 80,
"parsed only {} constants — the parser broke, not the catalog",
entries.len()
);
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(std::path::Path::parent)
.expect("workspace root");
let mut blob = String::new();
for dir in ["crates", "services"] {
collect_rs(&root.join(dir), &mut blob);
}
let unmarked: Vec<&str> = entries
.iter()
.filter(|e| !e.phantom && !e.is_referenced(&blob, &entries))
.map(|e| e.name.as_str())
.collect();
assert!(
unmarked.is_empty(),
"these constants are referenced nowhere outside `mako-events` but carry no \
`⚠ phantom:` marker:\n {unmarked:?}\n\
Either wire an emitter, or document the gap with `⚠ phantom:` so the \
catalog does not imply the event exists."
);
}
struct Entry {
module: String,
name: String,
value: String,
phantom: bool,
}
impl Entry {
fn is_referenced(&self, blob: &str, all: &[Entry]) -> bool {
if blob.contains(&self.value)
|| blob.contains(&format!("{}::{}", self.module, self.name))
{
return true;
}
let unique = all.iter().filter(|e| e.name == self.name).count() == 1;
unique && blob.contains(&self.name)
}
}
fn collect_rs(dir: &std::path::Path, out: &mut String) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if path.file_name().is_some_and(|n| n == "mako-events") {
continue;
}
collect_rs(&path, out);
} else if path.extension().is_some_and(|e| e == "rs")
&& let Ok(s) = std::fs::read_to_string(&path)
{
out.push_str(&s);
}
}
}
#[test]
fn german_namespaces_use_german_participles() {
const GERMAN_NAMESPACES: &[&str] = &["vertrag"];
const ENGLISH_PARTICIPLES: &[&str] = &[
"updated",
"changed",
"created",
"deleted",
"stored",
"replaced",
"cancelled",
"canceled",
"renewed",
"expired",
"planned",
];
for ty in all() {
let Some(ns) = ty.split('.').nth(1) else {
continue;
};
if !GERMAN_NAMESPACES.contains(&ns) {
continue;
}
for bad in ENGLISH_PARTICIPLES {
assert!(
!ty.ends_with(&format!("-{bad}")) && !ty.ends_with(&format!(".{bad}")),
"{ty}: `de.{ns}.*` names its facts in German — use the German \
participle instead of {bad:?} (e.g. `preisgarantie-hinterlegt`, \
not `preisgarantie-updated`)"
);
}
}
}
#[test]
fn segments_use_hyphen_not_underscore() {
for ty in all() {
assert!(
!ty.contains('_'),
"{ty} must join multi-word segments with `-`, not `_`"
);
for segment in ty.split('.') {
assert!(
!segment.is_empty(),
"{ty} must not contain an empty segment"
);
assert!(
!segment.starts_with('-') && !segment.ends_with('-'),
"{ty}: segment {segment:?} must not start or end with `-`"
);
assert!(
segment
.bytes()
.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-'),
"{ty}: segment {segment:?} must match [a-z0-9-]+"
);
}
}
}
#[test]
fn no_two_types_normalise_to_the_same_name() {
let mut seen: std::collections::HashMap<String, &str> = std::collections::HashMap::new();
for ty in all() {
let key = ty.replace(['-', '_'], "").to_lowercase();
if let Some(prev) = seen.insert(key, ty) {
assert_eq!(prev, *ty, "{prev} and {ty} collide after normalisation");
}
}
}
#[test]
fn every_type_is_lowercase() {
for ty in all() {
assert_eq!(
*ty,
ty.to_lowercase(),
"{ty} must be entirely lowercase (CloudEvents type convention)"
);
}
}
#[test]
fn every_type_has_valid_segments() {
for ty in all() {
assert!(
!ty.contains(' ') && !ty.contains('*'),
"{ty} must be a concrete type — no whitespace, no globs"
);
for segment in ty.split('.') {
assert!(!segment.is_empty(), "{ty} has an empty `.` segment");
assert!(
segment
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || "-_".contains(c)),
"{ty}: segment `{segment}` has characters outside [a-z0-9_-]"
);
}
}
}
#[test]
fn no_duplicates() {
let types = all();
let mut seen = std::collections::BTreeSet::new();
for ty in types {
assert!(seen.insert(*ty), "duplicate catalog entry: {ty}");
}
}
#[test]
fn legacy_prefixes_are_gone() {
for ty in all() {
assert!(
!ty.starts_with("de.edmd.") && !ty.starts_with("de.tarifbd."),
"{ty} uses a retired service-name prefix (use de.messwert / de.tarif)"
);
assert_ne!(
*ty, "de.angebot.angenommen",
"moved to de.tarif.angebot.angenommen"
);
}
}
}