cog_task/action/core/
key_logger.rs

1use crate::action::{Action, ActionSignal, Props, StatefulAction, INFINITE};
2use crate::comm::{QWriter, Signal, SignalId};
3use crate::resource::{IoManager, LoggerSignal, OptionalString, ResourceManager};
4use crate::server::{AsyncSignal, Config, State, SyncSignal};
5use chrono::Local;
6use eyre::{eyre, Result};
7use serde::{Deserialize, Serialize};
8use serde_cbor::Value;
9use std::collections::BTreeSet;
10use std::time::Instant;
11
12#[derive(Debug, Deserialize, Serialize)]
13#[serde(deny_unknown_fields)]
14pub struct KeyLogger {
15    #[serde(default = "defaults::group")]
16    group: OptionalString,
17    #[serde(default)]
18    out_key: SignalId,
19}
20
21stateful!(KeyLogger {
22    group: Option<String>,
23    out_key: SignalId,
24});
25
26mod defaults {
27    use crate::resource::OptionalString;
28
29    #[inline(always)]
30    pub fn group() -> OptionalString {
31        OptionalString::Some("keypress".to_owned())
32    }
33}
34
35impl Action for KeyLogger {
36    fn out_signals(&self) -> BTreeSet<SignalId> {
37        BTreeSet::from([self.out_key])
38    }
39
40    fn stateful(
41        &self,
42        _io: &IoManager,
43        _res: &ResourceManager,
44        _config: &Config,
45        _sync_writer: &QWriter<SyncSignal>,
46        _async_writer: &QWriter<AsyncSignal>,
47    ) -> Result<Box<dyn StatefulAction>> {
48        if self.group.is_none() && self.out_key == 0 {
49            return Err(eyre!(
50                "Both `group` and `out_key` for KeyLogger cannot be empty simultaneously."
51            ));
52        }
53
54        let group = match &self.group {
55            OptionalString::Some(s) => Some(s.clone()),
56            OptionalString::None => None,
57        };
58
59        Ok(Box::new(StatefulKeyLogger {
60            done: false,
61            group,
62            out_key: self.out_key,
63        }))
64    }
65}
66
67impl StatefulAction for StatefulKeyLogger {
68    impl_stateful!();
69
70    #[inline(always)]
71    fn props(&self) -> Props {
72        INFINITE.into()
73    }
74
75    fn start(
76        &mut self,
77        _sync_writer: &mut QWriter<SyncSignal>,
78        async_writer: &mut QWriter<AsyncSignal>,
79        _state: &State,
80    ) -> Result<Signal> {
81        if let Some(group) = self.group.as_ref() {
82            async_writer.push(LoggerSignal::Append(
83                group.clone(),
84                ("event".to_owned(), Value::Text("start".to_owned())),
85            ));
86        }
87
88        Ok(Signal::none())
89    }
90
91    fn update(
92        &mut self,
93        signal: &ActionSignal,
94        sync_writer: &mut QWriter<SyncSignal>,
95        async_writer: &mut QWriter<AsyncSignal>,
96        _state: &State,
97    ) -> Result<Signal> {
98        if let ActionSignal::KeyPress(_, keys) = signal {
99            let entry = (
100                "key".to_string(),
101                Value::Array(keys.iter().map(|k| Value::Text(format!("{k:?}"))).collect()),
102            );
103
104            if self.out_key > 0 {
105                sync_writer.push(SyncSignal::Emit(
106                    Instant::now(),
107                    Signal::from(
108                        keys.iter()
109                            .map(|k| (self.out_key, Value::Text(format!("{k:?}"))))
110                            .collect::<Vec<_>>(),
111                    ),
112                ));
113            }
114
115            if let Some(group) = self.group.as_ref() {
116                async_writer.push(AsyncSignal::Logger(
117                    Local::now(),
118                    LoggerSignal::Append(group.clone(), entry),
119                ));
120            }
121        }
122
123        Ok(Signal::none())
124    }
125
126    #[inline]
127    fn stop(
128        &mut self,
129        _sync_writer: &mut QWriter<SyncSignal>,
130        async_writer: &mut QWriter<AsyncSignal>,
131        _state: &State,
132    ) -> Result<Signal> {
133        if let Some(group) = self.group.as_ref() {
134            async_writer.push(LoggerSignal::Append(
135                group.clone(),
136                ("event".to_owned(), Value::Text("stop".to_owned())),
137            ));
138        }
139        Ok(Signal::none())
140    }
141
142    fn debug(&self) -> Vec<(&str, String)> {
143        <dyn StatefulAction>::debug(self)
144            .into_iter()
145            .chain([("group", format!("{:?}", self.group))])
146            .collect()
147    }
148}