sim_lib_standard_core/
scenario.rs1use std::{collections::BTreeSet, sync::Arc};
4
5use sim_kernel::{Cx, Datum, Error, Result, Symbol};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
9pub enum ScenarioObservationLane {
10 ValueOrFailure,
12 Events,
14 Receipts,
16 Browse,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct ScenarioInput {
23 pub id: Symbol,
25 pub authority: Symbol,
27 pub datum: Datum,
29}
30
31impl ScenarioInput {
32 pub fn new(id: Symbol, authority: Symbol, datum: Datum) -> Self {
34 Self {
35 id,
36 authority,
37 datum,
38 }
39 }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct ScenarioLimits {
45 pub max_inputs: usize,
47 pub max_observations: usize,
49}
50
51impl ScenarioLimits {
52 pub const fn new(max_inputs: usize, max_observations: usize) -> Self {
54 Self {
55 max_inputs,
56 max_observations,
57 }
58 }
59}
60
61#[derive(Clone, Debug, PartialEq, Eq)]
63pub struct ScenarioSpec {
64 pub id: Symbol,
66 pub setup: Symbol,
68 pub authorities: BTreeSet<Symbol>,
70 pub limits: Option<ScenarioLimits>,
72 pub inputs: Vec<ScenarioInput>,
74 pub observation_lanes: BTreeSet<ScenarioObservationLane>,
76}
77
78impl ScenarioSpec {
79 pub fn new(id: Symbol, setup: Symbol) -> Self {
81 Self {
82 id,
83 setup,
84 authorities: BTreeSet::new(),
85 limits: None,
86 inputs: Vec::new(),
87 observation_lanes: BTreeSet::new(),
88 }
89 }
90
91 pub fn with_authority(mut self, authority: Symbol) -> Self {
93 self.authorities.insert(authority);
94 self
95 }
96
97 pub fn with_limits(mut self, limits: ScenarioLimits) -> Self {
99 self.limits = Some(limits);
100 self
101 }
102
103 pub fn with_input(mut self, input: ScenarioInput) -> Self {
105 self.inputs.push(input);
106 self
107 }
108
109 pub fn observing(mut self, lane: ScenarioObservationLane) -> Self {
111 self.observation_lanes.insert(lane);
112 self
113 }
114
115 pub(crate) fn validate(
116 &self,
117 supported_lanes: &BTreeSet<ScenarioObservationLane>,
118 ) -> Result<()> {
119 let Some(limits) = self.limits else {
120 return Err(Error::Eval(format!(
121 "scenario {} is missing limits",
122 self.id
123 )));
124 };
125 if limits.max_inputs == 0 || limits.max_observations == 0 {
126 return Err(Error::Eval(format!(
127 "scenario {} has a zero bound",
128 self.id
129 )));
130 }
131 if self.inputs.len() > limits.max_inputs {
132 return Err(Error::Eval(format!(
133 "scenario {} exceeds its input bound",
134 self.id
135 )));
136 }
137 if self.observation_lanes.len() > limits.max_observations {
138 return Err(Error::Eval(format!(
139 "scenario {} exceeds its observation bound",
140 self.id
141 )));
142 }
143 if self.observation_lanes.is_empty() {
144 return Err(Error::Eval(format!(
145 "scenario {} selects no observation lanes",
146 self.id
147 )));
148 }
149 if let Some(input) = self
150 .inputs
151 .iter()
152 .find(|input| !self.authorities.contains(&input.authority))
153 {
154 return Err(Error::Eval(format!(
155 "scenario {} input {} uses undeclared authority {}",
156 self.id, input.id, input.authority
157 )));
158 }
159 if let Some(lane) = self
160 .observation_lanes
161 .iter()
162 .find(|lane| !supported_lanes.contains(lane))
163 {
164 return Err(Error::Eval(format!(
165 "scenario {} selects unsupported lane {lane:?}",
166 self.id
167 )));
168 }
169 Ok(())
170 }
171}
172
173pub type ScenarioDriver = Arc<dyn Fn(&mut Cx, &ScenarioSpec) -> Result<()> + Send + Sync + 'static>;
175
176#[derive(Clone)]
178pub struct CharacterizationScenario {
179 pub spec: ScenarioSpec,
181 pub(crate) driver: ScenarioDriver,
182}
183
184impl CharacterizationScenario {
185 pub fn new(spec: ScenarioSpec, driver: ScenarioDriver) -> Self {
187 Self { spec, driver }
188 }
189}