1#![deny(missing_docs)]
2
3use std::{collections::HashMap, convert::Infallible, io::Error as IoError, path::Path};
26
27use event_simulation::{Simulation, SimulationInfo, SimulationState};
28use header_parsing::parse_header;
29use multilinear::{MultilinearInfo, MultilinearSimulation};
30use multilinear_parser::{NamedMultilinearInfo, parse_multilinear, parse_multilinear_extended};
31use thiserror::Error;
32
33pub use multilinear::{Aspect, Event};
34
35pub struct Presentation {
37 pub owner: Box<str>,
39 pub dialog: Box<str>,
41}
42
43#[derive(Debug, Error)]
45pub enum LoadError {
46 #[error("failed to parse story net: {0}")]
48 Net(String),
49 #[error("failed to read wiring file: {0}")]
51 Wiring(#[from] IoError),
52 #[error("wiring references unknown event: {0}")]
54 UnknownEvent(Box<str>),
55}
56
57pub struct Story {
59 simulation: MultilinearSimulation,
60 events: HashMap<Box<str>, Event>,
61 aspects: HashMap<Box<str>, (Aspect, Vec<Box<str>>)>,
62 presentation: HashMap<Event, Presentation>,
63 idle: HashMap<Box<str>, Box<str>>,
64}
65
66impl Story {
67 pub fn load(net: &Path, wiring: &Path) -> Result<Self, LoadError> {
73 let named = parse_multilinear_extended(net, None)
74 .map_err(|error| LoadError::Net(error.to_string()))?;
75 let wiring = parse_wiring(wiring)?;
76 Self::from_parts(named, wiring)
77 }
78
79 pub fn load_with_state(
86 net: &Path,
87 wiring: &Path,
88 state: Vec<usize>,
89 ) -> Result<Self, LoadError> {
90 let named = parse_multilinear_extended(net, None)
91 .map_err(|error| LoadError::Net(error.to_string()))?;
92 let wiring = parse_wiring(wiring)?;
93 Self::from_parts_with_state(named, wiring, Some(state))
94 }
95
96 pub fn state(&self) -> Vec<usize> {
101 self.simulation.data().to_vec()
102 }
103
104 pub fn load_from_str(net: &str, wiring: &str) -> Result<Self, LoadError> {
110 let named =
111 parse_multilinear(net.as_bytes()).map_err(|error| LoadError::Net(error.to_string()))?;
112 Self::from_parts(named, parse_wiring_str(wiring))
113 }
114
115 fn from_parts(named: NamedMultilinearInfo, wiring: Wiring) -> Result<Self, LoadError> {
116 Self::from_parts_with_state(named, wiring, None)
117 }
118
119 fn from_parts_with_state(
120 named: NamedMultilinearInfo,
121 wiring: Wiring,
122 state: Option<Vec<usize>>,
123 ) -> Result<Self, LoadError> {
124 let events: HashMap<Box<str>, Event> = (&named.events)
125 .into_iter()
126 .map(|(event, namespace)| (join_namespace(namespace), event))
127 .collect();
128
129 let aspects: HashMap<Box<str>, (Aspect, Vec<Box<str>>)> = (&named.aspects)
130 .into_iter()
131 .map(|(aspect, (name, values))| (name.clone(), (aspect, values.clone())))
132 .collect();
133
134 let Wiring {
135 events: wired,
136 idle,
137 } = wiring;
138
139 let mut presentation = HashMap::new();
140 for (name, entry) in wired {
141 let Some(&event) = events.get(&name) else {
142 return Err(LoadError::UnknownEvent(name));
143 };
144 presentation.insert(event, entry);
145 }
146
147 for (name, event) in &events {
148 if !presentation.contains_key(event) {
149 eprintln!("warning: net event has no wiring (never surfaced): {name}");
150 }
151 }
152
153 let simulation = match state {
154 Some(data) => MultilinearSimulation::from_data(named.info, data)
155 .unwrap_or_else(|error: Infallible| match error {}),
156 None => MultilinearSimulation::new(named.info),
157 };
158
159 Ok(Self {
160 simulation,
161 events,
162 aspects,
163 presentation,
164 idle,
165 })
166 }
167
168 pub fn empty() -> Self {
170 Self {
171 simulation: MultilinearSimulation::new(MultilinearInfo::default()),
172 events: HashMap::new(),
173 aspects: HashMap::new(),
174 presentation: HashMap::new(),
175 idle: HashMap::new(),
176 }
177 }
178
179 pub fn event(&self, name: &str) -> Option<Event> {
181 self.events.get(name).copied()
182 }
183
184 pub fn aspects(&self) -> impl Iterator<Item = (&str, Aspect)> {
186 self.aspects
187 .iter()
188 .map(|(name, (aspect, _))| (name.as_ref(), *aspect))
189 }
190
191 pub fn aspect(&self, name: &str) -> Option<Aspect> {
193 self.aspects.get(name).map(|(aspect, _)| *aspect)
194 }
195
196 pub fn value(&self, aspect: Aspect) -> usize {
198 self.simulation.data()[aspect.0]
199 }
200
201 pub fn value_name(&self, name: &str) -> Option<&str> {
203 let (aspect, values) = self.aspects.get(name)?;
204 values
205 .get(self.simulation.data()[aspect.0])
206 .map(AsRef::as_ref)
207 }
208
209 pub fn values(&self, name: &str) -> Option<impl Iterator<Item = &str>> {
211 let (_, values) = self.aspects.get(name)?;
212 Some(values.iter().map(AsRef::as_ref))
213 }
214
215 pub fn set_value(&mut self, name: &str, value: &str) -> bool {
222 let Some((aspect, values)) = self.aspects.get(name) else {
223 return false;
224 };
225 let Some(index) = values
226 .iter()
227 .position(|candidate| candidate.as_ref() == value)
228 else {
229 return false;
230 };
231
232 let mut data = self.simulation.data().to_vec();
233 data[aspect.0] = index;
234 let state = self
235 .simulation
236 .load_state(data)
237 .unwrap_or_else(|error: Infallible| match error {});
238 self.simulation.state = state;
239 true
240 }
241
242 pub fn callable(&self, event: Event) -> bool {
244 self.simulation.callable(event)
245 }
246
247 pub fn callable_events(&self) -> Vec<Event> {
249 let mut events: Vec<Event> = self.simulation.callables().collect();
250 events.sort();
251 events
252 }
253
254 pub fn options(&self, owner: &str) -> Vec<Event> {
256 let mut events: Vec<Event> = self
257 .simulation
258 .callables()
259 .filter(|event| {
260 self.presentation
261 .get(event)
262 .is_some_and(|presentation| presentation.owner.as_ref() == owner)
263 })
264 .collect();
265 events.sort();
266 events
267 }
268
269 pub fn presentation(&self, event: Event) -> Option<&Presentation> {
271 self.presentation.get(&event)
272 }
273
274 pub fn dialog(&self, event: Event) -> Option<&str> {
276 self.presentation.get(&event).map(|p| p.dialog.as_ref())
277 }
278
279 pub fn owner(&self, event: Event) -> Option<&str> {
281 self.presentation.get(&event).map(|p| p.owner.as_ref())
282 }
283
284 pub fn idle(&self, owner: &str) -> Option<&str> {
286 self.idle.get(owner).map(AsRef::as_ref)
287 }
288
289 pub fn play(&mut self, event: Event) -> bool {
291 self.simulation.try_call(event)
292 }
293
294 pub fn revert(&mut self, event: Event) -> bool {
296 self.simulation.try_revert(event)
297 }
298}
299
300fn join_namespace(namespace: &[Box<str>]) -> Box<str> {
301 namespace
302 .iter()
303 .map(AsRef::as_ref)
304 .collect::<Vec<&str>>()
305 .join(" ")
306 .into_boxed_str()
307}
308
309struct Wiring {
310 events: HashMap<Box<str>, Presentation>,
311 idle: HashMap<Box<str>, Box<str>>,
312}
313
314fn parse_wiring(path: &Path) -> Result<Wiring, IoError> {
315 let content = std::fs::read_to_string(path)?;
316 Ok(parse_wiring_str(&content))
317}
318
319fn parse_wiring_str(content: &str) -> Wiring {
320 let mut events: HashMap<Box<str>, Presentation> = HashMap::new();
321 let mut idle: HashMap<Box<str>, Box<str>> = HashMap::new();
322 let mut header_path: Vec<Box<str>> = Vec::new();
323
324 for line in content.lines() {
325 if let Some(result) = parse_header(&mut header_path, line) {
326 match result {
327 Ok(changes) => {
328 changes.apply();
329 }
330 Err(_) => eprintln!("invalid header in wiring: {line}"),
331 }
332 continue;
333 }
334
335 let trimmed = line.trim();
336 if trimmed.is_empty() {
337 continue;
338 }
339
340 let mut tokens = trimmed.splitn(2, char::is_whitespace);
341 let Some(key) = tokens.next() else { continue };
342 let value = tokens.next().unwrap_or("").trim();
343
344 match header_path.as_slice() {
345 [section, event] if section.as_ref() == "events" => {
346 let entry = events.entry(event.clone()).or_insert_with(|| Presentation {
347 owner: Box::from(""),
348 dialog: Box::from(""),
349 });
350 match key {
351 "owner" => entry.owner = Box::from(value),
352 "dialog" => entry.dialog = Box::from(value),
353 _ => {}
354 }
355 }
356 [section, owner] if section.as_ref() == "idle" && key == "dialog" => {
357 idle.insert(owner.clone(), Box::from(value));
358 }
359 _ => {}
360 }
361 }
362
363 Wiring { events, idle }
364}