cog_task/action/core/
key_logger.rs1use 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}