liminal-protocol 0.14.1

Shared participant-lifecycle protocol types for liminal
Documentation
use super::tags;
use alloc::{
    boxed::Box,
    collections::BTreeMap,
    string::{String, ToString},
};

const FIXTURE: &str = include_str!("../../../../docs/wire/registry.json");
const SOURCE: &str = include_str!("tags.rs");
type Rows = BTreeMap<(String, String), u16>;

macro_rules! pin_registry {
    ($fixture:ident, $name:ident) => {
        let entries: BTreeMap<_, _> = $fixture
            .iter()
            .filter(|((r, _), _)| r == stringify!($name))
            .map(|((_, name), value)| (*value, name))
            .collect();
        for value in 0..=u16::MAX {
            let row = entries.get(&value);
            match (tags::$name::try_from(value), row) {
                (Ok(tag), Some(name)) => {
                    assert_eq!(tag.wire_value(), value);
                    assert_eq!(alloc::format!("{tag:?}"), **name);
                }
                (Err(_), None) => {}
                _ => {
                    return Err(alloc::format!(
                        "{} value {value} differs from the export",
                        stringify!($name)
                    )
                    .into());
                }
            }
        }
    };
}

#[test]
fn committed_registry_equals_every_rust_registry() -> Result<(), Box<dyn core::error::Error>> {
    let source = source_rows()?;
    let fixture = fixture_rows();
    assert_eq!(fixture, source, "registry export is incomplete or drifted");
    pin_registry!(fixture, ClientDiscriminant);
    pin_registry!(fixture, ServerDiscriminant);
    pin_registry!(fixture, RecordAdmissionFaultClassTag);
    pin_registry!(fixture, PushDiscriminant);
    pin_registry!(fixture, RecordKind);
    pin_registry!(fixture, CloseCauseTag);
    pin_registry!(fixture, TransportReasonTag);
    pin_registry!(fixture, DecodeClass);
    pin_registry!(fixture, AttemptOperation);
    pin_registry!(fixture, AttemptConflict);
    pin_registry!(fixture, DetachAuthorityStateTag);
    pin_registry!(fixture, LeaveAuthorityStateTag);
    pin_registry!(fixture, BindingStateTag);
    pin_registry!(fixture, ReceiptExpiryReason);
    pin_registry!(fixture, ReceiptCapacityScope);
    pin_registry!(fixture, IdentityCapacityScope);
    pin_registry!(fixture, ClosureScope);
    pin_registry!(fixture, ResourceDimensionTag);
    pin_registry!(fixture, RepaymentEdgeTag);
    pin_registry!(fixture, MarkerNotDeliveredReason);
    pin_registry!(fixture, MarkerMismatchReason);
    pin_registry!(fixture, AckGapReason);
    pin_registry!(fixture, AckRegressionReason);
    pin_registry!(fixture, InvalidObserverEpochReason);
    pin_registry!(fixture, InvalidObserverEpochListReason);
    pin_registry!(fixture, Counter);
    Ok(())
}

fn source_rows() -> Result<Rows, Box<dyn core::error::Error>> {
    let mut source = Rows::new();
    let mut registry = "";
    for line in SOURCE.lines().map(str::trim) {
        if let Some(name) = line.strip_suffix(" {")
            && name.chars().all(char::is_alphanumeric)
        {
            registry = name;
        }
        if let Some((name, value)) = line.split_once(" = ")
            && !registry.is_empty()
            && name.chars().all(char::is_alphanumeric)
        {
            let value = value.trim_end_matches(',');
            let value = if let Some(hex) = value.strip_prefix("0x") {
                u16::from_str_radix(hex, 16)?
            } else {
                value.parse()?
            };
            assert!(
                source
                    .insert((registry.to_string(), name.to_string()), value)
                    .is_none()
            );
        }
    }
    Ok(source)
}

fn fixture_rows() -> Rows {
    let mut fixture = Rows::new();
    let mut registry = "";
    for line in FIXTURE.lines().map(str::trim) {
        if let Some(name) = line.strip_suffix("\": {").and_then(|s| s.strip_prefix('"')) {
            registry = name;
        }
        if let Some((name, value)) = line
            .split_once("\": ")
            .and_then(|(n, v)| n.strip_prefix('"').map(|n| (n, v)))
            && let Ok(number) = value.trim_end_matches(',').parse::<u16>()
            && registry != "registries"
            && !registry.is_empty()
        {
            assert!(
                fixture
                    .insert((registry.to_string(), name.to_string()), number)
                    .is_none()
            );
        }
    }
    fixture
}