1use std::fmt;
2use std::ops::Range;
3
4use crate::command::Rejection;
5use crate::domain::DomainError;
6use crate::input::{ActionEvent, ActionId, Bindings, Input, Key};
7use crate::phase::PhaseError;
8use crate::session::{Publication, RestoreError, Session};
9use crate::stores::Stores;
10
11pub struct HostConfig {
12 pub title: &'static str,
13 pub bindings: Bindings,
14}
15
16impl HostConfig {
17 pub fn new(title: &'static str, bindings: Bindings) -> Self {
18 Self { title, bindings }
19 }
20}
21
22#[derive(Debug)]
23pub enum HostError {
24 MissingCapability(&'static str),
25 Setup(Rejection),
26 Phase(PhaseError),
27 Host(String),
28}
29
30impl fmt::Display for HostError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::MissingCapability(capability) => write!(f, "the host is missing {capability}"),
34 Self::Setup(rejection) => fmt::Display::fmt(rejection, f),
35 Self::Phase(error) => fmt::Display::fmt(error, f),
36 Self::Host(message) => f.write_str(message),
37 }
38 }
39}
40
41impl From<Rejection> for HostError {
42 fn from(rejection: Rejection) -> Self {
43 Self::Setup(rejection)
44 }
45}
46
47impl From<DomainError> for HostError {
48 fn from(error: DomainError) -> Self {
49 Self::Setup(Rejection::Domain(error))
50 }
51}
52
53impl From<RestoreError> for HostError {
54 fn from(error: RestoreError) -> Self {
55 Self::Setup(Rejection::Restore(error))
56 }
57}
58
59impl From<PhaseError> for HostError {
60 fn from(error: PhaseError) -> Self {
61 Self::Phase(error)
62 }
63}
64
65pub fn run_headless<A: Stores>(
66 session: &mut Session<A>,
67 config: &HostConfig,
68 steps: u32,
69 holds: &[(Key, Range<u32>)],
70) -> Result<Publication, HostError> {
71 let mut publication = Publication::default();
72 let mut held: Vec<ActionId> = Vec::new();
73 for step in 0..steps {
74 let now: Vec<ActionId> = holds
75 .iter()
76 .filter(|(_, range)| range.contains(&step))
77 .filter_map(|(key, _)| config.bindings.action(*key))
78 .collect();
79 let edges = |from: &[ActionId], to: &[ActionId], pressed: bool| {
80 from.iter()
81 .filter(|action| !to.contains(action))
82 .map(|&action| ActionEvent { action, pressed })
83 .collect::<Vec<_>>()
84 };
85 let mut actions = edges(&now, &held, true);
86 actions.extend(edges(&held, &now, false));
87 held = now;
88 session.boundary(Input {
89 actions,
90 held: held.clone(),
91 ..Input::default()
92 })?;
93 session.tick()?;
94 session.publish(&mut publication)?;
95 }
96 Ok(publication)
97}