Hedronite · Dev Lesson · Polyglot-Dev / Rust · Sun 2026-10-04

Rust enums and match for SNS filter policy shapes — Protocol, FilterScope, SubState

Parse strings at the SDK edge. Match on variants everywhere else.

Lesson Class: Dev (Rust enums · match · SNS shapes)
Topic: T1 Systems core
Lag rule: Through ch20 ceiling; uses ch06/ch19 only; no Unsafe reteach
Crate: pkg/sns-filter-shapes · std only · cargo test 5
Paired Ops: SNS subscription PendingConfirmation census
Paired Cert: AWS SAP SNS fan-out filter policies
The Pending Match
pending wins before filter arms.
Four enums
Protocol · FilterScope · FilterPolicy · SubState
Offline proof
cargo test --offline · five seams
A "true" string is not a state machine.

<!-- hal:authoritative:yaml -->

*Stringly typed attribute maps stay at the SDK boundary. Inside the crate, PendingConfirmation is a variant, not a "true" you keep re-parsing.*

§I. Frame

Duha shipped Unsafe and macros this morning (Topics #23 / TRPL ch20). That raises the lag ceiling; it does not force today's Dev into unsafe or macro_rules!. Patterns (ch19) and enums/match (ch06) already cover the spine this Ops surface needs.

Ops today lists SNS subscriptions with aws-sdk-sns. The interesting columns are protocol, pending confirmation, FilterPolicy JSON, and FilterPolicyScope. Those four fields want named variants before they want another HashMap lookup.

The crate sns-filter-shapes sits under pkg/. It has no dependencies. It does not call AWS.

§II. Four enums

pub enum Protocol {
    Http, Https, Email, EmailJson, Sms,
    Sqs, Application, Lambda, Firehose,
    Unknown(String),
}

pub enum FilterScope {
    MessageAttributes,
    MessageBody,
}

pub enum FilterPolicy {
    Absent,
    EmptyObject,
    Present { keys: Vec<String> },
}

pub enum SubState {
    PendingConfirmation,
    ConfirmedOpen,
    ConfirmedFiltered { scope: FilterScope, keys: Vec<String> },
}

Protocol::parse lowercases the wire string once. needs_endpoint_confirm is a matches! on HTTP(S), email, email-json, and SMS: the protocols that still require an endpoint owner to ConfirmSubscription.

FilterPolicy::from_json_attr turns the GetSubscriptionAttributes string into Absent / EmptyObject / Present. Present keeps only the top-level JSON keys. That is enough for a census flag line; it is not a JSON library.

§III. The Pending Match

The Pending Match (named technique). Classify with a single match on (pending, &filter_policy) so pending always wins.

pub fn classify(&self) -> SubState {
    match (self.pending, &self.filter_policy) {
        (true, _) => SubState::PendingConfirmation,
        (false, FilterPolicy::Present { keys }) => SubState::ConfirmedFiltered {
            scope: self.filter_scope.clone(),
            keys: keys.clone(),
        },
        (false, FilterPolicy::Absent | FilterPolicy::EmptyObject) => {
            SubState::ConfirmedOpen
        }
    }
}

Or-patterns collapse Absent and EmptyObject into ConfirmedOpen. A pending row that already has a FilterPolicy still classifies as PendingConfirmation. That matches the Ops rule: do not trust filter behavior until the subscription is confirmed.

flag_line then turns SubState into the same flag vocabulary the cargo script prints (pending, no_filter, filter_scope=MessageBody, …).

§IV. Protocol census without the SDK

protocol_census walks a slice of SubscriptionView and returns a BTreeMap<String, usize>. The key is the wire protocol string. The map is ordered so golden tests stay stable.

cargo run --offline --bin demo prints four rows:

  1. HTTPS + pending → PendingConfirmation / pending
  2. SQS + FilterPolicy on attributes → ConfirmedFiltered / filter_scope=MessageAttributes
  3. Lambda + {} → ConfirmedOpen / no_filter
  4. SQS + MessageBody scope → ConfirmedFiltered / filter_scope=MessageBody

§V. Tests that pin the seams

Five #[test] functions:

TestSeam it pins
protocol_parse_and_confirm_gateWire strings and which protocols need confirm
filter_policy_shapesNone / {} / multi-key Present
classify_pending_beats_filterPending wins even when a filter exists
classify_body_scope_filteredMessageBody scope survives into SubState
protocol_census_countsTwo SQS + one Lambda tally

Run: cargo test --offline inside pkg/sns-filter-shapes. Expect 5 passed.

§VI. What this is not

  1. Not a TRPL ch20 reteach. No unsafe, no procedural macros.
  2. Not an aws-sdk-sns census redo. Ops owns ListTopics.
  3. Not the 10-03 image-ref match-guard classifier. Different referent, different enums.
  4. Not Final project (#24). No multi-crate workspace beyond this proof.

§VII. Close instruction

Keep Protocol, FilterScope, FilterPolicy, and SubState as the boundary types if you later wrap the Ops script. Parse strings at the edge; match on variants everywhere else. When a new SNS protocol appears, add one enum arm and one parse arm, then extend the confirm gate only if that protocol requires ConfirmSubscription.

Related