Skip to main content

palladium_actor/
policy.rs

1use crate::errors::PathParseError;
2use crate::path::ActorPath;
3use std::time::Duration;
4
5// ── NamespacePolicy ───────────────────────────────────────────────────────────
6
7/// Controls which path prefixes an actor may hold addresses into.
8///
9/// Denied prefixes take precedence over allowed prefixes.  Evaluation occurs
10/// at address-grant time, not at send time.
11#[derive(Debug, Clone)]
12pub struct NamespacePolicy {
13    pub(crate) allowed_prefixes: Vec<ActorPath>,
14    pub(crate) denied_prefixes: Vec<ActorPath>,
15}
16
17impl NamespacePolicy {
18    pub fn new(allowed_prefixes: Vec<ActorPath>) -> Self {
19        Self {
20            allowed_prefixes,
21            denied_prefixes: Vec::new(),
22        }
23    }
24
25    /// Default policy for a non-plugin actor: allows siblings (same parent)
26    /// and `/system/*`.  Explicit deny rules take precedence.
27    pub fn default_for(namespace: &ActorPath) -> Result<Self, PathParseError> {
28        let parent = namespace.parent().unwrap_or_else(ActorPath::root);
29        Ok(Self {
30            allowed_prefixes: vec![parent, ActorPath::parse("/system")?],
31            denied_prefixes: Vec::new(),
32        })
33    }
34
35    /// Default policy for a plugin actor: allows `/plugins/<plugin_name>/*`
36    /// and `/system/*` only.
37    pub fn plugin_default(plugin_name: &str) -> Result<Self, PathParseError> {
38        let plugin_prefix = ActorPath::parse(&format!("/plugins/{plugin_name}"))?;
39        Ok(Self {
40            allowed_prefixes: vec![plugin_prefix, ActorPath::parse("/system")?],
41            denied_prefixes: Vec::new(),
42        })
43    }
44
45    /// Append an explicit deny rule.  Denied prefixes override allowed ones.
46    pub fn deny_prefix(mut self, path: ActorPath) -> Self {
47        self.denied_prefixes.push(path);
48        self
49    }
50
51    /// Returns `true` if `path` is permitted under this policy.
52    pub fn allows(&self, path: &ActorPath) -> bool {
53        if self.matches_any(&self.denied_prefixes, path) {
54            return false;
55        }
56        self.matches_any(&self.allowed_prefixes, path)
57    }
58
59    fn matches_any(&self, prefixes: &[ActorPath], target: &ActorPath) -> bool {
60        prefixes
61            .iter()
62            .any(|p| p.as_str() == target.as_str() || p.is_ancestor_of(target))
63    }
64}
65
66// ── RestartPolicy / ShutdownPolicy ────────────────────────────────
67
68/// Governs when a supervisor restarts a child after it exits.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum RestartPolicy {
71    /// Always restart, regardless of exit reason.
72    Permanent,
73    /// Restart only after an abnormal exit (`ActorError` / panic).
74    Transient,
75    /// Never restart.
76    Temporary,
77}
78
79/// Governs how a supervisor stops a child.
80#[derive(Debug, Clone)]
81pub enum ShutdownPolicy {
82    /// Call `on_stop` and wait up to `Duration`; force-kill on timeout.
83    Timeout(Duration),
84    /// Terminate immediately without calling `on_stop`.
85    Brutal,
86}