cyndikator_dispatch/dispatch/
mod.rs

1use crate::Event;
2use parse::Parsable;
3use runtime::DispatchCase;
4use std::fmt;
5
6use token::Token;
7
8mod parse;
9mod token;
10
11pub(crate) mod runtime;
12
13#[cfg(test)]
14mod test;
15
16/// A set of rules to determine what [Action]s should be taken given an [Event]
17#[derive(Debug)]
18pub struct Dispatch {
19    cases: Vec<runtime::DispatchCase>,
20}
21
22/// An action to take given a specific [Event]
23#[derive(Debug, PartialEq)]
24pub enum Action {
25    /// Record the event for viewing later
26    Record,
27
28    /// Notify via an approprate channel (system notification system)
29    Notify,
30
31    /// Execute a shell line
32    Exec(String),
33}
34
35impl Dispatch {
36    /// Find the [Action]s to take given an [Event].
37    pub fn dispatch(&self, event: &Event) -> Vec<Action> {
38        let mut actions = Vec::with_capacity(self.cases.len());
39        for case in &self.cases {
40            if case.cond.satisfies(event) {
41                for gen in &case.actions {
42                    if gen.is_drop() {
43                        return actions;
44                    }
45
46                    let action = gen.generate(event);
47
48                    if !actions.contains(&action) {
49                        actions.push(action);
50                    }
51                }
52            }
53        }
54
55        actions
56    }
57
58    /// Parse the DSL into a [Dispatch]
59    pub fn parse(input: &str) -> Result<Dispatch, ParseError> {
60        let tokens = Token::tokenize_significant(input)?;
61
62        let mut tokens = &tokens[..];
63        let mut cases = Vec::new();
64
65        while !tokens.is_empty() {
66            let (t, case) = DispatchCase::parse(&tokens)?;
67            cases.push(case);
68
69            tokens = t;
70        }
71
72        Ok(Dispatch { cases })
73    }
74}
75
76/// An Error found when parsing the [Dispatch] DSL
77#[derive(Debug)]
78#[non_exhaustive]
79pub enum ParseError {
80    /// An issue found during tokenization
81    Tokenize,
82
83    /// Parsing expected a token but ran out of tokens.
84    EndOfTokens,
85
86    /// Some sort of expectation failed with the arrangement of tokens
87    InvalidExpectation {
88        /// a description of what was expected
89        expect: String,
90        /// a debug representation of the token found instead of the expectation
91        reality: String,
92    },
93}
94
95impl std::error::Error for ParseError {}
96
97impl fmt::Display for ParseError {
98    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
99        match self {
100            ParseError::Tokenize => write!(fmt, "Issue with tokenization"),
101            ParseError::EndOfTokens => write!(fmt, "Ran out of tokens"),
102            ParseError::InvalidExpectation { expect, reality } => {
103                write!(fmt, "expected {} but received {}", expect, reality)
104            }
105        }
106    }
107}