waterui_cli/workflows/
gesture.rs1use std::time::Duration;
7
8use crate::capture::{DevicePlatform, detect_platform};
9use crate::diff::DiffResult;
10use crate::toolchain::Host;
11use crate::{android, apple};
12
13pub const LOCAL_DEVICE_ID: &str = "local";
15
16#[derive(Debug, Clone, Default)]
18pub struct GestureOptions {
19 pub diff: bool,
21 pub diff_output: Option<std::path::PathBuf>,
23 pub delay_ms: Option<u32>,
26}
27
28impl GestureOptions {
29 #[must_use]
31 pub fn with_diff() -> Self {
32 Self {
33 diff: true,
34 ..Default::default()
35 }
36 }
37
38 #[must_use]
40 pub fn diff_output(mut self, path: impl Into<std::path::PathBuf>) -> Self {
41 self.diff_output = Some(path.into());
42 self
43 }
44
45 #[must_use]
47 pub const fn delay(mut self, ms: u32) -> Self {
48 self.delay_ms = Some(ms);
49 self
50 }
51}
52
53#[derive(Debug)]
55pub struct GestureResult {
56 pub diff: Option<DiffResult>,
58}
59
60async fn capture_screenshot_bytes(host: &Host, device_id: &str) -> eyre::Result<Vec<u8>> {
62 if device_id == LOCAL_DEVICE_ID {
63 apple::local::screenshot_bytes(host).await
64 } else {
65 match detect_platform(device_id) {
66 DevicePlatform::Ios => apple::device::screenshot_bytes(host, device_id).await,
67 DevicePlatform::Android => android::device::screenshot_bytes(host, device_id).await,
68 }
69 }
70}
71
72async fn execute_with_diff<F, Fut>(
74 host: &Host,
75 device_id: &str,
76 options: &GestureOptions,
77 gesture_fn: F,
78) -> eyre::Result<GestureResult>
79where
80 F: FnOnce() -> Fut,
81 Fut: std::future::Future<Output = eyre::Result<()>>,
82{
83 if !options.diff {
84 gesture_fn().await?;
86 return Ok(GestureResult { diff: None });
87 }
88
89 let before = capture_screenshot_bytes(host, device_id).await?;
91
92 gesture_fn().await?;
94
95 let delay = options.delay_ms.unwrap_or(500);
97 smol::Timer::after(Duration::from_millis(u64::from(delay))).await;
98
99 let after = capture_screenshot_bytes(host, device_id).await?;
101
102 let diff_result = crate::diff::compare_images(&before, &after)?;
104
105 if let Some(ref output_path) = options.diff_output {
107 crate::diff::save_diff_image(&before, &after, output_path)?;
108 }
109
110 Ok(GestureResult {
111 diff: Some(diff_result),
112 })
113}
114
115pub async fn tap(
128 host: &Host,
129 device_id: &str,
130 x: u32,
131 y: u32,
132 options: &GestureOptions,
133) -> eyre::Result<GestureResult> {
134 execute_with_diff(host, device_id, options, || async {
135 if device_id == LOCAL_DEVICE_ID {
136 apple::local::tap(host, x, y).await
137 } else {
138 match detect_platform(device_id) {
139 DevicePlatform::Ios => apple::device::tap(host, device_id, x, y).await,
140 DevicePlatform::Android => android::device::tap(host, device_id, x, y).await,
141 }
142 }
143 })
144 .await
145}
146
147pub async fn swipe(
161 host: &Host,
162 device_id: &str,
163 from: (u32, u32),
164 to: (u32, u32),
165 duration_ms: Option<u32>,
166 options: &GestureOptions,
167) -> eyre::Result<GestureResult> {
168 execute_with_diff(host, device_id, options, || async {
169 if device_id == LOCAL_DEVICE_ID {
170 apple::local::swipe(host, from, to, duration_ms).await
171 } else {
172 match detect_platform(device_id) {
173 DevicePlatform::Ios => {
174 apple::device::swipe(host, device_id, from, to, duration_ms).await
175 }
176 DevicePlatform::Android => {
177 android::device::swipe(host, device_id, from, to, duration_ms).await
178 }
179 }
180 }
181 })
182 .await
183}
184
185pub async fn text(
197 host: &Host,
198 device_id: &str,
199 input: &str,
200 options: &GestureOptions,
201) -> eyre::Result<GestureResult> {
202 execute_with_diff(host, device_id, options, || async {
203 if device_id == LOCAL_DEVICE_ID {
204 apple::local::text(host, input).await
205 } else {
206 match detect_platform(device_id) {
207 DevicePlatform::Ios => apple::device::text(host, device_id, input).await,
208 DevicePlatform::Android => android::device::text(host, device_id, input).await,
209 }
210 }
211 })
212 .await
213}
214
215pub async fn verify_device(host: &Host, device_id: &str) -> eyre::Result<DevicePlatform> {
221 if device_id == LOCAL_DEVICE_ID {
222 #[cfg(target_os = "macos")]
224 return Ok(DevicePlatform::Ios); #[cfg(not(target_os = "macos"))]
227 return Err(eyre::eyre!("Local device is only available on macOS"));
228 }
229
230 crate::capture::verify_device(host, device_id).await
231}