Skip to main content

loam_runtime/
host.rs

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}