1use std::{io::Cursor, net::SocketAddr};
7
8use async_trait::async_trait;
9use image::{io::Reader as ImageReader, DynamicImage};
10use reqwest::Client;
11use serde::{Deserialize, Serialize};
12use strum::Display;
13use tracing::debug;
14
15use crate::GenericHandle;
16
17#[derive(Clone, Copy, PartialEq, Debug, Display)]
19#[strum(serialize_all = "kebab-case")]
20pub enum Button {
21 Left,
22 Right,
23 Both,
24}
25
26#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Display)]
28#[serde(rename_all = "kebab-case")]
29pub enum Action {
30 Press,
31 Release,
32 PressAndRelease,
33}
34
35#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
37struct ButtonAction {
38 pub action: Action,
39}
40
41#[async_trait]
43pub trait Handle {
44 fn addr(&self) -> SocketAddr;
46
47 async fn button(&self, button: Button, action: Action) -> anyhow::Result<()> {
49 debug!("Sending button request: {}:{}", button, action);
50
51 let r = Client::new()
53 .post(format!("http://{}/button/{}", self.addr(), button))
54 .json(&ButtonAction { action })
55 .send()
56 .await?;
57
58 debug!("Button request complete: {}", r.status());
59
60 Ok(())
61 }
62
63 async fn screenshot(&self) -> anyhow::Result<DynamicImage> {
65 let r = reqwest::get(format!("http://{}/screenshot", self.addr())).await?;
67
68 let b = r.bytes().await?;
70
71 let i = ImageReader::new(Cursor::new(b))
73 .with_guessed_format()?
74 .decode()?;
75
76 Ok(i)
77 }
78}
79
80impl Handle for GenericHandle {
81 fn addr(&self) -> SocketAddr {
82 match self {
83 GenericHandle::Local(h) => h.addr(),
84 GenericHandle::Docker(h) => h.addr(),
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
95 fn button_encoding() {
96 let tests = &[
97 (Button::Left, "left"),
98 (Button::Right, "right"),
99 (Button::Both, "both"),
100 ];
101
102 for (v, s) in tests {
103 assert_eq!(&v.to_string(), s);
104 }
105 }
106
107 #[test]
109 fn action_encoding() {
110 let tests = &[
111 (
112 ButtonAction {
113 action: Action::Press,
114 },
115 r#"{"action":"press"}"#,
116 ),
117 (
118 ButtonAction {
119 action: Action::Release,
120 },
121 r#"{"action":"release"}"#,
122 ),
123 (
124 ButtonAction {
125 action: Action::PressAndRelease,
126 },
127 r#"{"action":"press-and-release"}"#,
128 ),
129 ];
130
131 for (v, s) in tests {
132 assert_eq!(&serde_json::to_string(v).unwrap(), s);
133 }
134 }
135}