Skip to main content

edge_core/
intent.rs

1//! Input primitives and service intents exchanged by the routing engine.
2
3use serde::{Deserialize, Serialize};
4
5/// Physical input from a device. Device-agnostic: a Nuimo rotate and a
6/// dial rotate both produce `Rotate { delta }`.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub enum InputPrimitive {
9    Rotate { delta: f64 },
10    Press,
11    Release,
12    LongPress,
13    Swipe { direction: Direction },
14    Slide { value: f64 },
15    Hover { proximity: f64 },
16    Touch { area: TouchArea },
17    LongTouch { area: TouchArea },
18    KeyPress { key: u32 },
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum Direction {
24    Up,
25    Down,
26    Left,
27    Right,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "snake_case")]
32pub enum TouchArea {
33    Top,
34    Bottom,
35    Left,
36    Right,
37}
38
39/// Service-level intent produced by the routing engine. Adapters translate
40/// this into their service's native command.
41#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42#[serde(tag = "type", rename_all = "snake_case")]
43pub enum Intent {
44    Play,
45    Pause,
46    PlayPause,
47    Stop,
48    Next,
49    Previous,
50    VolumeChange { delta: f64 },
51    VolumeSet { value: f64 },
52    Mute,
53    Unmute,
54    SeekRelative { seconds: f64 },
55    SeekAbsolute { seconds: f64 },
56    BrightnessChange { delta: f64 },
57    BrightnessSet { value: f64 },
58    ColorTemperatureChange { delta: f64 },
59    PowerToggle,
60    PowerOn,
61    PowerOff,
62}
63
64impl InputPrimitive {
65    /// Match this primitive against a wire-format route input string
66    /// (e.g. "rotate", "press", "swipe_right", "touch_top").
67    pub fn matches_route(&self, route_input: &str) -> bool {
68        match (self, route_input) {
69            (InputPrimitive::Rotate { .. }, "rotate") => true,
70            (InputPrimitive::Press, "press") => true,
71            (InputPrimitive::Release, "release") => true,
72            (InputPrimitive::LongPress, "long_press") => true,
73            (InputPrimitive::Slide { .. }, "slide") => true,
74            (InputPrimitive::Hover { .. }, "hover") => true,
75            (InputPrimitive::Swipe { direction }, s) => matches!(
76                (direction, s),
77                (Direction::Up, "swipe_up")
78                    | (Direction::Down, "swipe_down")
79                    | (Direction::Left, "swipe_left")
80                    | (Direction::Right, "swipe_right")
81            ),
82            (InputPrimitive::Touch { area }, s) => matches!(
83                (area, s),
84                (TouchArea::Top, "touch_top")
85                    | (TouchArea::Bottom, "touch_bottom")
86                    | (TouchArea::Left, "touch_left")
87                    | (TouchArea::Right, "touch_right")
88            ),
89            (InputPrimitive::LongTouch { area }, s) => matches!(
90                (area, s),
91                (TouchArea::Top, "long_touch_top")
92                    | (TouchArea::Bottom, "long_touch_bottom")
93                    | (TouchArea::Left, "long_touch_left")
94                    | (TouchArea::Right, "long_touch_right")
95            ),
96            _ => false,
97        }
98    }
99
100    /// Extract the continuous value for a rotate/slide, if applicable.
101    pub fn continuous_value(&self) -> Option<f64> {
102        match self {
103            InputPrimitive::Rotate { delta } | InputPrimitive::Slide { value: delta } => {
104                Some(*delta)
105            }
106            _ => None,
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn rotate_matches_rotate_route() {
117        let r = InputPrimitive::Rotate { delta: 0.03 };
118        assert!(r.matches_route("rotate"));
119        assert!(!r.matches_route("press"));
120    }
121
122    #[test]
123    fn swipe_direction_matters() {
124        let s = InputPrimitive::Swipe {
125            direction: Direction::Right,
126        };
127        assert!(s.matches_route("swipe_right"));
128        assert!(!s.matches_route("swipe_left"));
129        assert!(!s.matches_route("swipe_up"));
130    }
131}