Skip to content

Commit a1550ee

Browse files
ids1024jackpot51
authored andcommitted
improv: Refactor daemon code using macro to reduce redundance
Macro magic like this is potentially confusing, but arguably it's no less confusing to have to manually add each new method in various places. Now it only has to be added to the commands! call, and the server. So dark sorcery, but it should be easier to work with ultimately. (Hail Cthulhu!)
1 parent 24896b8 commit a1550ee

File tree

3 files changed

+75
-118
lines changed

3 files changed

+75
-118
lines changed

src/daemon/client.rs

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use serde::de::DeserializeOwned;
21
use std::{
32
io::{
43
BufRead,
@@ -8,12 +7,11 @@ use std::{
87
},
98
};
109

11-
use crate::color::Rgb;
1210
use super::{
1311
err_str,
14-
Daemon,
12+
DaemonClientTrait,
1513
DaemonCommand,
16-
DaemonResult,
14+
DaemonResponse,
1715
};
1816

1917
pub struct DaemonClient<R: Read, W: Write> {
@@ -28,60 +26,22 @@ impl<R: Read, W: Write> DaemonClient<R, W> {
2826
write,
2927
}
3028
}
29+
}
3130

32-
fn command<T: DeserializeOwned>(&mut self, command: DaemonCommand) -> Result<T, String> {
31+
impl<R: std::io::Read, W: std::io::Write> DaemonClientTrait for DaemonClient<R, W> {
32+
fn send_command(&mut self, command: DaemonCommand) -> Result<DaemonResponse, String> {
3333
let mut command_json = serde_json::to_string(&command).map_err(err_str)?;
3434
command_json.push('\n');
3535
self.write.write_all(command_json.as_bytes()).map_err(err_str)?;
3636

37-
let mut result_json = String::new();
38-
self.read.read_line(&mut result_json).map_err(err_str)?;
39-
let result = serde_json::from_str::<DaemonResult>(&result_json).map_err(err_str)?;
40-
match result {
41-
DaemonResult::Ok { ok } => {
42-
serde_json::from_reader(ok.as_bytes()).map_err(err_str)
43-
},
44-
DaemonResult::Err { err } => Err(err),
45-
}
46-
}
47-
}
48-
49-
impl<R: Read, W: Write> Daemon for DaemonClient<R, W> {
50-
fn boards(&mut self) -> Result<Vec<String>, String> {
51-
self.command(DaemonCommand::Boards)
52-
}
53-
54-
fn keymap_get(&mut self, board: usize, layer: u8, output: u8, input: u8) -> Result<u16, String> {
55-
self.command(DaemonCommand::KeymapGet { board, layer, output, input })
56-
}
57-
58-
fn keymap_set(&mut self, board: usize, layer: u8, output: u8, input: u8, value: u16) -> Result<(), String> {
59-
self.command(DaemonCommand::KeymapSet { board, layer, output, input, value })
60-
}
61-
62-
fn color(&mut self) -> Result<Rgb, String> {
63-
self.command(DaemonCommand::Color)
64-
}
65-
66-
fn set_color(&mut self, color: Rgb) -> Result<(), String> {
67-
self.command(DaemonCommand::SetColor { color })
68-
}
69-
70-
fn max_brightness(&mut self) -> Result<i32, String> {
71-
self.command(DaemonCommand::MaxBrightness)
72-
}
73-
74-
fn brightness(&mut self) -> Result<i32, String> {
75-
self.command(DaemonCommand::Brightness)
76-
}
77-
78-
fn set_brightness(&mut self, brightness: i32) -> Result<(), String> {
79-
self.command(DaemonCommand::SetBrightness { brightness })
37+
let mut response_json = String::new();
38+
self.read.read_line(&mut response_json).map_err(err_str)?;
39+
serde_json::from_str(&response_json).map_err(err_str)?
8040
}
8141
}
8242

8343
impl<R: Read, W: Write> Drop for DaemonClient<R, W> {
8444
fn drop(&mut self) {
85-
let _ = self.command::<()>(DaemonCommand::Exit);
45+
let _ = self.send_command(DaemonCommand::exit{});
8646
}
8747
}

src/daemon/mod.rs

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,63 @@ mod client;
99
pub use self::server::DaemonServer;
1010
mod server;
1111

12-
pub trait Daemon {
12+
pub trait DaemonClientTrait {
13+
fn send_command(&mut self, command: DaemonCommand) -> Result<DaemonResponse, String>;
14+
}
15+
16+
// Define Daemon trait, DaemonCommand enum, and DaemonResponse enum
17+
macro_rules! commands {
18+
( $( fn $func:ident(&mut self $(,)? $( $arg:ident: $type:ty ),*) -> Result<$ret:ty, String>; )* ) => {
19+
pub trait Daemon {
20+
$(
21+
fn $func(&mut self, $( $arg: $type ),*) -> Result<$ret, String>;
22+
)*
23+
24+
fn dispatch_command_to_method(&mut self, command: DaemonCommand) -> Result<DaemonResponse, String> {
25+
match command {
26+
$(
27+
DaemonCommand::$func{$( $arg ),*} => {
28+
self.$func($( $arg ),*).map(DaemonResponse::$func)
29+
}
30+
)*
31+
}
32+
}
33+
}
34+
35+
#[allow(non_camel_case_types)]
36+
#[derive(Deserialize, Serialize)]
37+
#[serde(tag = "t", content = "c")]
38+
pub enum DaemonCommand {
39+
$(
40+
$func{$( $arg: $type ),*}
41+
),*
42+
}
43+
44+
#[allow(non_camel_case_types)]
45+
#[derive(Deserialize, Serialize)]
46+
#[serde(tag = "t", content = "c")]
47+
pub enum DaemonResponse {
48+
$(
49+
$func($ret)
50+
),*
51+
}
52+
53+
impl<T: DaemonClientTrait> Daemon for T {
54+
$(
55+
fn $func(&mut self, $( $arg: $type ),*) -> Result<$ret, String> {
56+
let res = self.send_command(DaemonCommand::$func{$( $arg ),*});
57+
match res {
58+
Ok(DaemonResponse::$func(ret)) => Ok(ret),
59+
Ok(_) => unreachable!(),
60+
Err(err) => Err(err),
61+
}
62+
}
63+
)*
64+
}
65+
};
66+
}
67+
68+
commands! {
1369
fn boards(&mut self) -> Result<Vec<String>, String>;
1470
fn keymap_get(&mut self, board: usize, layer: u8, output: u8, input: u8) -> Result<u16, String>;
1571
fn keymap_set(&mut self, board: usize, layer: u8, output: u8, input: u8, value: u16) -> Result<(), String>;
@@ -18,33 +74,13 @@ pub trait Daemon {
1874
fn max_brightness(&mut self) -> Result<i32, String>;
1975
fn brightness(&mut self) -> Result<i32, String>;
2076
fn set_brightness(&mut self, brightness: i32) -> Result<(), String>;
77+
fn exit(&mut self) -> Result<(), String>;
2178
}
2279

2380
fn err_str<E: std::fmt::Debug>(err: E) -> String {
2481
format!("{:?}", err)
2582
}
2683

27-
#[derive(Deserialize, Serialize)]
28-
#[serde(tag = "kind")]
29-
enum DaemonCommand {
30-
Boards,
31-
KeymapGet { board: usize, layer: u8, output: u8, input: u8 },
32-
KeymapSet { board: usize, layer: u8, output: u8, input: u8, value: u16 },
33-
MaxBrightness,
34-
Brightness,
35-
Color,
36-
SetBrightness { brightness: i32 },
37-
SetColor { color: Rgb },
38-
Exit,
39-
}
40-
41-
#[derive(Deserialize, Serialize)]
42-
#[serde(untagged)]
43-
enum DaemonResult {
44-
Ok { ok: String },
45-
Err { err: String },
46-
}
47-
4884
pub fn daemon_server() -> Result<DaemonServer<io::Stdin, io::Stdout>, String> {
4985
DaemonServer::new(io::stdin(), io::stdout())
5086
}

src/daemon/server.rs

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use ectool::{Access, AccessHid, Ec};
22
#[cfg(target_os = "linux")]
33
use ectool::AccessLpcLinux;
44
use hidapi::HidApi;
5-
use serde::Serialize;
65
use std::{
76
fs,
87
io::{
@@ -12,6 +11,7 @@ use std::{
1211
Read,
1312
Write,
1413
},
14+
process,
1515
str,
1616
time::Duration,
1717
};
@@ -21,7 +21,6 @@ use super::{
2121
err_str,
2222
Daemon,
2323
DaemonCommand,
24-
DaemonResult,
2524
};
2625

2726
pub struct DaemonServer<R: Read, W: Write> {
@@ -101,58 +100,16 @@ impl<R: Read, W: Write> DaemonServer<R, W> {
101100
})
102101
}
103102

104-
fn command(&mut self, command_json: &str) -> Result<String, String> {
105-
fn json<T: Serialize>(value: T) -> Result<String, String> {
106-
serde_json::to_string(&value).map_err(err_str)
107-
}
108-
109-
let command = serde_json::from_str::<DaemonCommand>(&command_json).map_err(err_str)?;
110-
match command {
111-
DaemonCommand::Boards => {
112-
json(self.boards()?)
113-
},
114-
DaemonCommand::KeymapGet { board, layer, output, input } => {
115-
json(self.keymap_get(board, layer, output, input)?)
116-
},
117-
DaemonCommand::KeymapSet { board, layer, output, input, value } => {
118-
json(self.keymap_set(board, layer, output, input, value)?)
119-
},
120-
DaemonCommand::Color => {
121-
json(self.color()?)
122-
}
123-
DaemonCommand::SetColor { color } => {
124-
self.set_color(color)?;
125-
json(())
126-
}
127-
DaemonCommand::MaxBrightness => {
128-
json(self.max_brightness()?)
129-
}
130-
DaemonCommand::Brightness => {
131-
json(self.brightness()?)
132-
}
133-
DaemonCommand::SetBrightness { brightness } => {
134-
self.set_brightness(brightness)?;
135-
json(())
136-
}
137-
DaemonCommand::Exit => {
138-
self.running = false;
139-
json(())
140-
},
141-
}
142-
}
143-
144103
pub fn run(mut self) -> io::Result<()> {
145104
while self.running {
146105
let mut command_json = String::new();
147106
self.read.read_line(&mut command_json)?;
148107

149-
let result = match self.command(&command_json) {
150-
Ok(ok) => DaemonResult::Ok { ok },
151-
Err(err) => DaemonResult::Err { err },
152-
};
108+
let command = serde_json::from_str::<DaemonCommand>(&command_json).expect("failed to deserialize command");
109+
let response = self.dispatch_command_to_method(command);
153110

154111
//TODO: what to do if we fail to serialize result?
155-
let mut result_json = serde_json::to_string(&result).expect("failed to serialize result");
112+
let mut result_json = serde_json::to_string(&response).expect("failed to serialize result");
156113
result_json.push('\n');
157114
self.write.write_all(result_json.as_bytes())?;
158115
}
@@ -291,4 +248,8 @@ impl<R: Read, W: Write> Daemon for DaemonServer<R, W> {
291248
Err(err) => Err(format!("Failed to write keyboard brightness: {}", err))
292249
}
293250
}
251+
252+
fn exit(&mut self) -> Result<(), String> {
253+
process::exit(0);
254+
}
294255
}

0 commit comments

Comments
 (0)