haematite 0.6.0

Content-addressed, branchable, actor-native storage engine
Documentation
use super::handle::ShardCommandKind;

const EXPECTED_VARIANTS: [&str; 22] = [
    "Get",
    "GetRaw",
    "Put",
    "Delete",
    "DeleteIfExpired",
    "Commit",
    "Range",
    "HasLiveInRange",
    "Append",
    "ReadValue",
    "Cas",
    "ApplyDurable",
    "ApplyDurableTombstone",
    "ApplyDurableBatch",
    "RecordPromise",
    "RecordOwnerEpoch",
    "ReserveMinted",
    "ReadPromiseState",
    "ExportReachable",
    "MergeAdopt",
    "ScanSequences",
    "Shutdown",
];

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Disposition {
    ReadOnly,
    PutExpiring,
    PutNonExpiring,
    Remove,
    PhysicalRecheck,
    AppendExpiring,
    AppendNonExpiring,
    RawOverwrite,
    DurableExpiring,
    DurableNonExpiring,
    DurableTombstone,
    DurableBatch,
    Metadata,
    BulkRebuild,
    Invalidate,
}

fn classify(kind: &ShardCommandKind) -> Disposition {
    match kind {
        ShardCommandKind::Get { .. }
        | ShardCommandKind::GetRaw { .. }
        | ShardCommandKind::Commit { .. }
        | ShardCommandKind::Range { .. }
        | ShardCommandKind::HasLiveInRange(..)
        | ShardCommandKind::ReadValue { .. }
        | ShardCommandKind::ReadPromiseState { .. }
        | ShardCommandKind::ExportReachable { .. }
        | ShardCommandKind::ScanSequences { .. } => Disposition::ReadOnly,
        ShardCommandKind::Put { ttl: Some(_), .. } => Disposition::PutExpiring,
        ShardCommandKind::Put { ttl: None, .. } => Disposition::PutNonExpiring,
        ShardCommandKind::Delete { .. } => Disposition::Remove,
        ShardCommandKind::DeleteIfExpired { .. } => Disposition::PhysicalRecheck,
        ShardCommandKind::Append { ttl: Some(_), .. } => Disposition::AppendExpiring,
        ShardCommandKind::Append { ttl: None, .. } => Disposition::AppendNonExpiring,
        ShardCommandKind::Cas { .. } => Disposition::RawOverwrite,
        ShardCommandKind::ApplyDurable { ttl: Some(_), .. } => Disposition::DurableExpiring,
        ShardCommandKind::ApplyDurable { ttl: None, .. } => Disposition::DurableNonExpiring,
        ShardCommandKind::ApplyDurableTombstone { .. } => Disposition::DurableTombstone,
        ShardCommandKind::ApplyDurableBatch { items, .. } => {
            let _contains_expiring = items.iter().any(|item| item.3.is_some());
            let _contains_non_expiring = items.iter().any(|item| item.3.is_none());
            Disposition::DurableBatch
        }
        ShardCommandKind::RecordPromise { .. }
        | ShardCommandKind::RecordOwnerEpoch { .. }
        | ShardCommandKind::ReserveMinted { .. } => Disposition::Metadata,
        ShardCommandKind::MergeAdopt { .. } => Disposition::BulkRebuild,
        ShardCommandKind::Shutdown { .. } => Disposition::Invalidate,
    }
}

fn declared_variants() -> Vec<&'static str> {
    let source = include_str!("handle.rs");
    let mut variants = Vec::new();
    let mut depth = 0_i32;
    let mut in_enum = false;
    for line in source.lines() {
        if !in_enum {
            if line.contains("enum ShardCommandKind {") {
                in_enum = true;
                depth = 1;
            }
            continue;
        }
        let trimmed = line.trim();
        if depth == 1
            && let Some(first) = trimmed.as_bytes().first()
            && first.is_ascii_uppercase()
        {
            let end = trimmed.find(['{', '(', ',']).unwrap_or(trimmed.len());
            variants.push(trimmed[..end].trim());
        }
        depth += i32::try_from(line.matches('{').count()).unwrap_or(i32::MAX);
        depth -= i32::try_from(line.matches('}').count()).unwrap_or(i32::MAX);
        if depth == 0 {
            break;
        }
    }
    variants
}

#[test]
fn shard_command_expiry_disposition_census_is_total() {
    let classifier: fn(&ShardCommandKind) -> Disposition = classify;
    let _ = classifier;
    assert_eq!(declared_variants(), EXPECTED_VARIANTS);
    assert_eq!(EXPECTED_VARIANTS.len(), 22);
}