Rust enums and match for SNS filter policy shapes — Protocol, FilterScope, SubState
Parse strings at the SDK edge. Match on variants everywhere else.
<!-- 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:
- HTTPS + pending →
PendingConfirmation/pending - SQS + FilterPolicy on attributes →
ConfirmedFiltered/filter_scope=MessageAttributes - Lambda +
{}→ConfirmedOpen/no_filter - SQS + MessageBody scope →
ConfirmedFiltered/filter_scope=MessageBody
§V. Tests that pin the seams
Five #[test] functions:
| Test | Seam it pins |
|---|---|
protocol_parse_and_confirm_gate | Wire strings and which protocols need confirm |
filter_policy_shapes | None / {} / multi-key Present |
classify_pending_beats_filter | Pending wins even when a filter exists |
classify_body_scope_filtered | MessageBody scope survives into SubState |
protocol_census_counts | Two SQS + one Lambda tally |
Run: cargo test --offline inside pkg/sns-filter-shapes. Expect 5 passed.
§VI. What this is not
- Not a TRPL ch20 reteach. No
unsafe, no procedural macros. - Not an aws-sdk-sns census redo. Ops owns ListTopics.
- Not the 10-03 image-ref match-guard classifier. Different referent, different enums.
- 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
- Ops: SNS subscription census (same trio)
- Cert: SAP SNS fan-out (same trio)
- Duha prior: TRPL ch19 Patterns
- Prior Dhuhr Dev: match guards on image refs