Rust · 4199 bytes Raw Blame History
1 //! IPC protocol definitions for garlock
2 //!
3 //! JSON-based protocol for communication between garlock daemon and clients.
4
5 use serde::{Deserialize, Serialize};
6
7 /// Commands sent from clients to garlock daemon
8 #[derive(Debug, Clone, Serialize, Deserialize)]
9 #[serde(tag = "command", rename_all = "kebab-case")]
10 pub enum Command {
11 /// Lock the screen immediately
12 Lock,
13 /// Query current lock state
14 QueryState,
15 /// Gracefully shutdown the daemon
16 Shutdown,
17 }
18
19 /// Response from garlock daemon to clients
20 #[derive(Debug, Clone, Serialize, Deserialize)]
21 #[serde(tag = "status", rename_all = "kebab-case")]
22 pub enum Response {
23 /// Command executed successfully
24 Ok {
25 /// Optional message
26 #[serde(skip_serializing_if = "Option::is_none")]
27 message: Option<String>,
28 },
29 /// Current state information
30 State {
31 /// Whether screen is currently locked
32 locked: bool,
33 /// Number of failed authentication attempts (if locked)
34 #[serde(skip_serializing_if = "Option::is_none")]
35 failed_attempts: Option<u32>,
36 /// Whether in cooldown period
37 #[serde(skip_serializing_if = "Option::is_none")]
38 in_cooldown: Option<bool>,
39 },
40 /// Error occurred
41 Error {
42 /// Error message
43 message: String,
44 },
45 }
46
47 /// Events broadcast from garlock daemon to subscribers
48 #[derive(Debug, Clone, Serialize, Deserialize)]
49 #[serde(tag = "event", rename_all = "kebab-case")]
50 pub enum Event {
51 /// Screen was locked
52 Locked,
53 /// Screen was unlocked (successful auth)
54 Unlocked,
55 /// Authentication attempt failed
56 AuthFailed {
57 /// Current attempt number
58 attempt: u32,
59 },
60 /// Cooldown started after too many failures
61 CooldownStarted {
62 /// Cooldown duration in seconds
63 seconds: u64,
64 },
65 /// Daemon is shutting down
66 Shutdown,
67 }
68
69 impl Response {
70 /// Create a success response
71 pub fn ok() -> Self {
72 Self::Ok { message: None }
73 }
74
75 /// Create a success response with message
76 pub fn ok_with_message(msg: impl Into<String>) -> Self {
77 Self::Ok {
78 message: Some(msg.into()),
79 }
80 }
81
82 /// Create an error response
83 pub fn error(msg: impl Into<String>) -> Self {
84 Self::Error {
85 message: msg.into(),
86 }
87 }
88
89 /// Create a state response
90 pub fn state(locked: bool, failed_attempts: Option<u32>, in_cooldown: Option<bool>) -> Self {
91 Self::State {
92 locked,
93 failed_attempts,
94 in_cooldown,
95 }
96 }
97 }
98
99 #[cfg(test)]
100 mod tests {
101 use super::*;
102
103 #[test]
104 fn test_command_serialization() {
105 let cmd = Command::Lock;
106 let json = serde_json::to_string(&cmd).unwrap();
107 assert_eq!(json, r#"{"command":"lock"}"#);
108
109 let cmd = Command::QueryState;
110 let json = serde_json::to_string(&cmd).unwrap();
111 assert_eq!(json, r#"{"command":"query-state"}"#);
112 }
113
114 #[test]
115 fn test_command_deserialization() {
116 let cmd: Command = serde_json::from_str(r#"{"command":"lock"}"#).unwrap();
117 assert!(matches!(cmd, Command::Lock));
118
119 let cmd: Command = serde_json::from_str(r#"{"command":"query-state"}"#).unwrap();
120 assert!(matches!(cmd, Command::QueryState));
121 }
122
123 #[test]
124 fn test_response_serialization() {
125 let resp = Response::ok();
126 let json = serde_json::to_string(&resp).unwrap();
127 assert_eq!(json, r#"{"status":"ok"}"#);
128
129 let resp = Response::state(true, Some(2), Some(false));
130 let json = serde_json::to_string(&resp).unwrap();
131 assert!(json.contains(r#""locked":true"#));
132 assert!(json.contains(r#""failed_attempts":2"#));
133 }
134
135 #[test]
136 fn test_event_serialization() {
137 let event = Event::Locked;
138 let json = serde_json::to_string(&event).unwrap();
139 assert_eq!(json, r#"{"event":"locked"}"#);
140
141 let event = Event::AuthFailed { attempt: 3 };
142 let json = serde_json::to_string(&event).unwrap();
143 assert!(json.contains(r#""event":"auth-failed""#));
144 assert!(json.contains(r#""attempt":3"#));
145 }
146 }
147