Skip to main content

Effect

Enum Effect 

Source
pub enum Effect {
    Allow,
    Deny {
        reason: Option<String>,
        code: Option<String>,
    },
    Plugin {
        name: String,
    },
    Delegate(DelegateStep),
    Taint {
        label: String,
        scopes: Vec<TaintScope>,
    },
    FieldOp {
        path: String,
        stages: Vec<Stage>,
    },
    Sequential(Vec<Effect>),
    Parallel(Vec<Effect>),
    When {
        condition: Expression,
        body: Vec<Effect>,
        source: String,
    },
    Pdp {
        call: PdpCall,
        on_allow: Vec<Effect>,
        on_deny: Vec<Effect>,
    },
}
Expand description

One thing a matching rule does. Mirrors DSL spec §3 effect classes:

  • Control — Allow, Deny
  • Label — Taint
  • Host — Plugin, Delegate

Content effects (redact, mask, omit, hash) and orchestration (Sequential, Parallel) land in later slices (E2 / E3).

PDP calls (cedar:(…), opa(…), …) remain top-level Step variants for now; folding them into Effect is an E4 cleanup.

§Inside a Vec<Effect> (a rule’s effects body)

  • Allow is a no-op — lets evaluation continue to the next effect in the list, then to the next step in policy:.
  • Deny short-circuits the rest of the list, the rest of the policy: block, and the route. The reason propagates into the violation message.
  • Plugin / Delegate dispatch identically to their top-level Step counterparts (same invoker traits).
  • Taint accumulates into the phase’s taint events.

Variants§

§

Allow

§

Deny

Fields

§reason: Option<String>
§code: Option<String>

Author-supplied stable violation code. When Some, it overrides the rule’s auto-generated source-position code (routes.tool:X.apl.policy[N]) downstream. Useful when MCP clients want to dispatch on category (quota.exceeded, delegation.depth_exceeded) rather than position, or when multiple routes share a deny category that should aggregate consistently in audit dashboards. When None, the evaluator falls back to rule.source as the code — matches the historical behavior.

Parser shape: deny('reason', 'code') (two positional arguments) or the structured deny: { reason: ..., code: ... } map form.

§

Plugin

Fields

§name: String
§

Delegate(DelegateStep)

§

Taint

Fields

§label: String
§scopes: Vec<TaintScope>
§

FieldOp

Content effect (DSL §3) — apply a pipe chain (redact, mask, omit, hash, validators, transforms) to a field in the route’s args or result. The author writes result.salary | redact inside a do: body; the parser splits the dotted path from the pipeline.

path must start with args. or result. — the evaluator dispatches the lookup against RoutePayload.args or RoutePayload.result. A FieldOp inside a Pre-phase route’s do: that targets result.X is a no-op (the result hasn’t been produced yet); same goes for a Post-phase rule that targets args.X (the args are already on the wire). The evaluator silently skips out-of-phase ops so the same when:/do: shape can describe both phases without branching.

Fields

§path: String
§stages: Vec<Stage>
§

Sequential(Vec<Effect>)

Run a list of effects in declaration order, stopping on the first Deny. Semantically equivalent to inlining the list into the enclosing scope; the variant exists to make grouping explicit and to pair with Parallel.

§

Parallel(Vec<Effect>)

Run a list of effects concurrently. Any Deny → overall Deny. Taints from all branches accumulate. Bag and payload mutations inside parallel branches are discarded when the branch completes — each branch gets a clone of the state, never the shared mutable original. Plugins inside Parallel can still emit taints (those merge); any other mutation they try to make (bag writes, args/result rewrites) vanishes.

Config-load rejects FieldOp and Delegate directly inside Parallel (recursively), since both would silently drop their effect. The escape valve is Sequential.

§

When

Predicate-gated body. body runs in order when condition evaluates to true; any Deny in the body halts the surrounding phase. Replaces the historical Step::Rule(Rule) shape — when: / do: directly desugars to this. A bare require(X) or deny(X) shorthand compiles to When { condition: X, body: vec![Effect::Allow / Deny] }.

source is the human-readable origin (e.g. "routes.X.policy[2]") surfaced in Decision::Deny.rule_source when the body denies without supplying its own code.

Fields

§condition: Expression
§body: Vec<Effect>
§source: String
§

Pdp

External PDP call. on_allow / on_deny are reaction effect lists fired against the PDP’s decision (DSL §7.5). Replaces Step::Pdp { ... }args-shape stays identical.

Fields

§call: PdpCall
§on_allow: Vec<Effect>
§on_deny: Vec<Effect>

Implementations§

Source§

impl Effect

Source

pub fn contains_mutation(&self) -> bool

Walk this effect (and any nested effects) checking whether any node would mutate route state. Used by the config-load validator to reject FieldOp / Delegate inside Parallel since both would silently drop their effect in a discarded branch.

Source

pub fn validate_parallel_purity(&self) -> Result<(), String>

Walk the effect tree rejecting any FieldOp / Delegate that lives directly or transitively under a Parallel node. Returns the path string of the first violation found (or Ok(()) if the tree is clean). Run at config-load.

Trait Implementations§

Source§

impl Clone for Effect

Source§

fn clone(&self) -> Effect

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Effect

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Effect

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl From<Rule> for Effect

Rule is structurally identical to Effect::When. The From impl lets callers that already hold a Rule (notably the parser’s inner helpers and the test fixtures) drop a .into() instead of re-spelling all three fields. Bridges the few remaining producers while the migration completes; will probably stay long-term because the parser still builds Rule incrementally before deciding it’s an Effect::When.

Source§

fn from(r: Rule) -> Effect

Converts to this type from the input type.
Source§

impl PartialEq for Effect

Source§

fn eq(&self, other: &Effect) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Effect

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Effect

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.