#[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 that used to prove this capability’s absence now proves
its presence, and they remain 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, which is how
the two blocks this replaces went on passing while asserting a
struct-variant syntax (OpenAfter { after: … }) that never
matched 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 plus the side it arrived on — never a
// transport stream id, which is the constant 0 on WebTransport.
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.