Skip to main content

rite_model/ir/
ceremony.rs

1//! Resolved ceremony IR types.
2//!
3//! These types represent a fully resolved ceremony, ready for execution.
4//! All references have been validated, execution order computed, and
5//! parameter values merged with defaults.
6
7use super::SymbolTable;
8use super::ids::{ActId, ArtifactId, MaterialId, OutputId, ParamId, RoleId, SectionId, StepId};
9use crate::expression::ExprValue;
10use crate::material::MaterialSource;
11use crate::types::{ActionType, DutyType, Metadata, OutputType, ParameterType};
12use rite_sdk::BackendConfig;
13use std::collections::HashMap;
14
15/// A fully resolved ceremony, ready for execution.
16///
17/// All references have been validated, the execution order has been computed,
18/// and parameter values have been merged with external inputs.
19#[derive(Debug, Clone)]
20pub struct Ceremony {
21    /// Ceremony metadata (name, description).
22    pub metadata: Metadata,
23
24    /// Resolved roles, keyed by role ID.
25    pub roles: SymbolTable<RoleId, Role>,
26
27    /// Resolved acts, keyed by act ID (for script display).
28    pub acts: SymbolTable<ActId, Act>,
29
30    /// Resolved sections, keyed by section ID.
31    pub sections: SymbolTable<SectionId, Section>,
32
33    /// Resolved parameters with their values, keyed by parameter ID.
34    pub parameters: SymbolTable<ParamId, Parameter>,
35
36    /// Resolved materials with their sources, keyed by material ID.
37    pub materials: SymbolTable<MaterialId, Material>,
38
39    /// Pre-ceremony conditions (prose-only checklist).
40    pub prerequisites: Vec<String>,
41
42    /// Resolved outputs with their destinations, keyed by output ID.
43    pub outputs: SymbolTable<OutputId, Output>,
44
45    /// Backend configurations, keyed by backend name.
46    pub backends: HashMap<String, BackendConfig>,
47
48    /// Steps in execution order.
49    ///
50    /// Pre-computed topological order based on sections.
51    pub execution_plan: Vec<Step>,
52
53    /// Post-ceremony duties, in declaration order.
54    pub after: Vec<PostCeremonyDuty>,
55}
56
57/// A resolved role.
58#[derive(Debug, Clone)]
59pub struct Role {
60    /// Role identifier.
61    pub id: RoleId,
62
63    /// Human-readable name (derived or explicit).
64    pub name: String,
65
66    /// Role type: the prefix before `__` (or the whole ID if no `__`).
67    ///
68    /// E.g. `"witness__1"` → `"witness"`, `"operator"` → `"operator"`.
69    pub role_type: String,
70
71    /// The person assigned to this role slot, if any.
72    pub person: Option<String>,
73}
74
75/// A resolved act (for script display).
76#[derive(Debug, Clone)]
77pub struct Act {
78    /// Act identifier.
79    pub id: ActId,
80
81    /// Human-readable name.
82    pub name: Option<String>,
83
84    /// Preamble text displayed at the start of the act.
85    pub description: Option<String>,
86}
87
88/// A resolved section.
89#[derive(Debug, Clone)]
90pub struct Section {
91    /// Section identifier.
92    pub id: SectionId,
93
94    /// Reference to the act this section belongs to.
95    pub act: Option<ActId>,
96
97    /// Human-readable name.
98    pub name: Option<String>,
99
100    /// Section description.
101    pub description: Option<String>,
102
103    /// Default role for steps in this section.
104    pub default_role: Option<RoleId>,
105}
106
107/// A resolved step, ready for execution.
108#[derive(Debug, Clone)]
109pub struct Step {
110    /// Step identifier.
111    pub id: StepId,
112
113    /// Display label for this step (e.g., `"1.1"`, `"2.3"`).
114    ///
115    /// Uses hierarchical numbering: `act.step_within_act`.
116    /// When there's only one act, just the step number (e.g., `"1"`, `"2"`).
117    pub step_label: String,
118
119    /// Section this step belongs to.
120    pub section: SectionId,
121
122    /// Action to perform.
123    pub action: ActionType,
124
125    /// Backend to use for this step (if action requires one).
126    ///
127    /// References a backend declared in the ceremony's backends section.
128    pub backend: Option<String>,
129
130    /// Role that performs this step (resolved from step or section default).
131    pub role: Option<RoleId>,
132
133    /// Human-confirmed preconditions before this step executes.
134    pub preconditions: Vec<String>,
135
136    /// Action-specific parameters with expressions parsed (not yet evaluated).
137    ///
138    /// The runtime evaluates these to `serde_json::Value` before passing to handlers.
139    pub with: ExprValue,
140
141    /// Validated input artifact references (for ordering validation).
142    pub reads: Vec<ArtifactRef>,
143
144    /// Pre-resolved input references for handlers.
145    ///
146    /// Handlers can access artifact IDs directly without parsing.
147    pub reads_resolved: Option<StepInputs>,
148
149    /// Artifact ID this step produces.
150    pub creates: Option<ArtifactId>,
151
152    /// Human-readable description for display (may contain interpolated expressions).
153    pub description: Option<ExprValue>,
154
155    /// Skip the default pause after step completion.
156    ///
157    /// When `true`, the executor auto-advances without waiting for user acknowledgment.
158    pub silent: bool,
159}
160
161/// A resolved parameter with its value.
162#[derive(Debug, Clone)]
163pub struct Parameter {
164    /// Parameter identifier.
165    pub id: ParamId,
166
167    /// Declared type.
168    pub declared_type: ParameterType,
169
170    /// Resolved value (from CLI/env/prompt or default).
171    pub value: serde_json::Value,
172
173    /// Human-readable description.
174    pub description: Option<String>,
175}
176
177/// What kind of material and its resolved source/identifier.
178#[derive(Debug, Clone)]
179pub enum MaterialKind {
180    /// Digital: loaded from file path. `None` if source not yet provided.
181    Digital {
182        /// Resolved file source, or `None` if not yet provided.
183        source: Option<MaterialSource>,
184    },
185    /// Physical: optional identifier string for display.
186    Physical {
187        /// Human-readable identifier (e.g., serial number).
188        identifier: Option<String>,
189        /// Item count for checklist rendering.
190        quantity: Option<u32>,
191    },
192}
193
194/// A resolved material with its source.
195#[derive(Debug, Clone)]
196pub struct Material {
197    /// Material identifier.
198    pub id: MaterialId,
199
200    /// What kind of material and its resolved source/identifier.
201    pub kind: MaterialKind,
202
203    /// Optional human-readable title for display (falls back to material ID).
204    pub title: Option<String>,
205
206    /// Human-readable description.
207    pub description: Option<String>,
208}
209
210impl Material {
211    /// Get the display name for this material (title or ID).
212    pub fn display_name(&self) -> &str {
213        self.title.as_deref().unwrap_or(self.id.as_str())
214    }
215
216    /// Whether this is a digital material.
217    pub fn is_digital(&self) -> bool {
218        matches!(self.kind, MaterialKind::Digital { .. })
219    }
220
221    /// Whether this is a physical material.
222    pub fn is_physical(&self) -> bool {
223        matches!(self.kind, MaterialKind::Physical { .. })
224    }
225}
226
227/// A resolved post-ceremony duty.
228#[derive(Debug, Clone)]
229pub struct PostCeremonyDuty {
230    /// Duty identifier (the dict key in the YAML `after:` block).
231    pub id: String,
232
233    /// Type of duty.
234    pub kind: DutyType,
235
236    /// Role responsible for this duty (resolved from plain role ID string).
237    pub role: Option<RoleId>,
238
239    /// Description of this duty (overrides built-in prose for the type).
240    pub description: Option<String>,
241
242    /// Checklist sub-items.
243    pub items: Vec<String>,
244
245    /// For distribute_* types: who receives.
246    pub recipient: Option<String>,
247
248    /// For storage/archive types: where.
249    pub location: Option<String>,
250}
251
252/// A resolved output declaration.
253#[derive(Debug, Clone)]
254pub struct Output {
255    /// Output identifier.
256    pub id: OutputId,
257
258    /// Type of output.
259    pub kind: OutputType,
260
261    /// Human-readable description.
262    pub description: Option<String>,
263}
264
265/// Reference to an artifact (either a material or a produced artifact).
266#[derive(Debug, Clone)]
267pub enum ArtifactRef {
268    /// References a pre-loaded material.
269    Material {
270        /// Material ID.
271        id: MaterialId,
272        /// Optional property accessor (e.g., `"public"` from a keypair).
273        property: Option<String>,
274    },
275
276    /// References an artifact produced by an earlier step.
277    Produced {
278        /// Artifact ID.
279        id: ArtifactId,
280        /// Optional property accessor.
281        property: Option<String>,
282    },
283}
284
285impl ArtifactRef {
286    /// Get a display name for this reference.
287    pub fn display_name(&self) -> String {
288        match self {
289            ArtifactRef::Material { id, property: None } => id.to_string(),
290            ArtifactRef::Material {
291                id,
292                property: Some(p),
293            } => format!("{id}.{p}"),
294            ArtifactRef::Produced { id, property: None } => id.to_string(),
295            ArtifactRef::Produced {
296                id,
297                property: Some(p),
298            } => format!("{id}.{p}"),
299        }
300    }
301
302    /// Get the artifact ID from this reference.
303    pub fn artifact_id(&self) -> ArtifactId {
304        match self {
305            ArtifactRef::Material { id, .. } => ArtifactId::new(id.as_str()),
306            ArtifactRef::Produced { id, .. } => id.clone(),
307        }
308    }
309
310    /// Get the property accessor from this reference (if any).
311    pub fn property(&self) -> Option<&str> {
312        match self {
313            ArtifactRef::Material { property, .. } | ArtifactRef::Produced { property, .. } => {
314                property.as_deref()
315            }
316        }
317    }
318}
319
320/// Pre-resolved input references for a step.
321///
322/// At resolution time, the resolver parses `${artifact.name}` strings and determines
323/// whether they reference materials or produced artifacts. This eliminates runtime
324/// string parsing in handlers.
325#[derive(Debug, Clone)]
326pub enum StepInputs {
327    /// Single artifact reference (e.g., `reads: "${artifact.keypair}"`)
328    Single(ArtifactRef),
329    /// Named artifact references (e.g., `reads: { key_to_wrap: "...", wrapping_key: "..." }`)
330    Named(HashMap<String, ArtifactRef>),
331}
332
333impl StepInputs {
334    /// Get a single input reference (returns `None` if this is `Named`).
335    pub fn as_single(&self) -> Option<&ArtifactRef> {
336        match self {
337            StepInputs::Single(r) => Some(r),
338            StepInputs::Named(_) => None,
339        }
340    }
341
342    /// Get named input references (returns `None` if this is `Single`).
343    pub fn as_named(&self) -> Option<&HashMap<String, ArtifactRef>> {
344        match self {
345            StepInputs::Single(_) => None,
346            StepInputs::Named(m) => Some(m),
347        }
348    }
349
350    /// Get a named input by key.
351    pub fn get(&self, key: &str) -> Option<&ArtifactRef> {
352        match self {
353            StepInputs::Single(_) => None,
354            StepInputs::Named(m) => m.get(key),
355        }
356    }
357}