#[non_exhaustive]pub enum ActionKind {
Show 14 variants
Pass,
Replace,
ReplacePayload,
Delay,
Hold,
DropElide,
Truncate,
ResetStream,
CloseSession,
Open,
Reject,
ReplaceObject,
OpenAfter,
SerializeAfter,
}Expand description
A capability, named independently of whether
Action can express it.
Every kind here names a capability some value can express. A variant that no value can carry is a unit that compiles and never runs, and a table row saying so is a row about this crate’s plans rather than about what it does — so the family of constructor-less capabilities is simply absent from this enum, and absence is what the table says by not listing it.
Self::ReplaceObject is deliberately not in that family, and the
distinction is what keeps the table honest: a value that carries it to
Site::Object exists (Action::Replace(b)), so the engine really is
asked and really does refuse, with Refusal::WrongSite. The
deferred-capability id printed on the variant names the gap; it is
not the refusal a run emits. See the variant’s own rustdoc.
Attempt mapping — how tests/action_matrix.rs turns a (site, kind) pair into something to run. Every kind maps to exactly one
expression, and a kind whose mapping does not typecheck at a site is
precisely a NotAttemptable cell:
| Kind | The attempt |
|---|---|
Pass | Action::Pass |
Replace | Action::Replace(b) |
ReplacePayload | Action::ReplacePayload(b), b.len() == payload_len |
ReplaceObject | Action::Replace(b) at Site::Object only — the same expression as Replace, so those two cells must agree, and action_matrix.rs asserts that they do. At every other site there is no attempt: the same expression there is Replace’s attempt, and this kind names a unit those sites do not carry (NotAttemptable::KindNotDefinedAtThisSite) |
Delay | Action::Pass.delayed(d) |
Hold | Action::Pass.held(gate) |
DropElide | Action::Drop(DropMode::Elide) |
Truncate | Action::Truncate { bytes, code } |
ResetStream | Action::ResetStream { code } |
CloseSession | Action::CloseSession { code, reason } |
Open | StreamAction::Open |
Reject | StreamAction::Reject { code } |
OpenAfter | StreamAction::OpenAfter(d) |
SerializeAfter | StreamAction::SerializeAfter(key) |
Action::ReplacePayload with a mismatched length is constructible,
and it is refused with Refusal::LengthChanged — the ReplacePayload
cell’s Conditional failing. Re-framing an object around a new length
is a different capability, and it has no row here because it has no
value: there is nothing to attempt and therefore nothing to refuse.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Pass
Replace
ReplacePayload
Action::ReplacePayload at
the original length.
Delay
Hold
DropElide
Action::Drop with
DropMode::Elide.
Truncate
ResetStream
CloseSession
Open
Reject
ReplaceObject
Replacing a whole wire object. Attemptable and refused, not
unconstructible — Action::Replace(b) at Site::Object is
exactly this attempt, so the engine is really asked and answers
with Refusal::WrongSite. Whole-object replacement at the object
site is a real attempt that really is refused, which is why this
kind is published and why its refusal is one a run emits.
At every non-object site it is
NotAttemptable { why: KindNotDefinedAtThisSite, refusal: WrongSite { site, action: ReplaceObject } }: a control frame, a
datagram and a stream are not objects, so no value carries this
kind there and nothing is ever attempted.
OpenAfter
Opening the peer stream after a delay.
StreamAction::OpenAfter.
open_after_and_serialize_after_are_constructible — the pair of
doc-tests below are the crate’s only compile-time proof that the two
variants exist with the shape they do. They are ordinary doc-tests
rather than inverted compile_fail blocks on purpose: a
compile_fail block that fails for the wrong reason reports ok
exactly as loudly as one that fails for the right one, so a block
asserting a struct-variant syntax (OpenAfter { after: … })
would go on passing against the tuple variants that actually exist.
An ordinary doc-test can only pass by compiling and running.
// open_after_and_serialize_after_are_constructible (1 of 2)
use std::time::Duration;
use moqtap_proxy::action::StreamAction;
let a = StreamAction::OpenAfter(Duration::from_millis(1));
assert!(matches!(a, StreamAction::OpenAfter(d) if d == Duration::from_millis(1)));// open_after_and_serialize_after_are_constructible (2 of 2)
use moqtap_proxy::action::StreamAction;
use moqtap_proxy::event::ProxySide;
use moqtap_proxy::shape::StreamKey;
// A session-local id and the side it arrived on.
let key = StreamKey { side: ProxySide::ClientToProxy, id: 7 };
let b = StreamAction::SerializeAfter(key);
assert!(matches!(b, StreamAction::SerializeAfter(k) if k == key));And the verdicts, which is the one place the two kinds disagree:
SerializeAfter takes exactly the verdict
Self::Open takes at every site, and OpenAfter takes the same
except at Site::StreamHeader, where the peer stream already
exists and there is nothing left to defer.
use moqtap_proxy::capability::{classify, ActionKind, CapCtx, Refusal, Site, Support};
for site in [Site::StreamOpen, Site::StreamHeader] {
assert_eq!(
classify(site, ActionKind::SerializeAfter, &CapCtx::default()),
classify(site, ActionKind::Open, &CapCtx::default()),
"SerializeAfter tracks Open at every site",
);
}
assert_eq!(
classify(Site::StreamOpen, ActionKind::OpenAfter, &CapCtx::default()),
Support::Yes,
);
assert_eq!(
classify(Site::StreamHeader, ActionKind::OpenAfter, &CapCtx::default()),
Support::No(Refusal::WrongSite {
site: Site::StreamHeader,
action: ActionKind::OpenAfter,
}),
);SerializeAfter
Head-of-line simulation.
StreamAction::SerializeAfter.
Its constructor proof hangs on Self::OpenAfter, with its pair.