Rust · 3260 bytes Raw Blame History
1 use serde::{Deserialize, Serialize};
2 use std::path::PathBuf;
3
4 /// IPC command sent to garcalc daemon
5 #[derive(Debug, Clone, Serialize, Deserialize)]
6 #[serde(tag = "command", rename_all = "snake_case")]
7 pub enum Command {
8 /// Show the calculator window
9 Show,
10 /// Hide the calculator window
11 Hide,
12 /// Toggle window visibility
13 Toggle,
14 /// Evaluate an expression and return the result
15 Evaluate { expr: String },
16 /// Get current calculator mode
17 GetMode,
18 /// Set calculator mode
19 SetMode { mode: Mode },
20 /// Open a document file
21 OpenDocument { path: PathBuf },
22 /// Get daemon status
23 Status,
24 /// Quit the daemon
25 Quit,
26 }
27
28 /// Calculator operating modes
29 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30 #[serde(rename_all = "snake_case")]
31 pub enum Mode {
32 Calculator,
33 Graph,
34 Graph3D,
35 Geometry,
36 Spreadsheet,
37 Notes,
38 }
39
40 impl Default for Mode {
41 fn default() -> Self {
42 Self::Calculator
43 }
44 }
45
46 /// Response from garcalc daemon
47 #[derive(Debug, Clone, Serialize, Deserialize)]
48 pub struct Response {
49 pub success: bool,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub data: Option<ResponseData>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub error: Option<String>,
54 }
55
56 impl Response {
57 pub fn ok() -> Self {
58 Self {
59 success: true,
60 data: None,
61 error: None,
62 }
63 }
64
65 pub fn ok_with_data(data: ResponseData) -> Self {
66 Self {
67 success: true,
68 data: Some(data),
69 error: None,
70 }
71 }
72
73 pub fn err(message: impl Into<String>) -> Self {
74 Self {
75 success: false,
76 data: None,
77 error: Some(message.into()),
78 }
79 }
80 }
81
82 /// Response data variants
83 #[derive(Debug, Clone, Serialize, Deserialize)]
84 #[serde(tag = "type", rename_all = "snake_case")]
85 pub enum ResponseData {
86 /// Result of expression evaluation
87 Evaluation {
88 input: String,
89 result: String,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 exact: Option<String>,
92 },
93 /// Current mode
94 Mode { mode: Mode },
95 /// Daemon status
96 Status {
97 visible: bool,
98 mode: Mode,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 document: Option<String>,
101 history_count: usize,
102 },
103 }
104
105 /// Get the IPC socket path
106 pub fn socket_path() -> PathBuf {
107 dirs::runtime_dir()
108 .unwrap_or_else(|| PathBuf::from("/tmp"))
109 .join("garcalc.sock")
110 }
111
112 #[cfg(test)]
113 mod tests {
114 use super::*;
115
116 #[test]
117 fn test_command_serialization() {
118 let cmd = Command::Evaluate {
119 expr: "2+2".to_string(),
120 };
121 let json = serde_json::to_string(&cmd).unwrap();
122 assert!(json.contains("evaluate"));
123 assert!(json.contains("2+2"));
124 }
125
126 #[test]
127 fn test_response_serialization() {
128 let resp = Response::ok_with_data(ResponseData::Evaluation {
129 input: "2+2".to_string(),
130 result: "4".to_string(),
131 exact: None,
132 });
133 let json = serde_json::to_string(&resp).unwrap();
134 assert!(json.contains("\"success\":true"));
135 }
136 }
137