use std::{io::Cursor, net::SocketAddr};
use async_trait::async_trait;
use image::{io::Reader as ImageReader, DynamicImage};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use strum::Display;
use tracing::debug;
use crate::GenericHandle;
#[derive(Clone, Copy, PartialEq, Debug, Display)]
#[strum(serialize_all = "kebab-case")]
pub enum Button {
Left,
Right,
Both,
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, Display)]
#[serde(rename_all = "kebab-case")]
pub enum Action {
Press,
Release,
PressAndRelease,
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
struct ButtonAction {
pub action: Action,
}
#[async_trait]
pub trait Handle {
fn addr(&self) -> SocketAddr;
async fn button(&self, button: Button, action: Action) -> anyhow::Result<()> {
debug!("Sending button request: {}:{}", button, action);
let r = Client::new()
.post(format!("http://{}/button/{}", self.addr(), button))
.json(&ButtonAction { action })
.send()
.await?;
debug!("Button request complete: {}", r.status());
Ok(())
}
async fn screenshot(&self) -> anyhow::Result<DynamicImage> {
let r = reqwest::get(format!("http://{}/screenshot", self.addr())).await?;
let b = r.bytes().await?;
let i = ImageReader::new(Cursor::new(b))
.with_guessed_format()?
.decode()?;
Ok(i)
}
}
impl Handle for GenericHandle {
fn addr(&self) -> SocketAddr {
match self {
GenericHandle::Local(h) => h.addr(),
GenericHandle::Docker(h) => h.addr(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn button_encoding() {
let tests = &[
(Button::Left, "left"),
(Button::Right, "right"),
(Button::Both, "both"),
];
for (v, s) in tests {
assert_eq!(&v.to_string(), s);
}
}
#[test]
fn action_encoding() {
let tests = &[
(
ButtonAction {
action: Action::Press,
},
r#"{"action":"press"}"#,
),
(
ButtonAction {
action: Action::Release,
},
r#"{"action":"release"}"#,
),
(
ButtonAction {
action: Action::PressAndRelease,
},
r#"{"action":"press-and-release"}"#,
),
];
for (v, s) in tests {
assert_eq!(&serde_json::to_string(v).unwrap(), s);
}
}
}