use std::time::Duration;
use color_eyre::eyre;
use crate::capture::{DevicePlatform, detect_platform};
use crate::diff::DiffResult;
use crate::{android, apple};
pub const LOCAL_DEVICE_ID: &str = "local";
#[derive(Debug, Clone, Default)]
pub struct GestureOptions {
pub diff: bool,
pub diff_output: Option<std::path::PathBuf>,
pub delay_ms: Option<u32>,
}
impl GestureOptions {
#[must_use]
pub fn with_diff() -> Self {
Self {
diff: true,
..Default::default()
}
}
#[must_use]
pub fn diff_output(mut self, path: impl Into<std::path::PathBuf>) -> Self {
self.diff_output = Some(path.into());
self
}
#[must_use]
pub const fn delay(mut self, ms: u32) -> Self {
self.delay_ms = Some(ms);
self
}
}
#[derive(Debug)]
pub struct GestureResult {
pub diff: Option<DiffResult>,
}
async fn capture_screenshot_bytes(device_id: &str) -> eyre::Result<Vec<u8>> {
if device_id == LOCAL_DEVICE_ID {
apple::local::screenshot_bytes().await
} else {
match detect_platform(device_id) {
DevicePlatform::Ios => apple::device::screenshot_bytes(device_id).await,
DevicePlatform::Android => android::device::screenshot_bytes(device_id).await,
}
}
}
async fn execute_with_diff<F, Fut>(
device_id: &str,
options: &GestureOptions,
gesture_fn: F,
) -> eyre::Result<GestureResult>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = eyre::Result<()>>,
{
if !options.diff {
gesture_fn().await?;
return Ok(GestureResult { diff: None });
}
let before = capture_screenshot_bytes(device_id).await?;
gesture_fn().await?;
let delay = options.delay_ms.unwrap_or(500);
smol::Timer::after(Duration::from_millis(u64::from(delay))).await;
let after = capture_screenshot_bytes(device_id).await?;
let diff_result = crate::diff::compare_images(&before, &after)?;
if let Some(ref output_path) = options.diff_output {
crate::diff::save_diff_image(&before, &after, output_path)?;
}
Ok(GestureResult {
diff: Some(diff_result),
})
}
pub async fn tap(
device_id: &str,
x: u32,
y: u32,
options: &GestureOptions,
) -> eyre::Result<GestureResult> {
execute_with_diff(device_id, options, || async {
if device_id == LOCAL_DEVICE_ID {
apple::local::tap(x, y).await
} else {
match detect_platform(device_id) {
DevicePlatform::Ios => apple::device::tap(device_id, x, y).await,
DevicePlatform::Android => android::device::tap(device_id, x, y).await,
}
}
})
.await
}
pub async fn swipe(
device_id: &str,
from: (u32, u32),
to: (u32, u32),
duration_ms: Option<u32>,
options: &GestureOptions,
) -> eyre::Result<GestureResult> {
execute_with_diff(device_id, options, || async {
if device_id == LOCAL_DEVICE_ID {
apple::local::swipe(from, to, duration_ms).await
} else {
match detect_platform(device_id) {
DevicePlatform::Ios => apple::device::swipe(device_id, from, to, duration_ms).await,
DevicePlatform::Android => {
android::device::swipe(device_id, from, to, duration_ms).await
}
}
}
})
.await
}
pub async fn text(
device_id: &str,
input: &str,
options: &GestureOptions,
) -> eyre::Result<GestureResult> {
execute_with_diff(device_id, options, || async {
if device_id == LOCAL_DEVICE_ID {
apple::local::text(input).await
} else {
match detect_platform(device_id) {
DevicePlatform::Ios => apple::device::text(device_id, input).await,
DevicePlatform::Android => android::device::text(device_id, input).await,
}
}
})
.await
}
pub async fn verify_device(device_id: &str) -> eyre::Result<DevicePlatform> {
if device_id == LOCAL_DEVICE_ID {
#[cfg(target_os = "macos")]
return Ok(DevicePlatform::Ios);
#[cfg(not(target_os = "macos"))]
return Err(eyre::eyre!("Local device is only available on macOS"));
}
crate::capture::verify_device(device_id).await
}