Skip to main content

trellis_core/
resource.rs

1use crate::{Graph, GraphResult, NodeId, ResourceKey, ScopeId};
2use std::sync::Arc;
3
4/// Data-only command describing an external resource lifecycle change.
5#[derive(Clone, Debug, Eq, PartialEq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub enum ResourceCommand<C> {
8    /// Open a resource with an application-defined command payload.
9    Open {
10        /// Resource identity understood by the graph.
11        key: ResourceKey,
12        /// Scope requesting ownership.
13        scope: ScopeId,
14        /// Host-defined command payload.
15        command: C,
16    },
17    /// Close a resource after its final graph-visible owner is removed.
18    Close {
19        /// Resource identity understood by the graph.
20        key: ResourceKey,
21        /// Scope whose ownership was removed.
22        scope: ScopeId,
23    },
24    /// Replace a live resource with an application-defined command payload.
25    Replace {
26        /// Resource identity understood by the graph.
27        key: ResourceKey,
28        /// Scope requesting replacement.
29        scope: ScopeId,
30        /// Host-defined command payload.
31        command: C,
32    },
33    /// Refresh a live resource with an application-defined command payload.
34    Refresh {
35        /// Resource identity understood by the graph.
36        key: ResourceKey,
37        /// Scope requesting refresh.
38        scope: ScopeId,
39        /// Host-defined command payload.
40        command: C,
41    },
42}
43
44impl<C> ResourceCommand<C> {
45    /// Returns the resource key for this command.
46    pub fn key(&self) -> &ResourceKey {
47        match self {
48            Self::Open { key, .. }
49            | Self::Close { key, .. }
50            | Self::Replace { key, .. }
51            | Self::Refresh { key, .. } => key,
52        }
53    }
54
55    /// Returns the scope associated with this command.
56    pub fn scope(&self) -> ScopeId {
57        match self {
58            Self::Open { scope, .. }
59            | Self::Close { scope, .. }
60            | Self::Replace { scope, .. }
61            | Self::Refresh { scope, .. } => *scope,
62        }
63    }
64}
65
66/// Ordered data-only resource plan returned from graph propagation.
67#[derive(Clone, Debug, Eq, PartialEq)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub struct ResourcePlan<C> {
70    commands: Vec<ResourceCommand<C>>,
71}
72
73impl<C> ResourcePlan<C> {
74    /// Creates an empty resource plan.
75    pub fn new() -> Self {
76        Self {
77            commands: Vec::new(),
78        }
79    }
80
81    /// Adds an open command.
82    pub fn open(&mut self, key: ResourceKey, scope: ScopeId, command: C) {
83        self.commands.push(ResourceCommand::Open {
84            key,
85            scope,
86            command,
87        });
88    }
89
90    /// Adds a close command.
91    pub fn close(&mut self, key: ResourceKey, scope: ScopeId) {
92        self.commands.push(ResourceCommand::Close { key, scope });
93    }
94
95    /// Adds a replace command.
96    pub fn replace(&mut self, key: ResourceKey, scope: ScopeId, command: C) {
97        self.commands.push(ResourceCommand::Replace {
98            key,
99            scope,
100            command,
101        });
102    }
103
104    /// Adds a refresh command.
105    pub fn refresh(&mut self, key: ResourceKey, scope: ScopeId, command: C) {
106        self.commands.push(ResourceCommand::Refresh {
107            key,
108            scope,
109            command,
110        });
111    }
112
113    /// Returns ordered commands in this plan.
114    pub fn commands(&self) -> &[ResourceCommand<C>] {
115        &self.commands
116    }
117
118    /// Consumes the plan into ordered commands.
119    pub fn into_commands(self) -> Vec<ResourceCommand<C>> {
120        self.commands
121    }
122
123    pub(crate) fn append(&mut self, other: ResourcePlan<C>) {
124        self.commands.extend(other.commands);
125    }
126}
127
128impl<C> Default for ResourcePlan<C> {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134/// Read-only context passed to resource planners.
135pub struct PlanContext<'graph, D> {
136    scope: ScopeId,
137    diff: &'graph D,
138}
139
140impl<'graph, D> PlanContext<'graph, D> {
141    pub(crate) fn new(scope: ScopeId, diff: &'graph D) -> Self {
142        Self { scope, diff }
143    }
144
145    /// Scope that owns resource demand produced by this planner.
146    pub fn scope(&self) -> ScopeId {
147        self.scope
148    }
149
150    /// Structural diff consumed by this planner.
151    pub fn diff(&self) -> &'graph D {
152        self.diff
153    }
154}
155
156type PlannerFn<C> = dyn Fn(&Graph<C>) -> GraphResult<ResourcePlan<C>> + Send + Sync;
157
158pub(crate) struct ResourcePlanner<C> {
159    pub(crate) collection: NodeId,
160    pub(crate) scope: ScopeId,
161    run: Arc<PlannerFn<C>>,
162}
163
164impl<C> Clone for ResourcePlanner<C> {
165    fn clone(&self) -> Self {
166        Self {
167            collection: self.collection,
168            scope: self.scope,
169            run: Arc::clone(&self.run),
170        }
171    }
172}
173
174impl<C> ResourcePlanner<C> {
175    pub(crate) fn new(
176        collection: NodeId,
177        scope: ScopeId,
178        run: impl Fn(&Graph<C>) -> GraphResult<ResourcePlan<C>> + Send + Sync + 'static,
179    ) -> Self {
180        Self {
181            collection,
182            scope,
183            run: Arc::new(run),
184        }
185    }
186
187    pub(crate) fn run(&self, graph: &Graph<C>) -> GraphResult<ResourcePlan<C>> {
188        (self.run)(graph)
189    }
190}