gardesk/garcalc / bf84d7c

Browse files

add garcalcctl IPC control utility

Authored by espadonne
SHA
bf84d7cce085b761b0677d6ce3a81f8a157a00a1
Parents
8905be0
Tree
417de8e

2 changed files

StatusFile+-
A garcalcctl/Cargo.toml 18 0
A garcalcctl/src/main.rs 105 0
garcalcctl/Cargo.tomladded
@@ -0,0 +1,18 @@
1
+[package]
2
+name = "garcalcctl"
3
+version.workspace = true
4
+edition.workspace = true
5
+authors.workspace = true
6
+license.workspace = true
7
+description = "Control utility for garcalc"
8
+
9
+[[bin]]
10
+name = "garcalcctl"
11
+path = "src/main.rs"
12
+
13
+[dependencies]
14
+garcalc-ipc = { workspace = true }
15
+clap = { workspace = true }
16
+anyhow = { workspace = true }
17
+serde_json = { workspace = true }
18
+tokio = { workspace = true }
garcalcctl/src/main.rsadded
@@ -0,0 +1,105 @@
1
+//! garcalcctl - Control utility for garcalc
2
+//!
3
+//! Sends commands to the running garcalc daemon via IPC.
4
+
5
+use std::io::{BufRead, BufReader, Write};
6
+use std::os::unix::net::UnixStream;
7
+
8
+use anyhow::{Context, Result};
9
+use clap::{Parser, Subcommand};
10
+use garcalc_ipc::{socket_path, Command, Mode, Response};
11
+
12
+#[derive(Parser)]
13
+#[command(name = "garcalcctl")]
14
+#[command(about = "Control utility for garcalc")]
15
+#[command(version)]
16
+struct Args {
17
+    #[command(subcommand)]
18
+    command: CtlCommand,
19
+}
20
+
21
+#[derive(Subcommand)]
22
+enum CtlCommand {
23
+    /// Show the calculator window
24
+    Show,
25
+    /// Hide the calculator window
26
+    Hide,
27
+    /// Toggle window visibility
28
+    Toggle,
29
+    /// Evaluate an expression
30
+    Eval {
31
+        /// Expression to evaluate
32
+        expr: String,
33
+    },
34
+    /// Get or set calculator mode
35
+    Mode {
36
+        /// Mode to set (calculator, graph, geometry, spreadsheet, notes)
37
+        #[arg(value_name = "MODE")]
38
+        mode: Option<String>,
39
+    },
40
+    /// Get daemon status
41
+    Status,
42
+    /// Quit the daemon
43
+    Quit,
44
+}
45
+
46
+fn main() -> Result<()> {
47
+    let args = Args::parse();
48
+
49
+    let command = match args.command {
50
+        CtlCommand::Show => Command::Show,
51
+        CtlCommand::Hide => Command::Hide,
52
+        CtlCommand::Toggle => Command::Toggle,
53
+        CtlCommand::Eval { expr } => Command::Evaluate { expr },
54
+        CtlCommand::Mode { mode: None } => Command::GetMode,
55
+        CtlCommand::Mode { mode: Some(m) } => {
56
+            let mode = match m.to_lowercase().as_str() {
57
+                "calc" | "calculator" => Mode::Calculator,
58
+                "graph" | "graphing" => Mode::Graph,
59
+                "geo" | "geometry" => Mode::Geometry,
60
+                "sheet" | "spreadsheet" => Mode::Spreadsheet,
61
+                "notes" | "note" => Mode::Notes,
62
+                _ => {
63
+                    eprintln!("Unknown mode: {m}");
64
+                    eprintln!("Valid modes: calculator, graph, geometry, spreadsheet, notes");
65
+                    std::process::exit(1);
66
+                }
67
+            };
68
+            Command::SetMode { mode }
69
+        }
70
+        CtlCommand::Status => Command::Status,
71
+        CtlCommand::Quit => Command::Quit,
72
+    };
73
+
74
+    let response = send_command(&command)?;
75
+
76
+    if response.success {
77
+        if let Some(data) = response.data {
78
+            println!("{}", serde_json::to_string_pretty(&data)?);
79
+        }
80
+    } else if let Some(error) = response.error {
81
+        eprintln!("Error: {error}");
82
+        std::process::exit(1);
83
+    }
84
+
85
+    Ok(())
86
+}
87
+
88
+fn send_command(command: &Command) -> Result<Response> {
89
+    let socket = socket_path();
90
+    let mut stream = UnixStream::connect(&socket)
91
+        .with_context(|| format!("Failed to connect to garcalc at {}", socket.display()))?;
92
+
93
+    let json = serde_json::to_string(command)?;
94
+    writeln!(stream, "{json}")?;
95
+    stream.flush()?;
96
+
97
+    let mut reader = BufReader::new(stream);
98
+    let mut response_line = String::new();
99
+    reader.read_line(&mut response_line)?;
100
+
101
+    let response: Response = serde_json::from_str(&response_line)
102
+        .context("Failed to parse response from garcalc")?;
103
+
104
+    Ok(response)
105
+}