#![allow(dead_code, unused_imports, unused_variables)]
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use tracing::{debug, info, warn};
use super::{ActionRateLimiter, MovementProfile};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MouseButton {
Left,
Right,
Middle,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Point {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
}
pub struct MouseController {
rate_limiter: ActionRateLimiter,
movement_profile: MovementProfile,
}
impl MouseController {
pub fn new() -> Self {
Self {
rate_limiter: ActionRateLimiter::default(),
movement_profile: MovementProfile::default(),
}
}
pub fn with_movement_profile(mut self, profile: MovementProfile) -> Self {
self.movement_profile = profile;
self
}
pub async fn move_to(&self, x: i32, y: i32) -> Result<()> {
if !self.rate_limiter.check() {
bail!("Mouse action rate limit exceeded");
}
self.validate_coordinates(x, y)?;
debug!("Mouse move to ({}, {})", x, y);
#[cfg(target_os = "macos")]
{
use tokio::process::Command;
let script = format!(
"tell application \"System Events\" to set position of mouse to {{{}, {}}}",
x, y
);
info!(
"Mouse move to ({}, {}) — requires Accessibility permissions",
x, y
);
}
Ok(())
}
pub async fn click(&self, button: MouseButton) -> Result<()> {
if !self.rate_limiter.check() {
bail!("Mouse action rate limit exceeded");
}
debug!("Mouse click: {:?}", button);
Ok(())
}
pub async fn double_click(&self) -> Result<()> {
if !self.rate_limiter.check() {
bail!("Mouse action rate limit exceeded");
}
debug!("Mouse double click");
Ok(())
}
pub async fn click_at(&self, x: i32, y: i32, button: MouseButton) -> Result<()> {
self.move_to(x, y).await?;
self.click(button).await
}
pub async fn scroll(&self, delta_x: i32, delta_y: i32) -> Result<()> {
if !self.rate_limiter.check() {
bail!("Mouse action rate limit exceeded");
}
debug!("Mouse scroll: dx={}, dy={}", delta_x, delta_y);
Ok(())
}
pub async fn drag(&self, from: Point, to: Point, button: MouseButton) -> Result<()> {
if !self.rate_limiter.check() {
bail!("Mouse action rate limit exceeded");
}
self.validate_coordinates(from.x, from.y)?;
self.validate_coordinates(to.x, to.y)?;
debug!(
"Mouse drag from ({}, {}) to ({}, {})",
from.x, from.y, to.x, to.y
);
Ok(())
}
fn validate_coordinates(&self, x: i32, y: i32) -> Result<()> {
if x.abs() > 32768 || y.abs() > 32768 {
bail!(
"Mouse coordinates ({}, {}) exceed maximum screen bounds",
x,
y
);
}
Ok(())
}
}
impl Default for MouseController {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point() {
let p = Point::new(100, 200);
assert_eq!(p.x, 100);
assert_eq!(p.y, 200);
}
#[test]
fn test_validate_coordinates() {
let mouse = MouseController::new();
assert!(mouse.validate_coordinates(100, 200).is_ok());
assert!(mouse.validate_coordinates(-100, 200).is_ok());
assert!(mouse.validate_coordinates(50000, 200).is_err());
}
}