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