#[derive(Default)]
pub enum WireFormat {
#[default]
UnderscoreSeparated,
DotSeparated,
Custom(Box<dyn WireNameTranslator + Send + Sync>),
}
impl WireFormat {
pub fn outbound(&self, internal: &str) -> String {
match self {
WireFormat::UnderscoreSeparated => DotToUnderscoreTranslator.outbound(internal),
WireFormat::DotSeparated => IdentityTranslator.outbound(internal),
WireFormat::Custom(t) => t.outbound(internal),
}
}
}
pub trait WireNameTranslator: Send + Sync {
fn outbound(&self, internal: &str) -> String;
}
pub struct DotToUnderscoreTranslator;
impl WireNameTranslator for DotToUnderscoreTranslator {
fn outbound(&self, internal: &str) -> String {
internal.replace('.', "_")
}
}
struct IdentityTranslator;
impl WireNameTranslator for IdentityTranslator {
fn outbound(&self, internal: &str) -> String {
internal.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn underscore_outbound_replaces_dots() {
let fmt = WireFormat::UnderscoreSeparated;
assert_eq!(fmt.outbound("jumperless.connect"), "jumperless_connect");
assert_eq!(
fmt.outbound("bench.jumperless.dac_set"),
"bench_jumperless_dac_set"
);
}
#[test]
fn dot_separated_is_identity() {
let fmt = WireFormat::DotSeparated;
assert_eq!(fmt.outbound("jumperless.connect"), "jumperless.connect");
}
#[test]
fn overlapping_prefix_kebab_produces_distinct_wire_names() {
let fmt = WireFormat::UnderscoreSeparated;
let wire_esp32 = fmt.outbound("esp32.p4_dac_set");
let wire_esp32_p4 = fmt.outbound("esp32-p4.dac_set");
assert_eq!(wire_esp32, "esp32_p4_dac_set");
assert_eq!(wire_esp32_p4, "esp32-p4_dac_set");
assert_ne!(wire_esp32, wire_esp32_p4);
}
}