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}