diff --git a/Cargo.toml b/Cargo.toml index ce75023..66c72d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ members = ["crates/core", "crates/macos", "crates/windows", "crates/linux", "cr resolver = "2" [workspace.package] -edition = "2021" -rust-version = "1.78" +edition = "2024" +rust-version = "1.85" license = "Apache-2.0" version = "0.1.14" # x-release-please-version @@ -18,8 +18,29 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } base64 = "0.22" rustc-hash = "2.1" libc = "0.2" +smallvec = { version = "1.13", features = ["serde", "union"] } agent-desktop-core = { path = "crates/core" } +[workspace.lints.rust] +unsafe_op_in_unsafe_fn = "warn" +unreachable_pub = "allow" +unused_must_use = "deny" +let_underscore_drop = "allow" + +[workspace.lints.clippy] +dbg_macro = "deny" +todo = "warn" +needless_collect = "warn" +redundant_clone = "allow" +unwrap_used = "allow" +expect_used = "allow" +needless_pass_by_value = "allow" +manual_let_else = "allow" +items_after_statements = "allow" +collapsible_if = "allow" +print_stdout = "allow" +print_stderr = "allow" + [profile.release] opt-level = "z" lto = true @@ -27,8 +48,6 @@ codegen-units = 1 strip = true panic = "abort" -# Fast build for CI: skip LTO and size optimisations, keep debug info for -# better test failure messages. Inherits release settings not listed here. [profile.ci] inherits = "release" opt-level = 1 @@ -37,9 +56,6 @@ codegen-units = 16 strip = false debug = 1 -# Dedicated profile for the FFI cdylib. Overrides panic = "abort" so -# catch_unwind actually catches at the extern "C" boundary — the CLI -# release profile stays abort for size reasons. [profile.release-ffi] inherits = "release" panic = "unwind" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index becfa7d..45dd81d 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -12,3 +12,7 @@ tracing.workspace = true rustc-hash.workspace = true base64.workspace = true libc.workspace = true +smallvec.workspace = true + +[lints] +workspace = true diff --git a/crates/core/src/adapter.rs b/crates/core/src/adapter.rs index 18f47e4..4bf04db 100644 --- a/crates/core/src/adapter.rs +++ b/crates/core/src/adapter.rs @@ -1,4 +1,5 @@ use crate::{ + PermissionReport, PermissionState, action::{ ActionRequest, ActionResult, DragParams, ElementState, KeyCombo, MouseEvent, WindowOp, }, @@ -6,7 +7,6 @@ use crate::{ node::{AccessibilityNode, AppInfo, Rect, SurfaceInfo, WindowInfo}, notification::{NotificationFilter, NotificationIdentity, NotificationInfo}, refs::RefEntry, - PermissionReport, PermissionState, }; use std::marker::PhantomData; diff --git a/crates/core/src/commands/check.rs b/crates/core/src/commands/check.rs index 44e9c0c..e682d6b 100644 --- a/crates/core/src/commands/check.rs +++ b/crates/core/src/commands/check.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/clear.rs b/crates/core/src/commands/clear.rs index 5a5c4df..dee80ee 100644 --- a/crates/core/src/commands/clear.rs +++ b/crates/core/src/commands/clear.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/click.rs b/crates/core/src/commands/click.rs index 4d2fb3d..7e26f3c 100644 --- a/crates/core/src/commands/click.rs +++ b/crates/core/src/commands/click.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/clipboard_clear.rs b/crates/core/src/commands/clipboard_clear.rs index f676dec..1e59be9 100644 --- a/crates/core/src/commands/clipboard_clear.rs +++ b/crates/core/src/commands/clipboard_clear.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub fn execute(adapter: &dyn PlatformAdapter) -> Result { adapter.clear_clipboard()?; diff --git a/crates/core/src/commands/clipboard_get.rs b/crates/core/src/commands/clipboard_get.rs index 3869fcd..d7b6a89 100644 --- a/crates/core/src/commands/clipboard_get.rs +++ b/crates/core/src/commands/clipboard_get.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub fn execute(adapter: &dyn PlatformAdapter) -> Result { let text = adapter.get_clipboard()?; diff --git a/crates/core/src/commands/clipboard_set.rs b/crates/core/src/commands/clipboard_set.rs index 9da9352..f25ab94 100644 --- a/crates/core/src/commands/clipboard_set.rs +++ b/crates/core/src/commands/clipboard_set.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub fn execute(text: String, adapter: &dyn PlatformAdapter) -> Result { adapter.set_clipboard(&text)?; diff --git a/crates/core/src/commands/close_app.rs b/crates/core/src/commands/close_app.rs index 33c9700..ef28932 100644 --- a/crates/core/src/commands/close_app.rs +++ b/crates/core/src/commands/close_app.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; const PROTECTED_PROCESSES: &[&str] = &["loginwindow", "windowserver", "dock", "launchd", "finder"]; diff --git a/crates/core/src/commands/collapse.rs b/crates/core/src/commands/collapse.rs index 5413026..e0a2318 100644 --- a/crates/core/src/commands/collapse.rs +++ b/crates/core/src/commands/collapse.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/dismiss_all_notifications.rs b/crates/core/src/commands/dismiss_all_notifications.rs index 06a3b89..c978d2b 100644 --- a/crates/core/src/commands/dismiss_all_notifications.rs +++ b/crates/core/src/commands/dismiss_all_notifications.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct DismissAllNotificationsArgs { pub app: Option, diff --git a/crates/core/src/commands/dismiss_notification.rs b/crates/core/src/commands/dismiss_notification.rs index 76400d0..3a503a1 100644 --- a/crates/core/src/commands/dismiss_notification.rs +++ b/crates/core/src/commands/dismiss_notification.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct DismissNotificationArgs { pub index: usize, diff --git a/crates/core/src/commands/double_click.rs b/crates/core/src/commands/double_click.rs index 7e61e2d..1e70bad 100644 --- a/crates/core/src/commands/double_click.rs +++ b/crates/core/src/commands/double_click.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/drag.rs b/crates/core/src/commands/drag.rs index 5a0e680..21c53a8 100644 --- a/crates/core/src/commands/drag.rs +++ b/crates/core/src/commands/drag.rs @@ -4,7 +4,7 @@ use crate::{ commands::helpers::resolve_ref, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct DragArgs { pub from_ref: Option, diff --git a/crates/core/src/commands/expand.rs b/crates/core/src/commands/expand.rs index 0f6bd5b..4d4a65f 100644 --- a/crates/core/src/commands/expand.rs +++ b/crates/core/src/commands/expand.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/find.rs b/crates/core/src/commands/find.rs index 97eb266..adfdb5f 100644 --- a/crates/core/src/commands/find.rs +++ b/crates/core/src/commands/find.rs @@ -2,7 +2,7 @@ use crate::{ adapter::PlatformAdapter, commands::search_text, error::AppError, node::AccessibilityNode, snapshot, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; const DEFAULT_LIMIT: usize = 50; diff --git a/crates/core/src/commands/focus.rs b/crates/core/src/commands/focus.rs index b8c4f9e..d4c59d7 100644 --- a/crates/core/src/commands/focus.rs +++ b/crates/core/src/commands/focus.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/focus_window.rs b/crates/core/src/commands/focus_window.rs index 1ba4cb1..4fd801d 100644 --- a/crates/core/src/commands/focus_window.rs +++ b/crates/core/src/commands/focus_window.rs @@ -3,7 +3,7 @@ use crate::{ error::{AdapterError, AppError, ErrorCode}, node::WindowInfo, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::time::{Duration, Instant}; #[cfg(not(test))] diff --git a/crates/core/src/commands/get.rs b/crates/core/src/commands/get.rs index 956470c..f3aec97 100644 --- a/crates/core/src/commands/get.rs +++ b/crates/core/src/commands/get.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, commands::helpers::resolve_ref, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct GetArgs { pub ref_id: String, diff --git a/crates/core/src/commands/helpers.rs b/crates/core/src/commands/helpers.rs index d68a257..d1de783 100644 --- a/crates/core/src/commands/helpers.rs +++ b/crates/core/src/commands/helpers.rs @@ -8,7 +8,7 @@ use crate::{ refs_store::RefStore, window_lookup, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct AppArgs { pub app: Option, @@ -137,8 +137,8 @@ mod tests { use crate::node::AppInfo; use crate::refs::RefMap; use crate::refs_test_support::HomeGuard; - use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Mutex; + use std::sync::atomic::{AtomicU32, Ordering}; struct ReleaseCountingAdapter { releases: AtomicU32, @@ -215,7 +215,7 @@ mod tests { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), } } diff --git a/crates/core/src/commands/hover.rs b/crates/core/src/commands/hover.rs index a5097f6..6eaab1f 100644 --- a/crates/core/src/commands/hover.rs +++ b/crates/core/src/commands/hover.rs @@ -4,7 +4,7 @@ use crate::{ commands::helpers::resolve_ref, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct HoverArgs { pub ref_id: Option, diff --git a/crates/core/src/commands/is_check.rs b/crates/core/src/commands/is_check.rs index 13a0481..73aa5ad 100644 --- a/crates/core/src/commands/is_check.rs +++ b/crates/core/src/commands/is_check.rs @@ -2,7 +2,7 @@ use crate::{ action::ElementState, adapter::PlatformAdapter, commands::helpers::resolve_ref, error::AppError, refs::RefEntry, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct IsArgs { pub ref_id: String, @@ -130,7 +130,7 @@ mod tests { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), } } @@ -252,7 +252,7 @@ mod tests { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); let adapter = LiveStateAdapter { state: Mutex::new(None), diff --git a/crates/core/src/commands/key_down.rs b/crates/core/src/commands/key_down.rs index b3f192e..5a19333 100644 --- a/crates/core/src/commands/key_down.rs +++ b/crates/core/src/commands/key_down.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, commands::press::parse_combo, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct KeyDownArgs { pub combo: String, diff --git a/crates/core/src/commands/key_up.rs b/crates/core/src/commands/key_up.rs index f8d42ed..c595f40 100644 --- a/crates/core/src/commands/key_up.rs +++ b/crates/core/src/commands/key_up.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, commands::press::parse_combo, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct KeyUpArgs { pub combo: String, diff --git a/crates/core/src/commands/list_apps.rs b/crates/core/src/commands/list_apps.rs index 1fe997d..d12177f 100644 --- a/crates/core/src/commands/list_apps.rs +++ b/crates/core/src/commands/list_apps.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, commands::search_text, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct ListAppsArgs { pub app: Option, diff --git a/crates/core/src/commands/list_notifications.rs b/crates/core/src/commands/list_notifications.rs index 7b7deb4..c59fccc 100644 --- a/crates/core/src/commands/list_notifications.rs +++ b/crates/core/src/commands/list_notifications.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError, notification::NotificationFilter}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct ListNotificationsArgs { pub app: Option, diff --git a/crates/core/src/commands/list_surfaces.rs b/crates/core/src/commands/list_surfaces.rs index 43ac6c1..3aedcc3 100644 --- a/crates/core/src/commands/list_surfaces.rs +++ b/crates/core/src/commands/list_surfaces.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, commands::helpers::resolve_app_pid, error::AppError}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct ListSurfacesArgs { pub app: Option, diff --git a/crates/core/src/commands/maximize.rs b/crates/core/src/commands/maximize.rs index 774fd35..eb98d02 100644 --- a/crates/core/src/commands/maximize.rs +++ b/crates/core/src/commands/maximize.rs @@ -1,7 +1,7 @@ use crate::{ action::WindowOp, adapter::PlatformAdapter, - commands::helpers::{window_op_command, AppArgs}, + commands::helpers::{AppArgs, window_op_command}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/minimize.rs b/crates/core/src/commands/minimize.rs index b67d374..f0831f0 100644 --- a/crates/core/src/commands/minimize.rs +++ b/crates/core/src/commands/minimize.rs @@ -1,7 +1,7 @@ use crate::{ action::WindowOp, adapter::PlatformAdapter, - commands::helpers::{window_op_command, AppArgs}, + commands::helpers::{AppArgs, window_op_command}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/mouse_click.rs b/crates/core/src/commands/mouse_click.rs index f4a6b14..06f4107 100644 --- a/crates/core/src/commands/mouse_click.rs +++ b/crates/core/src/commands/mouse_click.rs @@ -3,7 +3,7 @@ use crate::{ adapter::PlatformAdapter, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct MouseClickArgs { pub x: f64, diff --git a/crates/core/src/commands/mouse_down.rs b/crates/core/src/commands/mouse_down.rs index 68a6d98..7af3fc5 100644 --- a/crates/core/src/commands/mouse_down.rs +++ b/crates/core/src/commands/mouse_down.rs @@ -3,7 +3,7 @@ use crate::{ adapter::PlatformAdapter, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct MouseDownArgs { pub x: f64, diff --git a/crates/core/src/commands/mouse_move.rs b/crates/core/src/commands/mouse_move.rs index 9da3c78..90f462a 100644 --- a/crates/core/src/commands/mouse_move.rs +++ b/crates/core/src/commands/mouse_move.rs @@ -3,7 +3,7 @@ use crate::{ adapter::PlatformAdapter, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct MouseMoveArgs { pub x: f64, diff --git a/crates/core/src/commands/mouse_up.rs b/crates/core/src/commands/mouse_up.rs index bcba542..feae46e 100644 --- a/crates/core/src/commands/mouse_up.rs +++ b/crates/core/src/commands/mouse_up.rs @@ -3,7 +3,7 @@ use crate::{ adapter::PlatformAdapter, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct MouseUpArgs { pub x: f64, diff --git a/crates/core/src/commands/move_window.rs b/crates/core/src/commands/move_window.rs index 612b220..efad10c 100644 --- a/crates/core/src/commands/move_window.rs +++ b/crates/core/src/commands/move_window.rs @@ -2,7 +2,7 @@ use crate::{ action::WindowOp, adapter::PlatformAdapter, commands::helpers::resolve_window_for_app, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct MoveWindowArgs { pub app: Option, diff --git a/crates/core/src/commands/notification_action.rs b/crates/core/src/commands/notification_action.rs index 5e49b27..1a4ac01 100644 --- a/crates/core/src/commands/notification_action.rs +++ b/crates/core/src/commands/notification_action.rs @@ -1,5 +1,5 @@ use crate::{adapter::PlatformAdapter, error::AppError, notification::NotificationIdentity}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct NotificationActionArgs { pub index: usize, diff --git a/crates/core/src/commands/permissions.rs b/crates/core/src/commands/permissions.rs index a48c79c..4bfd2d2 100644 --- a/crates/core/src/commands/permissions.rs +++ b/crates/core/src/commands/permissions.rs @@ -1,5 +1,5 @@ -use crate::{adapter::PlatformAdapter, error::AppError, PermissionReport}; -use serde_json::{json, Value}; +use crate::{PermissionReport, adapter::PlatformAdapter, error::AppError}; +use serde_json::{Value, json}; pub struct PermissionsArgs { pub request: bool, diff --git a/crates/core/src/commands/press.rs b/crates/core/src/commands/press.rs index f3fdca5..665e309 100644 --- a/crates/core/src/commands/press.rs +++ b/crates/core/src/commands/press.rs @@ -59,7 +59,7 @@ pub fn parse_combo(s: &str) -> Result { other => { return Err(AppError::invalid_input(format!( "Unknown modifier: '{other}'" - ))) + ))); } }; modifiers.push(modifier); diff --git a/crates/core/src/commands/ref_policy_tests.rs b/crates/core/src/commands/ref_policy_tests.rs index 1de8a97..1417640 100644 --- a/crates/core/src/commands/ref_policy_tests.rs +++ b/crates/core/src/commands/ref_policy_tests.rs @@ -57,7 +57,7 @@ fn snapshot_id() -> String { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap() } diff --git a/crates/core/src/commands/resize_window.rs b/crates/core/src/commands/resize_window.rs index 067d0f3..415c256 100644 --- a/crates/core/src/commands/resize_window.rs +++ b/crates/core/src/commands/resize_window.rs @@ -2,7 +2,7 @@ use crate::{ action::WindowOp, adapter::PlatformAdapter, commands::helpers::resolve_window_for_app, error::AppError, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct ResizeWindowArgs { pub app: Option, diff --git a/crates/core/src/commands/restore.rs b/crates/core/src/commands/restore.rs index fccafc5..091f3f1 100644 --- a/crates/core/src/commands/restore.rs +++ b/crates/core/src/commands/restore.rs @@ -1,7 +1,7 @@ use crate::{ action::WindowOp, adapter::PlatformAdapter, - commands::helpers::{window_op_command, AppArgs}, + commands::helpers::{AppArgs, window_op_command}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/right_click.rs b/crates/core/src/commands/right_click.rs index 50ad158..ec7bcfd 100644 --- a/crates/core/src/commands/right_click.rs +++ b/crates/core/src/commands/right_click.rs @@ -1,12 +1,12 @@ use crate::{ action::{Action, ActionRequest}, adapter::{PlatformAdapter, SnapshotSurface, TreeOptions, WindowFilter}, - commands::helpers::{resolve_ref, RefArgs}, + commands::helpers::{RefArgs, resolve_ref}, error::AppError, refs::RefEntry, snapshot, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub fn execute(args: RefArgs, adapter: &dyn PlatformAdapter) -> Result { let (entry, handle) = resolve_ref(&args.ref_id, args.snapshot_id.as_deref(), adapter)?; @@ -180,7 +180,7 @@ mod tests { source_app, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap() } @@ -223,9 +223,11 @@ mod tests { assert_eq!(value["action"], "right_click"); assert_eq!(value["menu_probe"]["ok"], false); assert_eq!(value["menu_probe"]["error"]["code"], "ELEMENT_NOT_FOUND"); - assert!(value["menu_probe"]["error"]["suggestion"] - .as_str() - .unwrap() - .contains("snapshot --surface menu")); + assert!( + value["menu_probe"]["error"]["suggestion"] + .as_str() + .unwrap() + .contains("snapshot --surface menu") + ); } } diff --git a/crates/core/src/commands/screenshot.rs b/crates/core/src/commands/screenshot.rs index b1320c9..b135c77 100644 --- a/crates/core/src/commands/screenshot.rs +++ b/crates/core/src/commands/screenshot.rs @@ -3,7 +3,7 @@ use crate::{ error::AppError, }; use base64::Engine; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::path::PathBuf; pub struct ScreenshotArgs { diff --git a/crates/core/src/commands/scroll_to.rs b/crates/core/src/commands/scroll_to.rs index 91133d3..38823ed 100644 --- a/crates/core/src/commands/scroll_to.rs +++ b/crates/core/src/commands/scroll_to.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/skills.rs b/crates/core/src/commands/skills.rs index 41ff01f..b0a000d 100644 --- a/crates/core/src/commands/skills.rs +++ b/crates/core/src/commands/skills.rs @@ -1,5 +1,5 @@ use crate::error::AppError; -use serde_json::{json, Value}; +use serde_json::{Value, json}; const SKILL_DESKTOP_MAIN: &str = include_str!("../../../../skills/agent-desktop/SKILL.md"); const SKILL_DESKTOP_REF_OBSERVATION: &str = @@ -43,11 +43,26 @@ const SKILLS: &[Skill] = &[ summary: "Primary guide. Snapshot/ref loop, JSON envelope, 54 commands across observation, interaction, keyboard/mouse, app lifecycle, notifications, clipboard, wait.", main: SKILL_DESKTOP_MAIN, refs: &[ - SkillRef { rel_path: "references/commands-observation.md", body: SKILL_DESKTOP_REF_OBSERVATION }, - SkillRef { rel_path: "references/commands-interaction.md", body: SKILL_DESKTOP_REF_INTERACTION }, - SkillRef { rel_path: "references/commands-system.md", body: SKILL_DESKTOP_REF_SYSTEM }, - SkillRef { rel_path: "references/workflows.md", body: SKILL_DESKTOP_REF_WORKFLOWS }, - SkillRef { rel_path: "references/macos.md", body: SKILL_DESKTOP_REF_MACOS }, + SkillRef { + rel_path: "references/commands-observation.md", + body: SKILL_DESKTOP_REF_OBSERVATION, + }, + SkillRef { + rel_path: "references/commands-interaction.md", + body: SKILL_DESKTOP_REF_INTERACTION, + }, + SkillRef { + rel_path: "references/commands-system.md", + body: SKILL_DESKTOP_REF_SYSTEM, + }, + SkillRef { + rel_path: "references/workflows.md", + body: SKILL_DESKTOP_REF_WORKFLOWS, + }, + SkillRef { + rel_path: "references/macos.md", + body: SKILL_DESKTOP_REF_MACOS, + }, ], }, Skill { @@ -56,10 +71,22 @@ const SKILLS: &[Skill] = &[ summary: "Embedding agent-desktop in another process via the C ABI. Build/link, error propagation, handle ownership, threading rules.", main: SKILL_FFI_MAIN, refs: &[ - SkillRef { rel_path: "references/build-and-link.md", body: SKILL_FFI_REF_BUILD }, - SkillRef { rel_path: "references/error-handling.md", body: SKILL_FFI_REF_ERRORS }, - SkillRef { rel_path: "references/ownership.md", body: SKILL_FFI_REF_OWNERSHIP }, - SkillRef { rel_path: "references/threading.md", body: SKILL_FFI_REF_THREADING }, + SkillRef { + rel_path: "references/build-and-link.md", + body: SKILL_FFI_REF_BUILD, + }, + SkillRef { + rel_path: "references/error-handling.md", + body: SKILL_FFI_REF_ERRORS, + }, + SkillRef { + rel_path: "references/ownership.md", + body: SKILL_FFI_REF_OWNERSHIP, + }, + SkillRef { + rel_path: "references/threading.md", + body: SKILL_FFI_REF_THREADING, + }, ], }, ]; diff --git a/crates/core/src/commands/snapshot.rs b/crates/core/src/commands/snapshot.rs index 63be22b..b802009 100644 --- a/crates/core/src/commands/snapshot.rs +++ b/crates/core/src/commands/snapshot.rs @@ -4,7 +4,7 @@ use crate::{ error::AppError, snapshot, snapshot_ref, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct SnapshotArgs { pub app: Option, diff --git a/crates/core/src/commands/status.rs b/crates/core/src/commands/status.rs index f812fd4..9546082 100644 --- a/crates/core/src/commands/status.rs +++ b/crates/core/src/commands/status.rs @@ -1,11 +1,11 @@ use crate::{ + PermissionReport, adapter::PlatformAdapter, commands::permissions::{self, PermissionsArgs}, error::AppError, refs_store::RefStore, - PermissionReport, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub fn execute(adapter: &dyn PlatformAdapter) -> Result { let report = adapter.permission_report(); diff --git a/crates/core/src/commands/toggle.rs b/crates/core/src/commands/toggle.rs index dd2d5ba..531375b 100644 --- a/crates/core/src/commands/toggle.rs +++ b/crates/core/src/commands/toggle.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/triple_click.rs b/crates/core/src/commands/triple_click.rs index b40a727..88ccc41 100644 --- a/crates/core/src/commands/triple_click.rs +++ b/crates/core/src/commands/triple_click.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/uncheck.rs b/crates/core/src/commands/uncheck.rs index b2e2821..44c85b2 100644 --- a/crates/core/src/commands/uncheck.rs +++ b/crates/core/src/commands/uncheck.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionRequest}, adapter::PlatformAdapter, - commands::helpers::{execute_ref_action, RefArgs}, + commands::helpers::{RefArgs, execute_ref_action}, error::AppError, }; use serde_json::Value; diff --git a/crates/core/src/commands/version.rs b/crates/core/src/commands/version.rs index 5df1348..6a8c593 100644 --- a/crates/core/src/commands/version.rs +++ b/crates/core/src/commands/version.rs @@ -1,5 +1,5 @@ use crate::error::AppError; -use serde_json::{json, Value}; +use serde_json::{Value, json}; pub struct VersionArgs { pub json: bool, diff --git a/crates/core/src/commands/wait.rs b/crates/core/src/commands/wait.rs index 3025f73..922d768 100644 --- a/crates/core/src/commands/wait.rs +++ b/crates/core/src/commands/wait.rs @@ -8,7 +8,7 @@ use crate::{ refs_store::RefStore, snapshot, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::time::{Duration, Instant}; pub struct WaitArgs { diff --git a/crates/core/src/commands/wait_tests.rs b/crates/core/src/commands/wait_tests.rs index 99c7560..85345b7 100644 --- a/crates/core/src/commands/wait_tests.rs +++ b/crates/core/src/commands/wait_tests.rs @@ -40,7 +40,7 @@ fn snapshot_with_one_ref() -> String { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); RefStore::new().unwrap().save_new_snapshot(&refmap).unwrap() } @@ -142,7 +142,7 @@ fn latest_ref_cache_picks_up_newer_snapshot_after_refresh() { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); let second_id = store.save_new_snapshot(&second).unwrap(); assert_ne!(first_id, second_id); @@ -178,7 +178,7 @@ fn latest_ref_cache_debounces_consecutive_refreshes() { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); let _ = store.save_new_snapshot(&other).unwrap(); diff --git a/crates/core/src/ref_alloc.rs b/crates/core/src/ref_alloc.rs index 3844cd3..d10e182 100644 --- a/crates/core/src/ref_alloc.rs +++ b/crates/core/src/ref_alloc.rs @@ -40,7 +40,7 @@ pub(crate) fn ref_entry_from_node( source_app: source_app.map(str::to_string), source_window_title: source_window_title.map(str::to_string), root_ref, - path: path.to_vec(), + path: smallvec::SmallVec::from_slice(path), } } @@ -122,7 +122,6 @@ fn allocate_refs_at_path( config: &RefAllocConfig, path: &mut Vec, ) -> AccessibilityNode { - let root_ref_owned = config.root_ref_id.map(str::to_string); let is_interactive = INTERACTIVE_ROLES.contains(&node.role.as_str()); if is_interactive { @@ -131,7 +130,7 @@ fn allocate_refs_at_path( config.pid, config.source_app, config.source_window_title, - root_ref_owned.clone(), + config.root_ref_id.map(str::to_string), path, ); node.ref_id = Some(refmap.allocate(entry)); @@ -304,7 +303,7 @@ mod tests { let save_ref = out.children[0].ref_id.as_deref().unwrap(); let open_ref = out.children[1].children[0].ref_id.as_deref().unwrap(); - assert_eq!(refmap.get(save_ref).unwrap().path, vec![0]); - assert_eq!(refmap.get(open_ref).unwrap().path, vec![1, 0]); + assert_eq!(refmap.get(save_ref).unwrap().path.as_slice(), [0]); + assert_eq!(refmap.get(open_ref).unwrap().path.as_slice(), [1, 0]); } } diff --git a/crates/core/src/refs.rs b/crates/core/src/refs.rs index 0baf2fb..269a646 100644 --- a/crates/core/src/refs.rs +++ b/crates/core/src/refs.rs @@ -1,13 +1,16 @@ use crate::error::AppError; use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; use std::cell::RefCell; -use std::collections::hash_map::RandomState; use std::collections::HashMap; +use std::collections::hash_map::RandomState; use std::hash::BuildHasher; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; +pub type RefPath = SmallVec<[usize; 8]>; + pub(crate) const MAX_REFMAP_BYTES: u64 = 1_048_576; static SNAPSHOT_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -33,8 +36,8 @@ pub struct RefEntry { pub source_window_title: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub root_ref: Option, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub path: Vec, + #[serde(default, skip_serializing_if = "SmallVec::is_empty")] + pub path: RefPath, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/core/src/refs_store.rs b/crates/core/src/refs_store.rs index 4346768..950d5fe 100644 --- a/crates/core/src/refs_store.rs +++ b/crates/core/src/refs_store.rs @@ -1,6 +1,6 @@ use crate::{ error::{AdapterError, AppError}, - refs::{home_dir, new_snapshot_id, validate_snapshot_id, write_private_file, RefMap}, + refs::{RefMap, home_dir, new_snapshot_id, validate_snapshot_id, write_private_file}, refs_lock::RefStoreLock, }; use std::io::Read; diff --git a/crates/core/src/refs_tests.rs b/crates/core/src/refs_tests.rs index c9cd57d..794a4da 100644 --- a/crates/core/src/refs_tests.rs +++ b/crates/core/src/refs_tests.rs @@ -14,7 +14,7 @@ fn entry(role: &str, name: Option<&str>) -> RefEntry { source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), } } @@ -43,7 +43,7 @@ fn test_get_existing() { source_app: Some("Finder".into()), source_window_title: Some("Documents".into()), root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); let retrieved = map.get(&ref_id).unwrap(); assert_eq!(retrieved.pid, 42); @@ -167,7 +167,7 @@ fn test_save_load_roundtrip_with_home_override() { source_app: Some("TestApp".into()), source_window_title: Some("Test Window".into()), root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); map.save().expect("save should succeed under HomeGuard"); @@ -195,7 +195,7 @@ fn test_refstore_snapshot_roundtrip_and_latest_pointer() { source_app: Some("TestApp".into()), source_window_title: Some("Test Window".into()), root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }); let snapshot_id = store.save_new_snapshot(&map).unwrap(); diff --git a/crates/ffi/Cargo.toml b/crates/ffi/Cargo.toml index 8dea555..918ad39 100644 --- a/crates/ffi/Cargo.toml +++ b/crates/ffi/Cargo.toml @@ -9,6 +9,7 @@ publish = false crate-type = ["cdylib", "rlib"] [dependencies] +smallvec.workspace = true agent-desktop-core.workspace = true [target.'cfg(target_os = "macos")'.dependencies] @@ -23,3 +24,6 @@ agent-desktop-linux = { path = "../linux" } [build-dependencies] cbindgen = "= 0.27.0" + +[lints] +workspace = true diff --git a/crates/ffi/examples/panic_spike.rs b/crates/ffi/examples/panic_spike.rs index 89299cf..8b011e5 100644 --- a/crates/ffi/examples/panic_spike.rs +++ b/crates/ffi/examples/panic_spike.rs @@ -12,7 +12,7 @@ use std::panic::AssertUnwindSafe; -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn spike_panicking_entry() -> i32 { std::panic::catch_unwind(AssertUnwindSafe(|| -> i32 { panic!("synthetic panic inside extern C fn"); diff --git a/crates/ffi/src/actions/conversion.rs b/crates/ffi/src/actions/conversion.rs index ab698bc..e04cdcc 100644 --- a/crates/ffi/src/actions/conversion.rs +++ b/crates/ffi/src/actions/conversion.rs @@ -20,95 +20,99 @@ fn direction_from_c(d: AdDirection) -> Direction { const MAX_MODIFIERS_PER_COMBO: u32 = 4; pub(crate) unsafe fn key_combo_from_c(k: &AdKeyCombo) -> Result { - let key = c_to_string(k.key).ok_or("key is null or invalid UTF-8")?; + unsafe { + let key = c_to_string(k.key).ok_or("key is null or invalid UTF-8")?; - if k.modifier_count > MAX_MODIFIERS_PER_COMBO { - return Err("modifier_count exceeds MAX_MODIFIERS_PER_COMBO (4)"); - } - if k.modifier_count > 0 && k.modifiers.is_null() { - return Err("modifier_count > 0 but modifiers pointer is null"); - } - - let mut modifiers = Vec::with_capacity(k.modifier_count as usize); - if k.modifier_count > 0 { - let slice = std::slice::from_raw_parts(k.modifiers, k.modifier_count as usize); - for raw_modifier in slice { - let m = AdModifier::from_c(*raw_modifier).ok_or("invalid modifier discriminant")?; - let modifier = match m { - AdModifier::Cmd => Modifier::Cmd, - AdModifier::Ctrl => Modifier::Ctrl, - AdModifier::Alt => Modifier::Alt, - AdModifier::Shift => Modifier::Shift, - }; - modifiers.push(modifier); + if k.modifier_count > MAX_MODIFIERS_PER_COMBO { + return Err("modifier_count exceeds MAX_MODIFIERS_PER_COMBO (4)"); } + if k.modifier_count > 0 && k.modifiers.is_null() { + return Err("modifier_count > 0 but modifiers pointer is null"); + } + + let mut modifiers = Vec::with_capacity(k.modifier_count as usize); + if k.modifier_count > 0 { + let slice = std::slice::from_raw_parts(k.modifiers, k.modifier_count as usize); + for raw_modifier in slice { + let m = AdModifier::from_c(*raw_modifier).ok_or("invalid modifier discriminant")?; + let modifier = match m { + AdModifier::Cmd => Modifier::Cmd, + AdModifier::Ctrl => Modifier::Ctrl, + AdModifier::Alt => Modifier::Alt, + AdModifier::Shift => Modifier::Shift, + }; + modifiers.push(modifier); + } + } + Ok(CoreKeyCombo { key, modifiers }) } - Ok(CoreKeyCombo { key, modifiers }) } pub(crate) unsafe fn action_from_c(action: &AdAction) -> Result { - let kind = AdActionKind::from_c(action.kind).ok_or("invalid action kind discriminant")?; - match kind { - AdActionKind::Click => Ok(Action::Click), - AdActionKind::DoubleClick => Ok(Action::DoubleClick), - AdActionKind::RightClick => Ok(Action::RightClick), - AdActionKind::TripleClick => Ok(Action::TripleClick), - AdActionKind::SetFocus => Ok(Action::SetFocus), - AdActionKind::Expand => Ok(Action::Expand), - AdActionKind::Collapse => Ok(Action::Collapse), - AdActionKind::Toggle => Ok(Action::Toggle), - AdActionKind::Check => Ok(Action::Check), - AdActionKind::Uncheck => Ok(Action::Uncheck), - AdActionKind::ScrollTo => Ok(Action::ScrollTo), - AdActionKind::Clear => Ok(Action::Clear), - AdActionKind::Hover => Ok(Action::Hover), - AdActionKind::SetValue => { - let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; - Ok(Action::SetValue(text)) - } - AdActionKind::Select => { - let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; - Ok(Action::Select(text)) - } - AdActionKind::TypeText => { - let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; - Ok(Action::TypeText(text)) - } - AdActionKind::Scroll => { - let raw_dir = AdDirection::from_c(action.scroll.direction) - .ok_or("invalid scroll direction discriminant")?; - let dir = direction_from_c(raw_dir); - Ok(Action::Scroll(dir, action.scroll.amount)) - } - AdActionKind::PressKey => { - let combo = key_combo_from_c(&action.key)?; - Ok(Action::PressKey(combo)) - } - AdActionKind::KeyDown => { - let combo = key_combo_from_c(&action.key)?; - Ok(Action::KeyDown(combo)) - } - AdActionKind::KeyUp => { - let combo = key_combo_from_c(&action.key)?; - Ok(Action::KeyUp(combo)) - } - AdActionKind::Drag => { - let params = CoreDragParams { - from: CorePoint { - x: action.drag.from.x, - y: action.drag.from.y, - }, - to: CorePoint { - x: action.drag.to.x, - y: action.drag.to.y, - }, - duration_ms: if action.drag.duration_ms == 0 { - None - } else { - Some(action.drag.duration_ms) - }, - }; - Ok(Action::Drag(params)) + unsafe { + let kind = AdActionKind::from_c(action.kind).ok_or("invalid action kind discriminant")?; + match kind { + AdActionKind::Click => Ok(Action::Click), + AdActionKind::DoubleClick => Ok(Action::DoubleClick), + AdActionKind::RightClick => Ok(Action::RightClick), + AdActionKind::TripleClick => Ok(Action::TripleClick), + AdActionKind::SetFocus => Ok(Action::SetFocus), + AdActionKind::Expand => Ok(Action::Expand), + AdActionKind::Collapse => Ok(Action::Collapse), + AdActionKind::Toggle => Ok(Action::Toggle), + AdActionKind::Check => Ok(Action::Check), + AdActionKind::Uncheck => Ok(Action::Uncheck), + AdActionKind::ScrollTo => Ok(Action::ScrollTo), + AdActionKind::Clear => Ok(Action::Clear), + AdActionKind::Hover => Ok(Action::Hover), + AdActionKind::SetValue => { + let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; + Ok(Action::SetValue(text)) + } + AdActionKind::Select => { + let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; + Ok(Action::Select(text)) + } + AdActionKind::TypeText => { + let text = c_to_string(action.text).ok_or("text is null or invalid UTF-8")?; + Ok(Action::TypeText(text)) + } + AdActionKind::Scroll => { + let raw_dir = AdDirection::from_c(action.scroll.direction) + .ok_or("invalid scroll direction discriminant")?; + let dir = direction_from_c(raw_dir); + Ok(Action::Scroll(dir, action.scroll.amount)) + } + AdActionKind::PressKey => { + let combo = key_combo_from_c(&action.key)?; + Ok(Action::PressKey(combo)) + } + AdActionKind::KeyDown => { + let combo = key_combo_from_c(&action.key)?; + Ok(Action::KeyDown(combo)) + } + AdActionKind::KeyUp => { + let combo = key_combo_from_c(&action.key)?; + Ok(Action::KeyUp(combo)) + } + AdActionKind::Drag => { + let params = CoreDragParams { + from: CorePoint { + x: action.drag.from.x, + y: action.drag.from.y, + }, + to: CorePoint { + x: action.drag.to.x, + y: action.drag.to.y, + }, + duration_ms: if action.drag.duration_ms == 0 { + None + } else { + Some(action.drag.duration_ms) + }, + }; + Ok(Action::Drag(params)) + } } } } diff --git a/crates/ffi/src/actions/execute.rs b/crates/ffi/src/actions/execute.rs index 007bfa9..1728a70 100644 --- a/crates/ffi/src/actions/execute.rs +++ b/crates/ffi/src/actions/execute.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::actions::conversion::action_from_c; use crate::actions::result::action_result_to_c; use crate::error::{self, AdResult}; use crate::ffi_try::trap_panic; use crate::types::{AdAction, AdActionResult, AdNativeHandle, AdPolicyKind}; -use crate::AdAdapter; use agent_desktop_core::{action::ActionRequest, adapter::NativeHandle}; /// # Safety @@ -12,7 +12,7 @@ use agent_desktop_core::{action::ActionRequest, adapter::NativeHandle}; /// `handle` must be a non-null pointer to a valid `AdNativeHandle`. /// `action` must be a non-null pointer to a valid `AdAction`. /// `out` must be a non-null pointer to an `AdActionResult` to write the result into. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_execute_action( adapter: *const AdAdapter, handle: *const AdNativeHandle, @@ -30,7 +30,7 @@ pub unsafe extern "C" fn ad_execute_action( /// `handle` must be a non-null pointer to a valid `AdNativeHandle`. /// `action` must be a non-null pointer to a valid `AdAction`. /// `out` must be a non-null pointer to an `AdActionResult` to write the result into. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_execute_action_with_policy( adapter: *const AdAdapter, handle: *const AdNativeHandle, diff --git a/crates/ffi/src/actions/native_handle.rs b/crates/ffi/src/actions/native_handle.rs index 8095e7d..d69a331 100644 --- a/crates/ffi/src/actions/native_handle.rs +++ b/crates/ffi/src/actions/native_handle.rs @@ -1,7 +1,7 @@ -use crate::error::{set_last_error, AdResult}; +use crate::AdAdapter; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::AdNativeHandle; -use crate::AdAdapter; use agent_desktop_core::adapter::NativeHandle; /// Releases a handle previously returned by `ad_resolve_element` and @@ -28,7 +28,7 @@ use agent_desktop_core::adapter::NativeHandle; /// `handle` must be null or a `*mut AdNativeHandle` previously /// populated by `ad_resolve_element`. On return `(*handle).ptr` is /// `NULL` so a double-call is a no-op instead of a double-free. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_free_handle( adapter: *const AdAdapter, handle: *mut AdNativeHandle, diff --git a/crates/ffi/src/actions/resolve.rs b/crates/ffi/src/actions/resolve.rs index 5d1999f..dece4f3 100644 --- a/crates/ffi/src/actions/resolve.rs +++ b/crates/ffi/src/actions/resolve.rs @@ -1,8 +1,8 @@ +use crate::AdAdapter; use crate::convert::string::{c_to_string, try_c_to_string}; use crate::error::{self, AdResult}; use crate::ffi_try::trap_panic; use crate::types::{AdNativeHandle, AdRefEntry}; -use crate::AdAdapter; use agent_desktop_core::refs::RefEntry as CoreRefEntry; /// # Safety @@ -10,7 +10,7 @@ use agent_desktop_core::refs::RefEntry as CoreRefEntry; /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. /// `entry` must be a non-null pointer to a valid `AdRefEntry`. /// `out` must be a non-null pointer to an `AdNativeHandle` to write the result into. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_resolve_element( adapter: *const AdAdapter, entry: *const AdRefEntry, @@ -68,7 +68,7 @@ pub unsafe extern "C" fn ad_resolve_element( source_app: None, source_window_title: None, root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }; match adapter.inner.resolve_element(&core_entry) { Ok(handle) => { diff --git a/crates/ffi/src/actions/result.rs b/crates/ffi/src/actions/result.rs index b2902b6..a0e5e8b 100644 --- a/crates/ffi/src/actions/result.rs +++ b/crates/ffi/src/actions/result.rs @@ -42,7 +42,7 @@ pub(crate) fn action_result_to_c(r: &CoreActionResult) -> AdActionResult { /// /// `result` must be a pointer to an `AdActionResult` previously written by `ad_execute_action`, /// or null. After this call all pointers inside the struct are invalid. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_free_action_result(result: *mut AdActionResult) { crate::ffi_try::trap_panic_void(|| unsafe { if result.is_null() { diff --git a/crates/ffi/src/adapter.rs b/crates/ffi/src/adapter.rs index 457b935..163d1b0 100644 --- a/crates/ffi/src/adapter.rs +++ b/crates/ffi/src/adapter.rs @@ -1,6 +1,6 @@ use crate::error::{self, AdResult}; use crate::ffi_try::{trap_panic, trap_panic_ptr, trap_panic_void}; -use agent_desktop_core::{adapter::PlatformAdapter, PermissionState}; +use agent_desktop_core::{PermissionState, adapter::PlatformAdapter}; pub struct AdAdapter { pub(crate) inner: Box, @@ -33,7 +33,7 @@ fn build_adapter() -> Box { /// The returned pointer is owned by the caller and must be released with /// `ad_adapter_destroy`. Creating and destroying adapters is cheap; the /// common pattern is one adapter per process lifetime. -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ad_adapter_create() -> *mut AdAdapter { trap_panic_ptr(|| { let adapter = AdAdapter { @@ -47,7 +47,7 @@ pub extern "C" fn ad_adapter_create() -> *mut AdAdapter { /// /// `adapter` must be a pointer returned by `ad_adapter_create`, or null. /// After this call the pointer is invalid and must not be used. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_adapter_destroy(adapter: *mut AdAdapter) { trap_panic_void(|| { if !adapter.is_null() { @@ -60,7 +60,7 @@ pub unsafe extern "C" fn ad_adapter_destroy(adapter: *mut AdAdapter) { /// /// `adapter` must be a non-null pointer returned by `ad_adapter_create` that /// has not yet been destroyed. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_check_permissions(adapter: *const AdAdapter) -> AdResult { trap_panic(|| { crate::pointer_guard::guard_non_null!(adapter, c"adapter is null"); diff --git a/crates/ffi/src/apps/close.rs b/crates/ffi/src/apps/close.rs index 4b600f3..89fc762 100644 --- a/crates/ffi/src/apps/close.rs +++ b/crates/ffi/src/apps/close.rs @@ -1,7 +1,7 @@ -use crate::convert::string::c_to_string; -use crate::error::{set_last_error, AdResult}; -use crate::ffi_try::trap_panic; use crate::AdAdapter; +use crate::convert::string::c_to_string; +use crate::error::{AdResult, set_last_error}; +use crate::ffi_try::trap_panic; use std::os::raw::c_char; /// Closes the application identified by `id` (bundle id on macOS, @@ -10,7 +10,7 @@ use std::os::raw::c_char; /// /// # Safety /// `adapter` must be non-null. `id` must be a non-null UTF-8 C string. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_close_app( adapter: *const AdAdapter, id: *const c_char, diff --git a/crates/ffi/src/apps/launch.rs b/crates/ffi/src/apps/launch.rs index 9e52ff7..7e03927 100644 --- a/crates/ffi/src/apps/launch.rs +++ b/crates/ffi/src/apps/launch.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::convert::string::c_to_string; use crate::convert::window::window_info_to_c; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::AdWindowInfo; -use crate::AdAdapter; use std::os::raw::c_char; /// Launches the application identified by `id` (bundle id on macOS, @@ -19,7 +19,7 @@ use std::os::raw::c_char; /// # Safety /// `adapter` must be non-null. `id` must be a non-null UTF-8 C string. /// `out` must be a non-null writable `*mut AdWindowInfo`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_launch_app( adapter: *const AdAdapter, id: *const c_char, diff --git a/crates/ffi/src/apps/list.rs b/crates/ffi/src/apps/list.rs index 6767657..e85280d 100644 --- a/crates/ffi/src/apps/list.rs +++ b/crates/ffi/src/apps/list.rs @@ -1,8 +1,8 @@ +use crate::AdAdapter; use crate::convert::app::{app_info_to_c, free_app_info_fields}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::{trap_panic, trap_panic_void}; use crate::types::{AdAppInfo, AdAppList}; -use crate::AdAdapter; use std::ptr; /// # Safety @@ -10,7 +10,7 @@ use std::ptr; /// `out` must be a valid writable `*mut *mut AdAppList`. /// On success, `*out` is a newly-allocated opaque list freed with /// `ad_app_list_free`. On error, `*out` is null and last-error is set. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_list_apps( adapter: *const AdAdapter, out: *mut *mut AdAppList, @@ -42,7 +42,7 @@ pub unsafe extern "C" fn ad_list_apps( /// # Safety /// `list` must be null or a pointer returned by `ad_list_apps`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_app_list_count(list: *const AdAppList) -> u32 { if list.is_null() { return 0; @@ -56,7 +56,7 @@ pub unsafe extern "C" fn ad_app_list_count(list: *const AdAppList) -> u32 { /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_apps`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_app_list_get(list: *const AdAppList, index: u32) -> *const AdAppInfo { if list.is_null() { return ptr::null(); @@ -73,7 +73,7 @@ pub unsafe extern "C" fn ad_app_list_get(list: *const AdAppList, index: u32) -> /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_apps`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_app_list_free(list: *mut AdAppList) { trap_panic_void(|| unsafe { if list.is_null() { diff --git a/crates/ffi/src/convert/app.rs b/crates/ffi/src/convert/app.rs index 1adaa41..1bef12b 100644 --- a/crates/ffi/src/convert/app.rs +++ b/crates/ffi/src/convert/app.rs @@ -13,10 +13,12 @@ pub(crate) fn app_info_to_c(a: &AppInfo) -> AdAppInfo { } pub(crate) unsafe fn free_app_info_fields(a: &mut AdAppInfo) { - free_c_string(a.name as *mut c_char); - free_c_string(a.bundle_id as *mut c_char); - a.name = ptr::null(); - a.bundle_id = ptr::null(); + unsafe { + free_c_string(a.name as *mut c_char); + free_c_string(a.bundle_id as *mut c_char); + a.name = ptr::null(); + a.bundle_id = ptr::null(); + } } #[cfg(test)] diff --git a/crates/ffi/src/convert/notification.rs b/crates/ffi/src/convert/notification.rs index 9f3a0ed..bedadb9 100644 --- a/crates/ffi/src/convert/notification.rs +++ b/crates/ffi/src/convert/notification.rs @@ -17,15 +17,17 @@ pub(crate) fn notification_info_to_c(info: &NotificationInfo) -> AdNotificationI } pub(crate) unsafe fn free_notification_info_fields(info: &mut AdNotificationInfo) { - free_c_string(info.app_name as *mut c_char); - free_c_string(info.title as *mut c_char); - free_c_string(info.body as *mut c_char); - free_c_string_array(info.actions, info.action_count); - info.app_name = ptr::null(); - info.title = ptr::null(); - info.body = ptr::null(); - info.actions = ptr::null_mut(); - info.action_count = 0; + unsafe { + free_c_string(info.app_name as *mut c_char); + free_c_string(info.title as *mut c_char); + free_c_string(info.body as *mut c_char); + free_c_string_array(info.actions, info.action_count); + info.app_name = ptr::null(); + info.title = ptr::null(); + info.body = ptr::null(); + info.actions = ptr::null_mut(); + info.action_count = 0; + } } fn strings_to_c_array(strings: &[String]) -> (*mut *mut c_char, u32) { @@ -41,15 +43,17 @@ fn strings_to_c_array(strings: &[String]) -> (*mut *mut c_char, u32) { } unsafe fn free_c_string_array(arr: *mut *mut c_char, count: u32) { - if arr.is_null() { - return; + unsafe { + if arr.is_null() { + return; + } + let slice = std::slice::from_raw_parts_mut(arr, count as usize); + for p in slice.iter_mut() { + free_c_string(*p); + } + drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( + arr, + count as usize, + ))); } - let slice = std::slice::from_raw_parts_mut(arr, count as usize); - for p in slice.iter_mut() { - free_c_string(*p); - } - drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( - arr, - count as usize, - ))); } diff --git a/crates/ffi/src/convert/string.rs b/crates/ffi/src/convert/string.rs index 91b4b93..b323d31 100644 --- a/crates/ffi/src/convert/string.rs +++ b/crates/ffi/src/convert/string.rs @@ -36,16 +36,20 @@ pub(crate) fn opt_string_to_c(s: Option<&str>) -> *mut c_char { } pub(crate) unsafe fn free_c_string(ptr: *mut c_char) { - if !ptr.is_null() { - drop(CString::from_raw(ptr)); + unsafe { + if !ptr.is_null() { + drop(CString::from_raw(ptr)); + } } } pub(crate) unsafe fn c_to_string(ptr: *const c_char) -> Option { - if ptr.is_null() { - return None; + unsafe { + if ptr.is_null() { + return None; + } + CStr::from_ptr(ptr).to_str().ok().map(str::to_owned) } - CStr::from_ptr(ptr).to_str().ok().map(str::to_owned) } /// Tri-state decode of a foreign C string used for optional filter @@ -62,12 +66,14 @@ pub(crate) unsafe fn c_to_string(ptr: *const c_char) -> Option { /// # Safety /// `ptr` must be null or a NUL-terminated C string. pub(crate) unsafe fn try_c_to_string(ptr: *const c_char) -> Result, ()> { - if ptr.is_null() { - return Ok(None); - } - match CStr::from_ptr(ptr).to_str() { - Ok(s) => Ok(Some(s.to_owned())), - Err(_) => Err(()), + unsafe { + if ptr.is_null() { + return Ok(None); + } + match CStr::from_ptr(ptr).to_str() { + Ok(s) => Ok(Some(s.to_owned())), + Err(_) => Err(()), + } } } diff --git a/crates/ffi/src/convert/surface.rs b/crates/ffi/src/convert/surface.rs index 882261b..6fe1855 100644 --- a/crates/ffi/src/convert/surface.rs +++ b/crates/ffi/src/convert/surface.rs @@ -13,10 +13,12 @@ pub(crate) fn surface_info_to_c(s: &SurfaceInfo) -> AdSurfaceInfo { } pub(crate) unsafe fn free_surface_info_fields(s: &mut AdSurfaceInfo) { - free_c_string(s.kind as *mut c_char); - free_c_string(s.title as *mut c_char); - s.kind = ptr::null(); - s.title = ptr::null(); + unsafe { + free_c_string(s.kind as *mut c_char); + free_c_string(s.title as *mut c_char); + s.kind = ptr::null(); + s.title = ptr::null(); + } } #[cfg(test)] diff --git a/crates/ffi/src/convert/window.rs b/crates/ffi/src/convert/window.rs index bc3347c..4f14dcc 100644 --- a/crates/ffi/src/convert/window.rs +++ b/crates/ffi/src/convert/window.rs @@ -30,12 +30,14 @@ pub(crate) fn window_info_to_c(w: &WindowInfo) -> AdWindowInfo { } pub(crate) unsafe fn free_window_info_fields(w: &mut AdWindowInfo) { - free_c_string(w.id as *mut c_char); - free_c_string(w.title as *mut c_char); - free_c_string(w.app_name as *mut c_char); - w.id = ptr::null(); - w.title = ptr::null(); - w.app_name = ptr::null(); + unsafe { + free_c_string(w.id as *mut c_char); + free_c_string(w.title as *mut c_char); + free_c_string(w.app_name as *mut c_char); + w.id = ptr::null(); + w.title = ptr::null(); + w.app_name = ptr::null(); + } } #[cfg(test)] diff --git a/crates/ffi/src/error.rs b/crates/ffi/src/error.rs index 8fbffcf..bdbed6c 100644 --- a/crates/ffi/src/error.rs +++ b/crates/ffi/src/error.rs @@ -1,6 +1,6 @@ use agent_desktop_core::error::{AdapterError, ErrorCode}; use std::cell::RefCell; -use std::ffi::{c_char, CStr, CString}; +use std::ffi::{CStr, CString, c_char}; const fn error_code_variant_count() -> usize { let mut count = 0; @@ -215,7 +215,7 @@ pub(crate) fn last_error_code() -> AdResult { /// leaks to Thread B. /// Returns the `AdResult` code of the last error on the calling thread, /// or `AD_RESULT_OK` if no error has been recorded. -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ad_last_error_code() -> AdResult { crate::ffi_try::trap_panic(last_error_code) } @@ -224,7 +224,7 @@ pub extern "C" fn ad_last_error_code() -> AdResult { /// error has been recorded on the calling thread. The pointer remains /// valid across any number of subsequent *successful* FFI calls; only /// the next failing call overwrites it. -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ad_last_error_message() -> *const c_char { crate::ffi_try::trap_panic_const_ptr(|| { LAST_ERROR.with(|cell| { @@ -239,7 +239,7 @@ pub extern "C" fn ad_last_error_message() -> *const c_char { /// Returns a borrowed C string with a human-readable suggestion for how /// to recover from the last error, or null if the adapter didn't emit /// one. Same lifetime rules as `ad_last_error_message`. -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ad_last_error_suggestion() -> *const c_char { crate::ffi_try::trap_panic_const_ptr(|| { LAST_ERROR.with(|cell| { @@ -255,7 +255,7 @@ pub extern "C" fn ad_last_error_suggestion() -> *const c_char { /// for the last error (AX error codes, COM HRESULTs, AT-SPI messages, /// etc.), or null if the adapter didn't supply one. Same lifetime rules /// as `ad_last_error_message`. -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn ad_last_error_platform_detail() -> *const c_char { crate::ffi_try::trap_panic_const_ptr(|| { LAST_ERROR.with(|cell| { diff --git a/crates/ffi/src/ffi_try.rs b/crates/ffi/src/ffi_try.rs index 2893016..b25ad56 100644 --- a/crates/ffi/src/ffi_try.rs +++ b/crates/ffi/src/ffi_try.rs @@ -1,6 +1,6 @@ -use crate::error::{set_last_error_static, AdResult}; +use crate::error::{AdResult, set_last_error_static}; use std::ffi::CStr; -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; static PANIC_MESSAGE: &CStr = c"rust panic in FFI boundary"; diff --git a/crates/ffi/src/input/clipboard.rs b/crates/ffi/src/input/clipboard.rs index 8fc01e2..f36f219 100644 --- a/crates/ffi/src/input/clipboard.rs +++ b/crates/ffi/src/input/clipboard.rs @@ -1,7 +1,7 @@ +use crate::AdAdapter; use crate::convert::string::{c_to_string, free_c_string, string_to_c}; use crate::error::{self, AdResult}; use crate::ffi_try::{trap_panic, trap_panic_void}; -use crate::AdAdapter; use std::os::raw::c_char; /// Reads the current clipboard text and writes an owned C string into @@ -11,7 +11,7 @@ use std::os::raw::c_char; /// # Safety /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. /// `out` must be a non-null writable `*mut *mut c_char`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_get_clipboard( adapter: *const AdAdapter, out: *mut *mut c_char, @@ -28,12 +28,10 @@ pub unsafe extern "C" fn ad_get_clipboard( Ok(text) => { let c = string_to_c(&text); if c.is_null() { - error::set_last_error( - &agent_desktop_core::error::AdapterError::new( - agent_desktop_core::error::ErrorCode::Internal, - "clipboard text contains an interior NUL and cannot be represented as a C string", - ), - ); + error::set_last_error(&agent_desktop_core::error::AdapterError::new( + agent_desktop_core::error::ErrorCode::Internal, + "clipboard text contains an interior NUL and cannot be represented as a C string", + )); return AdResult::ErrInternal; } *out = c; @@ -53,7 +51,7 @@ pub unsafe extern "C" fn ad_get_clipboard( /// # Safety /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. /// `text` must be a non-null, NUL-terminated UTF-8 C string. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_set_clipboard( adapter: *const AdAdapter, text: *const c_char, @@ -88,7 +86,7 @@ pub unsafe extern "C" fn ad_set_clipboard( /// /// # Safety /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_clear_clipboard(adapter: *const AdAdapter) -> AdResult { trap_panic(|| unsafe { if let Err(rc) = crate::main_thread::require_main_thread() { @@ -113,7 +111,7 @@ pub unsafe extern "C" fn ad_clear_clipboard(adapter: *const AdAdapter) -> AdResu /// # Safety /// `s` must be null or a pointer previously handed out by this crate. /// After this call the pointer is invalid and must not be used. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_free_string(s: *mut c_char) { trap_panic_void(|| unsafe { free_c_string(s) }) } diff --git a/crates/ffi/src/input/drag.rs b/crates/ffi/src/input/drag.rs index ae808c6..c6309e9 100644 --- a/crates/ffi/src/input/drag.rs +++ b/crates/ffi/src/input/drag.rs @@ -1,7 +1,7 @@ +use crate::AdAdapter; use crate::error::{self, AdResult}; use crate::ffi_try::trap_panic; use crate::types::AdDragParams; -use crate::AdAdapter; use agent_desktop_core::action::{DragParams as CoreDragParams, Point as CorePoint}; /// Synthesizes a mouse drag from `params.from` to `params.to`. When @@ -11,7 +11,7 @@ use agent_desktop_core::action::{DragParams as CoreDragParams, Point as CorePoin /// # Safety /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. /// `params` must be a non-null pointer to a valid `AdDragParams`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_drag( adapter: *const AdAdapter, params: *const AdDragParams, diff --git a/crates/ffi/src/input/mouse.rs b/crates/ffi/src/input/mouse.rs index 888ecfe..a87325b 100644 --- a/crates/ffi/src/input/mouse.rs +++ b/crates/ffi/src/input/mouse.rs @@ -1,7 +1,7 @@ +use crate::AdAdapter; use crate::error::{self, AdResult}; use crate::ffi_try::trap_panic; use crate::types::{AdMouseButton, AdMouseEvent, AdMouseEventKind}; -use crate::AdAdapter; use agent_desktop_core::action::{ MouseButton as CoreMouseButton, MouseEvent as CoreMouseEvent, MouseEventKind as CoreMouseEventKind, Point as CorePoint, @@ -22,7 +22,7 @@ pub(crate) fn mouse_button_from_c(b: AdMouseButton) -> CoreMouseButton { /// # Safety /// `adapter` must be a non-null pointer returned by `ad_adapter_create`. /// `event` must be a non-null pointer to a valid `AdMouseEvent`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_mouse_event( adapter: *const AdAdapter, event: *const AdMouseEvent, diff --git a/crates/ffi/src/main_thread.rs b/crates/ffi/src/main_thread.rs index 5c04c4e..bba2b30 100644 --- a/crates/ffi/src/main_thread.rs +++ b/crates/ffi/src/main_thread.rs @@ -17,7 +17,7 @@ //! / `ad_free_string` / `ad_free_action_result` / `ad_free_tree` family. //! Those paths touch no AX/Cocoa state and are safe from any thread. -use crate::error::{set_last_error_static, AdResult}; +use crate::error::{AdResult, set_last_error_static}; use std::ffi::CStr; static OFF_MAIN_THREAD_MESSAGE: &CStr = diff --git a/crates/ffi/src/notifications/action.rs b/crates/ffi/src/notifications/action.rs index 29bdef2..8942585 100644 --- a/crates/ffi/src/notifications/action.rs +++ b/crates/ffi/src/notifications/action.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::actions::result::action_result_to_c; use crate::convert::string::{c_to_string, decode_optional_filter}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::AdActionResult; -use crate::AdAdapter; use agent_desktop_core::notification::NotificationIdentity; use std::os::raw::c_char; @@ -34,7 +34,7 @@ use std::os::raw::c_char; /// is rejected with `AD_RESULT_ERR_INVALID_ARGS` rather than silently /// treated as "no fingerprint". `out` must be a valid writable /// `*mut AdActionResult`; on error it is zero-initialized. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_notification_action( adapter: *const AdAdapter, index: u32, diff --git a/crates/ffi/src/notifications/dismiss.rs b/crates/ffi/src/notifications/dismiss.rs index 81269a6..65f00af 100644 --- a/crates/ffi/src/notifications/dismiss.rs +++ b/crates/ffi/src/notifications/dismiss.rs @@ -1,7 +1,7 @@ -use crate::convert::string::decode_optional_filter; -use crate::error::{set_last_error, AdResult}; -use crate::ffi_try::trap_panic; use crate::AdAdapter; +use crate::convert::string::decode_optional_filter; +use crate::error::{AdResult, set_last_error}; +use crate::ffi_try::trap_panic; use std::os::raw::c_char; /// Dismisses the notification at `index`. Indexes are only valid within @@ -11,7 +11,7 @@ use std::os::raw::c_char; /// /// # Safety /// `adapter` must be valid. `app_filter` may be null. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_dismiss_notification( adapter: *const AdAdapter, index: u32, diff --git a/crates/ffi/src/notifications/dismiss_all.rs b/crates/ffi/src/notifications/dismiss_all.rs index c26d72f..d5839b3 100644 --- a/crates/ffi/src/notifications/dismiss_all.rs +++ b/crates/ffi/src/notifications/dismiss_all.rs @@ -1,10 +1,10 @@ +use crate::AdAdapter; use crate::convert::notification::notification_info_to_c; use crate::convert::string::decode_optional_filter; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::notifications::list::ad_notification_list_free; use crate::types::{AdNotificationInfo, AdNotificationList}; -use crate::AdAdapter; use std::os::raw::c_char; use std::ptr; @@ -23,7 +23,7 @@ use std::ptr; /// # Safety /// `adapter` must be valid. `app_filter` may be null. `dismissed_out` /// and `failed_out` must both be valid writable `*mut *mut AdNotificationList`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_dismiss_all_notifications( adapter: *const AdAdapter, app_filter: *const c_char, @@ -86,7 +86,7 @@ pub unsafe extern "C" fn ad_dismiss_all_notifications( /// # Safety /// Both arguments must be null or pointers from /// `ad_dismiss_all_notifications`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_dismiss_all_notifications_free( dismissed: *mut AdNotificationList, failed: *mut AdNotificationList, diff --git a/crates/ffi/src/notifications/list.rs b/crates/ffi/src/notifications/list.rs index 0d7aa75..c9d3fe4 100644 --- a/crates/ffi/src/notifications/list.rs +++ b/crates/ffi/src/notifications/list.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::convert::notification::{free_notification_info_fields, notification_info_to_c}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::{trap_panic, trap_panic_void}; use crate::notifications::filter::filter_from_c; use crate::types::{AdNotificationFilter, AdNotificationInfo, AdNotificationList}; -use crate::AdAdapter; use std::ptr; /// Lists the notifications currently on-screen. @@ -17,7 +17,7 @@ use std::ptr; /// `adapter` must be valid. `filter` may be null. `out` must be a valid /// writable `*mut *mut AdNotificationList`. On success `*out` is a /// non-null handle freed with `ad_notification_list_free`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_list_notifications( adapter: *const AdAdapter, filter: *const AdNotificationFilter, @@ -58,7 +58,7 @@ pub unsafe extern "C" fn ad_list_notifications( /// # Safety /// `list` must be null or a pointer returned by `ad_list_notifications`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_notification_list_count(list: *const AdNotificationList) -> u32 { if list.is_null() { return 0; @@ -71,7 +71,7 @@ pub unsafe extern "C" fn ad_notification_list_count(list: *const AdNotificationL /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_notifications`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_notification_list_get( list: *const AdNotificationList, index: u32, @@ -90,7 +90,7 @@ pub unsafe extern "C" fn ad_notification_list_get( /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_notifications`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_notification_list_free(list: *mut AdNotificationList) { trap_panic_void(|| unsafe { if list.is_null() { diff --git a/crates/ffi/src/observation/find.rs b/crates/ffi/src/observation/find.rs index bb3b662..1ffcb99 100644 --- a/crates/ffi/src/observation/find.rs +++ b/crates/ffi/src/observation/find.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::convert::string::decode_optional_filter; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::observation::walk::find_first_match; use crate::types::{AdFindQuery, AdNativeHandle, AdWindowInfo}; -use crate::AdAdapter; use agent_desktop_core::adapter::{SnapshotSurface, TreeOptions}; use agent_desktop_core::refs::RefEntry; @@ -22,7 +22,7 @@ use agent_desktop_core::refs::RefEntry; /// `adapter`, `win`, and `query` must be valid pointers. `out_handle` /// must be a valid writable `*mut AdNativeHandle`. On /// `AD_RESULT_ERR_ELEMENT_NOT_FOUND` the out-handle is zero-initialized. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_find( adapter: *const AdAdapter, win: *const AdWindowInfo, @@ -102,7 +102,7 @@ pub unsafe extern "C" fn ad_find( source_app: None, source_window_title: Some(core_win.title.clone()), root_ref: None, - path: Vec::new(), + path: smallvec::SmallVec::new(), }; match adapter.inner.resolve_element(&ref_entry) { Ok(handle) => { diff --git a/crates/ffi/src/observation/get.rs b/crates/ffi/src/observation/get.rs index 1006607..b4f8920 100644 --- a/crates/ffi/src/observation/get.rs +++ b/crates/ffi/src/observation/get.rs @@ -1,8 +1,8 @@ +use crate::AdAdapter; use crate::convert::string::{c_to_string, string_to_c_lossy}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::AdNativeHandle; -use crate::AdAdapter; use agent_desktop_core::adapter::NativeHandle; use std::os::raw::c_char; @@ -20,7 +20,7 @@ use std::os::raw::c_char; /// `adapter` must be valid. `handle` must be a non-null `AdNativeHandle`. /// `property` must be a non-null UTF-8 C string. `out` must be a valid /// writable `*mut *mut c_char`; it is null-initialized on entry. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_get( adapter: *const AdAdapter, handle: *const AdNativeHandle, diff --git a/crates/ffi/src/observation/is.rs b/crates/ffi/src/observation/is.rs index 6eabf22..181bddf 100644 --- a/crates/ffi/src/observation/is.rs +++ b/crates/ffi/src/observation/is.rs @@ -1,8 +1,8 @@ +use crate::AdAdapter; use crate::convert::string::{c_to_string, decode_optional_filter}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::{AdFindQuery, AdWindowInfo}; -use crate::AdAdapter; use agent_desktop_core::adapter::{SnapshotSurface, TreeOptions}; use agent_desktop_core::node::AccessibilityNode; use std::os::raw::c_char; @@ -36,7 +36,7 @@ use std::os::raw::c_char; /// # Safety /// All pointers must be valid. `property` must be a non-null UTF-8 /// C string. `out` must be a valid writable `*mut bool`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_is( adapter: *const AdAdapter, win: *const AdWindowInfo, diff --git a/crates/ffi/src/screenshot/accessors.rs b/crates/ffi/src/screenshot/accessors.rs index 2aac1b0..0a1eb28 100644 --- a/crates/ffi/src/screenshot/accessors.rs +++ b/crates/ffi/src/screenshot/accessors.rs @@ -6,7 +6,7 @@ use std::ptr; /// /// # Safety /// `buf` must be null or returned by `ad_screenshot`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_data(buf: *const AdImageBuffer) -> *const u8 { if buf.is_null() { return ptr::null(); @@ -20,7 +20,7 @@ pub unsafe extern "C" fn ad_image_buffer_data(buf: *const AdImageBuffer) -> *con /// /// # Safety /// `buf` must be null or returned by `ad_screenshot`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_size(buf: *const AdImageBuffer) -> u64 { if buf.is_null() { return 0; @@ -33,7 +33,7 @@ pub unsafe extern "C" fn ad_image_buffer_size(buf: *const AdImageBuffer) -> u64 /// /// # Safety /// `buf` must be null or returned by `ad_screenshot`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_width(buf: *const AdImageBuffer) -> u32 { if buf.is_null() { return 0; @@ -46,7 +46,7 @@ pub unsafe extern "C" fn ad_image_buffer_width(buf: *const AdImageBuffer) -> u32 /// /// # Safety /// `buf` must be null or returned by `ad_screenshot`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_height(buf: *const AdImageBuffer) -> u32 { if buf.is_null() { return 0; @@ -60,7 +60,7 @@ pub unsafe extern "C" fn ad_image_buffer_height(buf: *const AdImageBuffer) -> u3 /// /// # Safety /// `buf` must be null or returned by `ad_screenshot`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_format(buf: *const AdImageBuffer) -> AdImageFormat { if buf.is_null() { return AdImageFormat::Png; diff --git a/crates/ffi/src/screenshot/capture.rs b/crates/ffi/src/screenshot/capture.rs index d6cd185..cabf179 100644 --- a/crates/ffi/src/screenshot/capture.rs +++ b/crates/ffi/src/screenshot/capture.rs @@ -1,7 +1,7 @@ -use crate::error::{set_last_error, AdResult}; +use crate::AdAdapter; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::{AdImageBuffer, AdImageFormat, AdScreenshotKind, AdScreenshotTarget}; -use crate::AdAdapter; use agent_desktop_core::adapter::{ImageFormat, ScreenshotTarget as CoreScreenshotTarget}; use std::ptr; @@ -14,7 +14,7 @@ use std::ptr; /// `adapter` and `target` must be valid pointers. `out` must be a valid /// writable `*mut *mut AdImageBuffer`. On error `*out` is null and /// last-error is set. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_screenshot( adapter: *const AdAdapter, target: *const AdScreenshotTarget, diff --git a/crates/ffi/src/screenshot/free.rs b/crates/ffi/src/screenshot/free.rs index 4e24dcb..311cac8 100644 --- a/crates/ffi/src/screenshot/free.rs +++ b/crates/ffi/src/screenshot/free.rs @@ -6,7 +6,7 @@ use crate::types::AdImageBuffer; /// # Safety /// `buf` must be null or a pointer previously returned by `ad_screenshot`. /// Double-free is undefined behavior. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_image_buffer_free(buf: *mut AdImageBuffer) { trap_panic_void(|| unsafe { if buf.is_null() { diff --git a/crates/ffi/src/surfaces/list.rs b/crates/ffi/src/surfaces/list.rs index 009d4da..4e22692 100644 --- a/crates/ffi/src/surfaces/list.rs +++ b/crates/ffi/src/surfaces/list.rs @@ -1,15 +1,15 @@ +use crate::AdAdapter; use crate::convert::surface::{free_surface_info_fields, surface_info_to_c}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::{trap_panic, trap_panic_void}; use crate::types::{AdSurfaceInfo, AdSurfaceList}; -use crate::AdAdapter; use std::ptr; /// # Safety /// `adapter` must be valid. `out` must be a valid writable /// `*mut *mut AdSurfaceList`. Success produces a list handle freed via /// `ad_surface_list_free`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_list_surfaces( adapter: *const AdAdapter, pid: i32, @@ -42,7 +42,7 @@ pub unsafe extern "C" fn ad_list_surfaces( /// # Safety /// `list` must be null or a pointer returned by `ad_list_surfaces`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_surface_list_count(list: *const AdSurfaceList) -> u32 { if list.is_null() { return 0; @@ -55,7 +55,7 @@ pub unsafe extern "C" fn ad_surface_list_count(list: *const AdSurfaceList) -> u3 /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_surfaces`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_surface_list_get( list: *const AdSurfaceList, index: u32, @@ -74,7 +74,7 @@ pub unsafe extern "C" fn ad_surface_list_get( /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_surfaces`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_surface_list_free(list: *mut AdSurfaceList) { trap_panic_void(|| unsafe { if list.is_null() { diff --git a/crates/ffi/src/tree/free.rs b/crates/ffi/src/tree/free.rs index 91c84dc..da92daf 100644 --- a/crates/ffi/src/tree/free.rs +++ b/crates/ffi/src/tree/free.rs @@ -4,41 +4,45 @@ use std::os::raw::c_char; use std::ptr; unsafe fn free_c_string_array(arr: *mut *mut c_char, count: u32) { - if arr.is_null() { - return; + unsafe { + if arr.is_null() { + return; + } + let slice = std::slice::from_raw_parts_mut(arr, count as usize); + for p in slice.iter_mut() { + free_c_string(*p); + } + drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( + arr, + count as usize, + ))); } - let slice = std::slice::from_raw_parts_mut(arr, count as usize); - for p in slice.iter_mut() { - free_c_string(*p); - } - drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( - arr, - count as usize, - ))); } unsafe fn free_node_fields(node: &mut AdNode) { - free_c_string(node.ref_id as *mut c_char); - free_c_string(node.role as *mut c_char); - free_c_string(node.name as *mut c_char); - free_c_string(node.value as *mut c_char); - free_c_string(node.description as *mut c_char); - free_c_string(node.hint as *mut c_char); - free_c_string_array(node.states, node.state_count); - node.ref_id = ptr::null(); - node.role = ptr::null(); - node.name = ptr::null(); - node.value = ptr::null(); - node.description = ptr::null(); - node.hint = ptr::null(); - node.states = ptr::null_mut(); - node.state_count = 0; + unsafe { + free_c_string(node.ref_id as *mut c_char); + free_c_string(node.role as *mut c_char); + free_c_string(node.name as *mut c_char); + free_c_string(node.value as *mut c_char); + free_c_string(node.description as *mut c_char); + free_c_string(node.hint as *mut c_char); + free_c_string_array(node.states, node.state_count); + node.ref_id = ptr::null(); + node.role = ptr::null(); + node.name = ptr::null(); + node.value = ptr::null(); + node.description = ptr::null(); + node.hint = ptr::null(); + node.states = ptr::null_mut(); + node.state_count = 0; + } } /// # Safety /// `tree` must be null or point to a valid `AdNodeTree` previously returned /// by `flatten_tree` or `ad_get_tree`. After this call the tree is zeroed. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_free_tree(tree: *mut AdNodeTree) { crate::ffi_try::trap_panic_void(|| unsafe { if tree.is_null() { diff --git a/crates/ffi/src/tree/get.rs b/crates/ffi/src/tree/get.rs index 6127066..7d57ad7 100644 --- a/crates/ffi/src/tree/get.rs +++ b/crates/ffi/src/tree/get.rs @@ -1,8 +1,8 @@ -use crate::error::{set_last_error, AdResult}; +use crate::AdAdapter; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::tree::flatten::flatten_tree; use crate::types::{AdNodeTree, AdSnapshotSurface, AdTreeOptions, AdWindowInfo}; -use crate::AdAdapter; use agent_desktop_core::adapter::SnapshotSurface; use std::ptr; @@ -58,7 +58,7 @@ fn core_surface(s: AdSnapshotSurface) -> SnapshotSurface { /// # Safety /// All pointers must be non-null. `win.id` and `win.title` must be /// valid UTF-8 C strings. `out` must be writable. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_get_tree( adapter: *const AdAdapter, win: *const AdWindowInfo, diff --git a/crates/ffi/src/windows/focus.rs b/crates/ffi/src/windows/focus.rs index 482955e..177e859 100644 --- a/crates/ffi/src/windows/focus.rs +++ b/crates/ffi/src/windows/focus.rs @@ -1,8 +1,8 @@ -use crate::error::{set_last_error, AdResult}; +use crate::AdAdapter; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::AdWindowInfo; use crate::windows::to_core::ad_window_to_core; -use crate::AdAdapter; /// Brings `win` to the foreground on the current space. Returns /// `AD_RESULT_ERR_WINDOW_NOT_FOUND` when the referenced window no longer @@ -12,7 +12,7 @@ use crate::AdAdapter; /// `adapter` must be a non-null pointer from `ad_adapter_create`. `win` /// must be a non-null pointer to an `AdWindowInfo` whose `id` and /// `title` fields are non-null, valid UTF-8 C strings. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_focus_window( adapter: *const AdAdapter, win: *const AdWindowInfo, diff --git a/crates/ffi/src/windows/free_one.rs b/crates/ffi/src/windows/free_one.rs index 7683d86..cbb3c37 100644 --- a/crates/ffi/src/windows/free_one.rs +++ b/crates/ffi/src/windows/free_one.rs @@ -16,7 +16,7 @@ use crate::types::AdWindowInfo; /// `win` must be null or point to a valid `AdWindowInfo` whose string /// fields were allocated by this crate. Do not call on pointers inside /// an `AdWindowList` — free the list instead. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_release_window_fields(win: *mut AdWindowInfo) { trap_panic_void(|| unsafe { if win.is_null() { diff --git a/crates/ffi/src/windows/list.rs b/crates/ffi/src/windows/list.rs index 93e7059..41694f8 100644 --- a/crates/ffi/src/windows/list.rs +++ b/crates/ffi/src/windows/list.rs @@ -1,9 +1,9 @@ +use crate::AdAdapter; use crate::convert::string::decode_optional_filter; use crate::convert::window::{free_window_info_fields, window_info_to_c}; -use crate::error::{set_last_error, AdResult}; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::{trap_panic, trap_panic_void}; use crate::types::{AdWindowInfo, AdWindowList}; -use crate::AdAdapter; use agent_desktop_core::adapter::WindowFilter; use std::os::raw::c_char; use std::ptr; @@ -12,7 +12,7 @@ use std::ptr; /// `adapter` must be valid. `out` must be a valid writable /// `*mut *mut AdWindowList`. `app_filter` may be null or a C string. /// Success produces a list handle freed via `ad_window_list_free`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_list_windows( adapter: *const AdAdapter, app_filter: *const c_char, @@ -50,7 +50,7 @@ pub unsafe extern "C" fn ad_list_windows( /// # Safety /// `list` must be null or a pointer returned by `ad_list_windows`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_window_list_count(list: *const AdWindowList) -> u32 { if list.is_null() { return 0; @@ -63,7 +63,7 @@ pub unsafe extern "C" fn ad_window_list_count(list: *const AdWindowList) -> u32 /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_windows`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_window_list_get( list: *const AdWindowList, index: u32, @@ -82,7 +82,7 @@ pub unsafe extern "C" fn ad_window_list_get( /// /// # Safety /// `list` must be null or a pointer returned by `ad_list_windows`. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_window_list_free(list: *mut AdWindowList) { trap_panic_void(|| unsafe { if list.is_null() { diff --git a/crates/ffi/src/windows/op.rs b/crates/ffi/src/windows/op.rs index 10d08cd..5a9fbe4 100644 --- a/crates/ffi/src/windows/op.rs +++ b/crates/ffi/src/windows/op.rs @@ -1,8 +1,8 @@ -use crate::error::{set_last_error, AdResult}; +use crate::AdAdapter; +use crate::error::{AdResult, set_last_error}; use crate::ffi_try::trap_panic; use crate::types::{AdWindowInfo, AdWindowOp, AdWindowOpKind}; use crate::windows::to_core::ad_window_to_core; -use crate::AdAdapter; use agent_desktop_core::action::WindowOp; /// Performs a window-manager operation (`Resize`, `Move`, `Minimize`, @@ -15,7 +15,7 @@ use agent_desktop_core::action::WindowOp; /// # Safety /// `adapter` and `win` must be non-null pointers. `win.id` and /// `win.title` must be non-null valid UTF-8 C strings. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn ad_window_op( adapter: *const AdAdapter, win: *const AdWindowInfo, diff --git a/crates/ffi/tests/c_abi_actions.rs b/crates/ffi/tests/c_abi_actions.rs index 12bd6e6..7f5a59d 100644 --- a/crates/ffi/tests/c_abi_actions.rs +++ b/crates/ffi/tests/c_abi_actions.rs @@ -1,8 +1,8 @@ mod common; use common::{ - ad_execute_action, ad_execute_action_with_policy, default_action, with_adapter, AdActionResult, - AdNativeHandle, AdPolicyKind, AdResult, + AdActionResult, AdNativeHandle, AdPolicyKind, AdResult, ad_execute_action, + ad_execute_action_with_policy, default_action, with_adapter, }; #[test] diff --git a/crates/ffi/tests/c_abi_inputs.rs b/crates/ffi/tests/c_abi_inputs.rs index 0af892f..a364f92 100644 --- a/crates/ffi/tests/c_abi_inputs.rs +++ b/crates/ffi/tests/c_abi_inputs.rs @@ -1,8 +1,8 @@ mod common; use common::{ - ad_launch_app, ad_list_windows, ad_resolve_element, c_char, with_adapter, AdNativeHandle, - AdRefEntry, AdResult, AdWindowInfo, AdWindowList, + AdNativeHandle, AdRefEntry, AdResult, AdWindowInfo, AdWindowList, ad_launch_app, + ad_list_windows, ad_resolve_element, c_char, with_adapter, }; #[test] diff --git a/crates/ffi/tests/c_abi_lifecycle.rs b/crates/ffi/tests/c_abi_lifecycle.rs index 2af3865..24821aa 100644 --- a/crates/ffi/tests/c_abi_lifecycle.rs +++ b/crates/ffi/tests/c_abi_lifecycle.rs @@ -1,10 +1,10 @@ mod common; use common::{ + AdAppList, AdFindQuery, AdNativeHandle, AdResult, AdWindowInfo, AdWindowList, CStr, ad_adapter_create, ad_adapter_destroy, ad_app_list_count, ad_app_list_free, ad_app_list_get, ad_check_permissions, ad_find, ad_free_handle, ad_last_error_code, ad_last_error_message, ad_list_apps, ad_list_windows, ad_window_list_count, ad_window_list_free, with_adapter, - AdAppList, AdFindQuery, AdNativeHandle, AdResult, AdWindowInfo, AdWindowList, CStr, }; #[test] diff --git a/crates/ffi/tests/common/mod.rs b/crates/ffi/tests/common/mod.rs index 4cf8fc7..5892d2b 100644 --- a/crates/ffi/tests/common/mod.rs +++ b/crates/ffi/tests/common/mod.rs @@ -10,7 +10,7 @@ pub use agent_desktop_ffi::{ pub use std::ffi::CStr; pub use std::os::raw::c_char; -extern "C" { +unsafe extern "C" { pub fn ad_adapter_create() -> *mut AdAdapter; pub fn ad_adapter_destroy(adapter: *mut AdAdapter); pub fn ad_check_permissions(adapter: *const AdAdapter) -> AdResult; diff --git a/crates/ffi/tests/error_lifetime.rs b/crates/ffi/tests/error_lifetime.rs index 2cbca7d..a03a7e9 100644 --- a/crates/ffi/tests/error_lifetime.rs +++ b/crates/ffi/tests/error_lifetime.rs @@ -2,7 +2,7 @@ use agent_desktop_ffi::error::AdResult; use std::ffi::CStr; #[allow(improper_ctypes)] -extern "C" { +unsafe extern "C" { fn ad_adapter_create() -> *mut agent_desktop_ffi::AdAdapter; fn ad_adapter_destroy(adapter: *mut agent_desktop_ffi::AdAdapter); fn ad_launch_app( diff --git a/crates/linux/Cargo.toml b/crates/linux/Cargo.toml index 0e096f8..558f46c 100644 --- a/crates/linux/Cargo.toml +++ b/crates/linux/Cargo.toml @@ -7,3 +7,6 @@ license.workspace = true [dependencies] agent-desktop-core.workspace = true thiserror.workspace = true + +[lints] +workspace = true diff --git a/crates/macos/Cargo.toml b/crates/macos/Cargo.toml index 000b85c..208a431 100644 --- a/crates/macos/Cargo.toml +++ b/crates/macos/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true [dependencies] +smallvec.workspace = true agent-desktop-core.workspace = true thiserror.workspace = true serde.workspace = true @@ -27,3 +28,6 @@ name = "ax_probe" required-features = ["dev-tools"] [build-dependencies] + +[lints] +workspace = true diff --git a/crates/macos/examples/ax_probe.rs b/crates/macos/examples/ax_probe.rs index ba02f3e..02c1c1d 100644 --- a/crates/macos/examples/ax_probe.rs +++ b/crates/macos/examples/ax_probe.rs @@ -76,7 +76,10 @@ fn probe_app(pid: i32) { // ── 3. Test kAXPressAction on each child ──────────────────────── let ax_err = ax_press(*child); - println!(" kAXPressAction → err={} (0=ok, -25200=fail, -25205=not_supported)", ax_err); + println!( + " kAXPressAction → err={} (0=ok, -25200=fail, -25205=not_supported)", + ax_err + ); // ── 4. Test CGEvent click at element center ───────────────────── if let (Some(p), Some(s)) = (cpos, csz) { @@ -217,11 +220,7 @@ fn read_cgpoint(el: accessibility_sys::AXUIElementRef, attr: &str) -> Option<(f6 ) }; unsafe { core_foundation::base::CFRelease(val) }; - if ok { - Some((pt.x, pt.y)) - } else { - None - } + if ok { Some((pt.x, pt.y)) } else { None } } #[cfg(target_os = "macos")] diff --git a/crates/macos/src/actions/ax_helpers.rs b/crates/macos/src/actions/ax_helpers.rs index 6ef46b5..b8976d2 100644 --- a/crates/macos/src/actions/ax_helpers.rs +++ b/crates/macos/src/actions/ax_helpers.rs @@ -5,9 +5,9 @@ mod imp { use super::*; use crate::tree::AXElement; use accessibility_sys::{ - kAXErrorAPIDisabled, kAXErrorCannotComplete, kAXErrorSuccess, kAXFocusedAttribute, - kAXValueAttribute, AXUIElementCopyActionNames, AXUIElementIsAttributeSettable, - AXUIElementPerformAction, AXUIElementSetAttributeValue, AXUIElementSetMessagingTimeout, + AXUIElementCopyActionNames, AXUIElementIsAttributeSettable, AXUIElementPerformAction, + AXUIElementSetAttributeValue, AXUIElementSetMessagingTimeout, kAXErrorAPIDisabled, + kAXErrorCannotComplete, kAXErrorSuccess, kAXFocusedAttribute, kAXValueAttribute, }; use core_foundation::{ array::CFArray, @@ -17,17 +17,20 @@ mod imp { }; use std::os::raw::c_uchar; - pub fn try_ax_action(el: &AXElement, name: &str) -> bool { + pub(crate) fn try_ax_action(el: &AXElement, name: &str) -> bool { let action = CFString::new(name); let err = unsafe { AXUIElementPerformAction(el.0, action.as_concrete_TypeRef()) }; err == kAXErrorSuccess } - pub fn try_ax_action_retried(el: &AXElement, name: &str) -> bool { + pub(crate) fn try_ax_action_retried(el: &AXElement, name: &str) -> bool { try_ax_action_retried_or_err(el, name).unwrap_or(false) } - pub fn try_ax_action_retried_or_err(el: &AXElement, name: &str) -> Result { + pub(crate) fn try_ax_action_retried_or_err( + el: &AXElement, + name: &str, + ) -> Result { let action = CFString::new(name); let err = unsafe { AXUIElementPerformAction(el.0, action.as_concrete_TypeRef()) }; if err == kAXErrorSuccess { @@ -46,11 +49,11 @@ mod imp { Ok(false) } - pub fn set_ax_bool(el: &AXElement, attr: &str, value: bool) -> bool { + pub(crate) fn set_ax_bool(el: &AXElement, attr: &str, value: bool) -> bool { set_ax_bool_or_err(el, attr, value).unwrap_or(false) } - pub fn set_ax_bool_or_err( + pub(crate) fn set_ax_bool_or_err( el: &AXElement, attr: &str, value: bool, @@ -71,7 +74,7 @@ mod imp { Ok(false) } - pub fn set_ax_string_or_err( + pub(crate) fn set_ax_string_or_err( el: &AXElement, attr: &str, value: &str, @@ -92,7 +95,7 @@ mod imp { Ok(()) } - pub fn is_attr_settable(el: &AXElement, attr: &str) -> bool { + pub(crate) fn is_attr_settable(el: &AXElement, attr: &str) -> bool { let cf_attr = CFString::new(attr); let mut settable: c_uchar = 0; let err = unsafe { @@ -101,7 +104,7 @@ mod imp { err == kAXErrorSuccess && settable != 0 } - pub fn list_ax_actions(el: &AXElement) -> Vec { + pub(crate) fn list_ax_actions(el: &AXElement) -> Vec { let mut actions_ref: core_foundation_sys::array::CFArrayRef = std::ptr::null(); let err = unsafe { AXUIElementCopyActionNames(el.0, &mut actions_ref) }; if err != kAXErrorSuccess || actions_ref.is_null() { @@ -117,11 +120,15 @@ mod imp { result } - pub fn has_ax_action(el: &AXElement, target: &str) -> bool { + pub(crate) fn has_ax_action(el: &AXElement, target: &str) -> bool { list_ax_actions(el).iter().any(|a| a == target) } - pub fn try_action_from_list(el: &AXElement, actions: &[String], targets: &[&str]) -> bool { + pub(crate) fn try_action_from_list( + el: &AXElement, + actions: &[String], + targets: &[&str], + ) -> bool { for target in targets { if actions.iter().any(|a| a == target) && try_ax_action(el, target) { return true; @@ -130,7 +137,11 @@ mod imp { false } - pub fn try_each_child(el: &AXElement, f: impl Fn(&AXElement) -> bool, limit: usize) -> bool { + pub(crate) fn try_each_child( + el: &AXElement, + f: impl Fn(&AXElement) -> bool, + limit: usize, + ) -> bool { let children = crate::tree::copy_ax_array(el, "AXChildren").unwrap_or_default(); for child in children.iter().take(limit) { if f(child) { @@ -140,7 +151,11 @@ mod imp { false } - pub fn try_each_ancestor(el: &AXElement, f: impl Fn(&AXElement) -> bool, limit: usize) -> bool { + pub(crate) fn try_each_ancestor( + el: &AXElement, + f: impl Fn(&AXElement) -> bool, + limit: usize, + ) -> bool { let mut current = crate::tree::copy_element_attr(el, "AXParent"); for _ in 0..limit { let ancestor = match ¤t { @@ -155,28 +170,28 @@ mod imp { false } - pub fn ensure_visible(el: &AXElement) { + pub(crate) fn ensure_visible(el: &AXElement) { let action = CFString::new("AXScrollToVisible"); unsafe { AXUIElementPerformAction(el.0, action.as_concrete_TypeRef()) }; } - pub fn set_messaging_timeout(el: &AXElement, seconds: f32) { + pub(crate) fn set_messaging_timeout(el: &AXElement, seconds: f32) { unsafe { AXUIElementSetMessagingTimeout(el.0, seconds) }; } - pub fn ax_focus_or_err(el: &AXElement) -> Result { + pub(crate) fn ax_focus_or_err(el: &AXElement) -> Result { set_ax_bool_or_err(el, kAXFocusedAttribute, true) } - pub fn ax_set_value(el: &AXElement, val: &str) -> Result<(), AdapterError> { + pub(crate) fn ax_set_value(el: &AXElement, val: &str) -> Result<(), AdapterError> { set_ax_string_or_err(el, kAXValueAttribute, val) } - pub fn ax_press(el: &AXElement) -> bool { + pub(crate) fn ax_press(el: &AXElement) -> bool { try_ax_action(el, "AXPress") } - pub fn element_role(el: &AXElement) -> Option { + pub(crate) fn element_role(el: &AXElement) -> Option { use accessibility_sys::kAXRoleAttribute; crate::tree::copy_string_attr(el, kAXRoleAttribute) .map(|r| crate::tree::roles::ax_role_to_str(&r).to_string()) diff --git a/crates/macos/src/actions/chain.rs b/crates/macos/src/actions/chain.rs index 52ed70c..13bb467 100644 --- a/crates/macos/src/actions/chain.rs +++ b/crates/macos/src/actions/chain.rs @@ -17,7 +17,7 @@ mod imp { const DEFAULT_CHAIN_TIMEOUT: Duration = Duration::from_secs(10); const MAX_CHAIN_TIMEOUT_MS: u64 = 300_000; - pub fn execute_chain( + pub(crate) fn execute_chain( el: &AXElement, caps: &ElementCaps, def: &ChainDef, diff --git a/crates/macos/src/actions/chain_defs.rs b/crates/macos/src/actions/chain_defs.rs index f863bff..92f41de 100644 --- a/crates/macos/src/actions/chain_defs.rs +++ b/crates/macos/src/actions/chain_defs.rs @@ -12,7 +12,7 @@ mod imp { use crate::tree::AXElement; use agent_desktop_core::action::{InteractionPolicy, MouseButton}; - pub static CLICK_CHAIN: ChainDef = ChainDef { + pub(crate) static CLICK_CHAIN: ChainDef = ChainDef { pre_scroll: true, steps: &[ ChainStep::Custom { @@ -66,7 +66,7 @@ mod imp { suggestion: "Element may not be interactable. Try 'mouse-click --xy X,Y'.", }; - pub static RIGHT_CLICK_CHAIN: ChainDef = ChainDef { + pub(crate) static RIGHT_CLICK_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::Custom { @@ -97,7 +97,7 @@ mod imp { suggestion: "Try 'mouse-click --button right --xy X,Y'.", }; - pub static EXPAND_CHAIN: ChainDef = ChainDef { + pub(crate) static EXPAND_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::Action("AXExpand"), @@ -113,7 +113,7 @@ mod imp { suggestion: "Try 'click' to open it instead.", }; - pub static COLLAPSE_CHAIN: ChainDef = ChainDef { + pub(crate) static COLLAPSE_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::Action("AXCollapse"), @@ -134,13 +134,13 @@ mod imp { ChainStep::FocusThenSetDynamic { attr: "AXValue" }, ]; - pub static SET_VALUE_CHAIN: ChainDef = ChainDef { + pub(crate) static SET_VALUE_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: VALUE_STEPS, suggestion: "Try 'clear' then 'type', or check element is a text field.", }; - pub static CLEAR_CHAIN: ChainDef = ChainDef { + pub(crate) static CLEAR_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::SetDynamic { attr: "AXValue" }, @@ -150,7 +150,7 @@ mod imp { suggestion: "Try 'press cmd+a' then 'press delete'.", }; - pub static FOCUS_CHAIN: ChainDef = ChainDef { + pub(crate) static FOCUS_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::SetBool { @@ -171,7 +171,7 @@ mod imp { suggestion: "Try 'click' to focus the element instead.", }; - pub static SCROLL_TO_CHAIN: ChainDef = ChainDef { + pub(crate) static SCROLL_TO_CHAIN: ChainDef = ChainDef { pre_scroll: false, steps: &[ ChainStep::Action("AXScrollToVisible"), @@ -183,7 +183,7 @@ mod imp { suggestion: "Element may not be in a scrollable container.", }; - pub fn double_click( + pub(crate) fn double_click( el: &AXElement, _caps: &ElementCaps, policy: InteractionPolicy, @@ -194,7 +194,7 @@ mod imp { crate::actions::dispatch::click_via_bounds(el, MouseButton::Left, 2, policy) } - pub fn triple_click( + pub(crate) fn triple_click( el: &AXElement, _caps: &ElementCaps, policy: InteractionPolicy, @@ -208,6 +208,6 @@ mod imp {} #[cfg(target_os = "macos")] pub(crate) use imp::{ - double_click, triple_click, CLEAR_CHAIN, CLICK_CHAIN, COLLAPSE_CHAIN, EXPAND_CHAIN, - FOCUS_CHAIN, RIGHT_CLICK_CHAIN, SCROLL_TO_CHAIN, SET_VALUE_CHAIN, + CLEAR_CHAIN, CLICK_CHAIN, COLLAPSE_CHAIN, EXPAND_CHAIN, FOCUS_CHAIN, RIGHT_CLICK_CHAIN, + SCROLL_TO_CHAIN, SET_VALUE_CHAIN, double_click, triple_click, }; diff --git a/crates/macos/src/actions/chain_menu_steps.rs b/crates/macos/src/actions/chain_menu_steps.rs index 28ec599..e62a7ec 100644 --- a/crates/macos/src/actions/chain_menu_steps.rs +++ b/crates/macos/src/actions/chain_menu_steps.rs @@ -4,11 +4,11 @@ mod imp { use crate::tree::AXElement; use agent_desktop_core::error::AdapterError; - pub fn show_menu(el: &AXElement, _caps: &ElementCaps) -> Result { + pub(crate) fn show_menu(el: &AXElement, _caps: &ElementCaps) -> Result { show_menu_on_element(el) } - pub fn show_menu_on_ancestors( + pub(crate) fn show_menu_on_ancestors( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -25,7 +25,7 @@ mod imp { Ok(false) } - pub fn show_menu_on_children( + pub(crate) fn show_menu_on_children( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -41,7 +41,7 @@ mod imp { Ok(false) } - pub fn select_then_show_menu( + pub(crate) fn select_then_show_menu( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -52,7 +52,7 @@ mod imp { show_menu_on_element(el) } - pub fn select_then_selected_items_menu( + pub(crate) fn select_then_selected_items_menu( el: &AXElement, _caps: &ElementCaps, ) -> Result { diff --git a/crates/macos/src/actions/chain_steps.rs b/crates/macos/src/actions/chain_steps.rs index e996a1e..2cdd98f 100644 --- a/crates/macos/src/actions/chain_steps.rs +++ b/crates/macos/src/actions/chain_steps.rs @@ -4,7 +4,10 @@ mod imp { use crate::tree::AXElement; use agent_desktop_core::error::AdapterError; - pub fn do_verified_press(el: &AXElement, caps: &ElementCaps) -> Result { + pub(crate) fn do_verified_press( + el: &AXElement, + caps: &ElementCaps, + ) -> Result { dispatch_verified_press(el, caps, crate::actions::chain_web_steps::is_in_webarea(el)) } @@ -52,7 +55,10 @@ mod imp { Ok(false) } - pub fn try_value_relay(el: &AXElement, _caps: &ElementCaps) -> Result { + pub(crate) fn try_value_relay( + el: &AXElement, + _caps: &ElementCaps, + ) -> Result { if !ax_helpers::list_ax_actions(el).is_empty() { return Ok(false); } @@ -119,7 +125,7 @@ mod imp { crate::tree::same_element(&owner_window, expected_window) } - pub fn element_is_visible_in_scroll_context( + pub(crate) fn element_is_visible_in_scroll_context( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -161,7 +167,7 @@ mod imp { x >= outer.x && x <= outer.x + outer.width && y >= outer.y && y <= outer.y + outer.height } - pub fn try_show_alternate_ui( + pub(crate) fn try_show_alternate_ui( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -180,7 +186,7 @@ mod imp { )) } - pub fn try_parent_row_select( + pub(crate) fn try_parent_row_select( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -198,7 +204,7 @@ mod imp { ax_helpers::set_ax_bool_or_err(&parent, "AXSelected", true) } - pub fn try_select_containing_item( + pub(crate) fn try_select_containing_item( el: &AXElement, _caps: &ElementCaps, ) -> Result { @@ -238,7 +244,7 @@ mod imp { } fn set_container_selection(candidate: &AXElement, attr: &str) -> bool { - use accessibility_sys::{kAXErrorSuccess, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess}; use core_foundation::{ array::CFArray, base::{CFRetain, CFType, CFTypeRef, TCFType}, @@ -275,11 +281,11 @@ mod imp { .any(|selected| crate::tree::same_element(selected, candidate)) } - pub fn try_select_via_parent( + pub(crate) fn try_select_via_parent( el: &AXElement, _caps: &ElementCaps, ) -> Result { - use accessibility_sys::{kAXErrorSuccess, kAXRoleAttribute, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess, kAXRoleAttribute}; use core_foundation::{ array::CFArray, base::{CFRetain, CFType, CFTypeRef, TCFType}, @@ -311,7 +317,10 @@ mod imp { Ok(err == kAXErrorSuccess) } - pub fn try_custom_actions(el: &AXElement, _caps: &ElementCaps) -> Result { + pub(crate) fn try_custom_actions( + el: &AXElement, + _caps: &ElementCaps, + ) -> Result { let has = !crate::tree::copy_ax_array(el, "AXCustomActions") .unwrap_or_default() .is_empty(); diff --git a/crates/macos/src/actions/chain_web_steps.rs b/crates/macos/src/actions/chain_web_steps.rs index f31d903..39d68e6 100644 --- a/crates/macos/src/actions/chain_web_steps.rs +++ b/crates/macos/src/actions/chain_web_steps.rs @@ -19,7 +19,7 @@ mod imp { } } - pub fn is_in_webarea(el: &AXElement) -> bool { + pub(crate) fn is_in_webarea(el: &AXElement) -> bool { use accessibility_sys::kAXRoleAttribute; let mut current = crate::tree::copy_element_attr(el, "AXParent"); for _ in 0..20 { @@ -36,7 +36,7 @@ mod imp { false } - pub fn activate_web_element(el: &AXElement) -> bool { + pub(crate) fn activate_web_element(el: &AXElement) -> bool { let Some(pid) = crate::system::app_ops::pid_from_element(el) else { return false; }; diff --git a/crates/macos/src/actions/discovery.rs b/crates/macos/src/actions/discovery.rs index d574662..534a766 100644 --- a/crates/macos/src/actions/discovery.rs +++ b/crates/macos/src/actions/discovery.rs @@ -1,13 +1,13 @@ use crate::tree::AXElement; -pub struct ElementCaps { +pub(crate) struct ElementCaps { pub settable_focus: bool, pub settable_selected: bool, pub settable_disclosing: bool, } #[cfg(target_os = "macos")] -pub fn discover(el: &AXElement) -> ElementCaps { +pub(crate) fn discover(el: &AXElement) -> ElementCaps { use crate::actions::ax_helpers; ElementCaps { settable_focus: ax_helpers::is_attr_settable(el, "AXFocused"), diff --git a/crates/macos/src/actions/dispatch.rs b/crates/macos/src/actions/dispatch.rs index fc24662..f50571f 100644 --- a/crates/macos/src/actions/dispatch.rs +++ b/crates/macos/src/actions/dispatch.rs @@ -11,12 +11,12 @@ mod imp { use super::*; use crate::actions::{ ax_helpers, - chain::{execute_chain, ChainContext}, + chain::{ChainContext, execute_chain}, chain_defs, discovery, toggle_state, }; use crate::tree::AXElement; - pub fn click_via_bounds( + pub(crate) fn click_via_bounds( el: &AXElement, button: MouseButton, count: u32, @@ -58,7 +58,7 @@ mod imp { }) } - pub fn perform_action( + pub(crate) fn perform_action( el: &AXElement, request: &ActionRequest, ) -> Result { @@ -217,7 +217,7 @@ mod imp { Ok(result) } - pub fn ax_press_or_fail(el: &AXElement, context: &str) -> Result<(), AdapterError> { + pub(crate) fn ax_press_or_fail(el: &AXElement, context: &str) -> Result<(), AdapterError> { if !ax_helpers::ax_press(el) { return Err(AdapterError::new( ErrorCode::ActionFailed, @@ -242,7 +242,7 @@ mod imp { } } -pub use imp::perform_action; +pub(crate) use imp::perform_action; #[cfg(target_os = "macos")] pub(crate) use imp::{ax_press_or_fail, click_via_bounds}; diff --git a/crates/macos/src/actions/extras.rs b/crates/macos/src/actions/extras.rs index 019c84a..f4c5857 100644 --- a/crates/macos/src/actions/extras.rs +++ b/crates/macos/src/actions/extras.rs @@ -166,7 +166,7 @@ fn menu_opened_after_action(was_open: bool, is_open: bool) -> bool { #[cfg(target_os = "macos")] fn find_and_press_menu_item(el: &AXElement, target_value: &str) -> bool { - use accessibility_sys::{kAXPressAction, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXPressAction}; use core_foundation::{base::TCFType, string::CFString}; let children = crate::tree::copy_ax_array(el, "AXChildren").unwrap_or_default(); @@ -197,7 +197,7 @@ fn press_escape(el: &AXElement) { #[cfg(target_os = "macos")] fn select_child_by_name(el: &AXElement, name: &str) -> bool { - use accessibility_sys::{kAXPressAction, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXPressAction}; use core_foundation::{base::TCFType, string::CFString}; let children = crate::tree::copy_ax_array(el, "AXChildren").unwrap_or_default(); diff --git a/crates/macos/src/actions/scroll.rs b/crates/macos/src/actions/scroll.rs index 90edaa7..9d2934e 100644 --- a/crates/macos/src/actions/scroll.rs +++ b/crates/macos/src/actions/scroll.rs @@ -12,7 +12,7 @@ pub(crate) fn ax_scroll( policy: agent_desktop_core::action::InteractionPolicy, ) -> Result<(), AdapterError> { use accessibility_sys::{ - kAXErrorSuccess, AXUIElementPerformAction, AXUIElementSetAttributeValue, + AXUIElementPerformAction, AXUIElementSetAttributeValue, kAXErrorSuccess, }; use agent_desktop_core::action::Direction; use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString}; @@ -157,7 +157,7 @@ fn try_scroll_bar_value_shift( direction: &agent_desktop_core::action::Direction, amount: u32, ) -> bool { - use accessibility_sys::{kAXErrorSuccess, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess}; use agent_desktop_core::action::Direction; use core_foundation::{base::TCFType, number::CFNumber, string::CFString}; @@ -180,7 +180,7 @@ fn try_scroll_bar_value_shift( #[cfg(target_os = "macos")] fn read_scroll_bar_value(bar: &AXElement) -> Option { - use accessibility_sys::{kAXErrorSuccess, AXUIElementCopyAttributeValue}; + use accessibility_sys::{AXUIElementCopyAttributeValue, kAXErrorSuccess}; use core_foundation::{base::TCFType, number::CFNumber, string::CFString}; let cf_attr = CFString::new("AXValue"); @@ -199,7 +199,7 @@ fn try_scroll_bar_sub_elements( bar: &AXElement, direction: &agent_desktop_core::action::Direction, ) -> bool { - use accessibility_sys::{kAXErrorSuccess, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXErrorSuccess}; use agent_desktop_core::action::Direction; use core_foundation::{base::TCFType, string::CFString}; @@ -226,7 +226,7 @@ fn try_focus_child_in_direction( scroll_area: &AXElement, _direction: &agent_desktop_core::action::Direction, ) -> bool { - use accessibility_sys::{kAXErrorSuccess, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess}; use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString}; let children = crate::tree::copy_ax_array(scroll_area, "AXChildren").unwrap_or_default(); @@ -255,7 +255,7 @@ fn try_select_row_in_direction( scroll_area: &AXElement, _direction: &agent_desktop_core::action::Direction, ) -> bool { - use accessibility_sys::{kAXErrorSuccess, kAXRoleAttribute, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess, kAXRoleAttribute}; use core_foundation::{ array::CFArray, base::{CFRetain, CFType, CFTypeRef, TCFType}, diff --git a/crates/macos/src/actions/toggle_state.rs b/crates/macos/src/actions/toggle_state.rs index 767ceb8..25cc346 100644 --- a/crates/macos/src/actions/toggle_state.rs +++ b/crates/macos/src/actions/toggle_state.rs @@ -6,7 +6,7 @@ use agent_desktop_core::{ use crate::{ actions::{ ax_helpers, - chain::{execute_chain, ChainContext}, + chain::{ChainContext, execute_chain}, chain_defs, discovery, }, tree::AXElement, diff --git a/crates/macos/src/adapter.rs b/crates/macos/src/adapter.rs index 59bf462..9a3ba27 100644 --- a/crates/macos/src/adapter.rs +++ b/crates/macos/src/adapter.rs @@ -1,4 +1,5 @@ use agent_desktop_core::{ + PermissionReport, action::{ActionRequest, ActionResult, DragParams, ElementState, MouseEvent, WindowOp}, adapter::{ ImageBuffer, NativeHandle, PlatformAdapter, ScreenshotTarget, SnapshotSurface, TreeOptions, @@ -8,7 +9,6 @@ use agent_desktop_core::{ node::{AccessibilityNode, AppInfo, Rect, SurfaceInfo, WindowInfo}, notification::{NotificationFilter, NotificationIdentity, NotificationInfo}, refs::RefEntry, - PermissionReport, }; use rustc_hash::FxHashSet; diff --git a/crates/macos/src/input/clipboard.rs b/crates/macos/src/input/clipboard.rs index a1805f2..2435f95 100644 --- a/crates/macos/src/input/clipboard.rs +++ b/crates/macos/src/input/clipboard.rs @@ -10,7 +10,7 @@ mod imp { type Class = *mut c_void; type Sel = *mut c_void; - extern "C" { + unsafe extern "C" { fn objc_getClass(name: *const core::ffi::c_char) -> Class; fn sel_registerName(name: *const core::ffi::c_char) -> Sel; fn objc_msgSend(receiver: Id, sel: Sel, ...) -> Id; diff --git a/crates/macos/src/input/keyboard.rs b/crates/macos/src/input/keyboard.rs index c5b28f6..50d9059 100644 --- a/crates/macos/src/input/keyboard.rs +++ b/crates/macos/src/input/keyboard.rs @@ -7,8 +7,8 @@ use agent_desktop_core::{ mod imp { use super::*; use accessibility_sys::{ - kAXErrorCannotComplete, kAXErrorSuccess, AXUIElementCreateSystemWide, - AXUIElementPostKeyboardEvent, + AXUIElementCreateSystemWide, AXUIElementPostKeyboardEvent, kAXErrorCannotComplete, + kAXErrorSuccess, }; use std::time::Duration; diff --git a/crates/macos/src/input/mouse.rs b/crates/macos/src/input/mouse.rs index 7f618e4..eb0da8d 100644 --- a/crates/macos/src/input/mouse.rs +++ b/crates/macos/src/input/mouse.rs @@ -97,7 +97,7 @@ mod imp { } } - extern "C" { + unsafe extern "C" { fn CGEventSetIntegerValueField(event: *const std::ffi::c_void, field: u32, value: i64); } @@ -150,7 +150,7 @@ mod imp { tracing::debug!("mouse: scroll at ({x:.0},{y:.0}) dy={dy} dx={dx}"); use core_graphics::geometry::CGPoint; - extern "C" { + unsafe extern "C" { fn CGEventCreateScrollWheelEvent( source: *const std::ffi::c_void, units: u32, diff --git a/crates/macos/src/notifications/nc_session.rs b/crates/macos/src/notifications/nc_session.rs index d641683..1c99de3 100644 --- a/crates/macos/src/notifications/nc_session.rs +++ b/crates/macos/src/notifications/nc_session.rs @@ -61,11 +61,7 @@ fn frontmost_app() -> Option { .ok()?; if output.status.success() { let name = String::from_utf8_lossy(&output.stdout).trim().to_string(); - if name.is_empty() { - None - } else { - Some(name) - } + if name.is_empty() { None } else { Some(name) } } else { None } diff --git a/crates/macos/src/system/app_list.rs b/crates/macos/src/system/app_list.rs index c63159c..208adbc 100644 --- a/crates/macos/src/system/app_list.rs +++ b/crates/macos/src/system/app_list.rs @@ -92,20 +92,22 @@ fn running_apps_from_workspace() -> Vec { type Sel = *mut c_void; #[link(name = "AppKit", kind = "framework")] - extern "C" { + unsafe extern "C" { fn objc_getClass(name: *const core::ffi::c_char) -> Class; fn sel_registerName(name: *const core::ffi::c_char) -> Sel; fn objc_msgSend(receiver: Id, sel: Sel, ...) -> Id; } unsafe fn ns_string(id: Id) -> Option { - if id.is_null() { - return None; + unsafe { + if id.is_null() { + return None; + } + Some( + CFString::wrap_under_get_rule(id as core_foundation_sys::string::CFStringRef) + .to_string(), + ) } - Some( - CFString::wrap_under_get_rule(id as core_foundation_sys::string::CFStringRef) - .to_string(), - ) } unsafe { @@ -184,7 +186,7 @@ fn appkit_loaded() -> bool { type Id = *mut c_void; - extern "C" { + unsafe extern "C" { fn dlopen(filename: *const core::ffi::c_char, flag: i32) -> Id; } @@ -224,7 +226,7 @@ fn apps_from_cg_windows() -> Vec { string::CFString, }; - extern "C" { + unsafe extern "C" { fn CGWindowListCopyWindowInfo(option: u32, window_id: u32) -> CFTypeRef; } diff --git a/crates/macos/src/system/app_ops.rs b/crates/macos/src/system/app_ops.rs index fcc10ac..76507e5 100644 --- a/crates/macos/src/system/app_ops.rs +++ b/crates/macos/src/system/app_ops.rs @@ -14,7 +14,7 @@ pub fn pid_from_element(el: &crate::tree::AXElement) -> Option { #[cfg(target_os = "macos")] pub fn ensure_app_focused(pid: i32) -> Result<(), AdapterError> { tracing::debug!("system: ensure_app_focused pid={pid}"); - use accessibility_sys::{kAXErrorSuccess, AXUIElementSetAttributeValue}; + use accessibility_sys::{AXUIElementSetAttributeValue, kAXErrorSuccess}; use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString}; let app_el = crate::tree::element_for_pid(pid); @@ -43,8 +43,8 @@ pub fn focus_window_impl(win: &WindowInfo) -> Result<(), AdapterError> { win.title ); use accessibility_sys::{ - kAXErrorSuccess, AXUIElementCreateApplication, AXUIElementPerformAction, - AXUIElementSetAttributeValue, + AXUIElementCreateApplication, AXUIElementPerformAction, AXUIElementSetAttributeValue, + kAXErrorSuccess, }; use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString}; @@ -197,7 +197,7 @@ end tell"# #[cfg(target_os = "macos")] fn try_quit_via_menu_bar(app_el: &crate::tree::AXElement) -> bool { - use accessibility_sys::{kAXErrorSuccess, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXErrorSuccess}; use core_foundation::{base::TCFType, string::CFString}; let Some(menu_bar) = crate::tree::copy_element_attr(app_el, "AXMenuBar") else { diff --git a/crates/macos/src/system/key_dispatch.rs b/crates/macos/src/system/key_dispatch.rs index 9a83bcf..1b87681 100644 --- a/crates/macos/src/system/key_dispatch.rs +++ b/crates/macos/src/system/key_dispatch.rs @@ -48,7 +48,7 @@ fn try_simple_key_action( app_el: accessibility_sys::AXUIElementRef, combo: &KeyCombo, ) -> Option> { - use accessibility_sys::{kAXErrorSuccess, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXErrorSuccess}; use core_foundation::{base::TCFType, string::CFString}; if !combo.modifiers.is_empty() { @@ -76,7 +76,7 @@ fn try_simple_key_action( fn get_focused_element( app_el: accessibility_sys::AXUIElementRef, ) -> Option { - use accessibility_sys::{kAXErrorSuccess, AXUIElementCopyAttributeValue, AXUIElementRef}; + use accessibility_sys::{AXUIElementCopyAttributeValue, AXUIElementRef, kAXErrorSuccess}; use core_foundation::{base::TCFType, string::CFString}; let attr = CFString::new("AXFocusedUIElement"); @@ -94,7 +94,7 @@ fn try_menu_bar_shortcut( app_el: &crate::tree::AXElement, combo: &KeyCombo, ) -> Option> { - use accessibility_sys::{kAXErrorSuccess, AXUIElementPerformAction}; + use accessibility_sys::{AXUIElementPerformAction, kAXErrorSuccess}; use core_foundation::{base::TCFType, string::CFString}; let menu_bar = crate::tree::copy_element_attr(app_el, "AXMenuBar")?; @@ -137,7 +137,7 @@ fn try_menu_bar_shortcut( #[cfg(target_os = "macos")] fn read_menu_item_modifiers(el: &crate::tree::AXElement) -> u32 { - use accessibility_sys::{kAXErrorSuccess, AXUIElementCopyAttributeValue}; + use accessibility_sys::{AXUIElementCopyAttributeValue, kAXErrorSuccess}; use core_foundation::{base::TCFType, number::CFNumber, string::CFString}; let attr = CFString::new("AXMenuItemCmdModifiers"); diff --git a/crates/macos/src/system/permissions.rs b/crates/macos/src/system/permissions.rs index 39e9588..33521dd 100644 --- a/crates/macos/src/system/permissions.rs +++ b/crates/macos/src/system/permissions.rs @@ -6,17 +6,17 @@ const SCREEN_RECORDING_SUGGESTION: &str = "Open System Settings > Privacy & Secu #[cfg(target_os = "macos")] mod imp { use accessibility_sys::{ - kAXTrustedCheckOptionPrompt, AXIsProcessTrusted, AXIsProcessTrustedWithOptions, + AXIsProcessTrusted, AXIsProcessTrustedWithOptions, kAXTrustedCheckOptionPrompt, }; use core_foundation::{ base::TCFType, boolean::CFBoolean, dictionary::CFDictionary, string::CFString, }; - pub fn is_trusted() -> bool { + pub(super) fn is_trusted() -> bool { unsafe { AXIsProcessTrusted() } } - pub fn request_trust() -> bool { + pub(super) fn request_trust() -> bool { unsafe { let key = CFString::wrap_under_get_rule(kAXTrustedCheckOptionPrompt); let val = CFBoolean::true_value(); @@ -26,16 +26,16 @@ mod imp { } #[link(name = "CoreGraphics", kind = "framework")] - extern "C" { + unsafe extern "C" { fn CGPreflightScreenCaptureAccess() -> bool; fn CGRequestScreenCaptureAccess() -> bool; } - pub fn screen_recording_granted() -> bool { + pub(super) fn screen_recording_granted() -> bool { unsafe { CGPreflightScreenCaptureAccess() } } - pub fn request_screen_recording() -> bool { + pub(super) fn request_screen_recording() -> bool { unsafe { CGRequestScreenCaptureAccess() } } } diff --git a/crates/macos/src/system/screenshot.rs b/crates/macos/src/system/screenshot.rs index da4f53b..39b67e0 100644 --- a/crates/macos/src/system/screenshot.rs +++ b/crates/macos/src/system/screenshot.rs @@ -146,7 +146,7 @@ mod imp { string::CFString, }; - extern "C" { + unsafe extern "C" { fn CGWindowListCopyWindowInfo(option: u32, window_id: u32) -> CFTypeRef; } diff --git a/crates/macos/src/system/window_ops.rs b/crates/macos/src/system/window_ops.rs index 85302cf..4bc07ec 100644 --- a/crates/macos/src/system/window_ops.rs +++ b/crates/macos/src/system/window_ops.rs @@ -4,15 +4,15 @@ use agent_desktop_core::{action::WindowOp, error::AdapterError, node::WindowInfo mod imp { use super::*; use accessibility_sys::{ - kAXErrorSuccess, kAXPositionAttribute, kAXSizeAttribute, kAXValueTypeCGPoint, - kAXValueTypeCGSize, AXUIElementSetAttributeValue, + AXUIElementSetAttributeValue, kAXErrorSuccess, kAXPositionAttribute, kAXSizeAttribute, + kAXValueTypeCGPoint, kAXValueTypeCGSize, }; use agent_desktop_core::error::ErrorCode; use core_foundation::{base::TCFType, boolean::CFBoolean, string::CFString}; use core_graphics::geometry::{CGPoint, CGSize}; use std::ffi::c_void; - extern "C" { + unsafe extern "C" { fn AXValueCreate(value_type: u32, value_ptr: *const c_void) -> *mut c_void; } diff --git a/crates/macos/src/tree/action_list.rs b/crates/macos/src/tree/action_list.rs index b783578..e67609c 100644 --- a/crates/macos/src/tree/action_list.rs +++ b/crates/macos/src/tree/action_list.rs @@ -1,5 +1,5 @@ -use super::capabilities::{copy_action_names, is_attr_settable}; use super::AXElement; +use super::capabilities::{copy_action_names, is_attr_settable}; #[cfg(target_os = "macos")] use accessibility_sys::{kAXFocusedAttribute, kAXValueAttribute}; diff --git a/crates/macos/src/tree/builder.rs b/crates/macos/src/tree/builder.rs index 86da272..9a56217 100644 --- a/crates/macos/src/tree/builder.rs +++ b/crates/macos/src/tree/builder.rs @@ -1,15 +1,15 @@ use agent_desktop_core::node::AccessibilityNode; use rustc_hash::FxHashSet; +use super::AXElement; use super::action_list::platform_available_actions; use super::build_context::TreeBuildContext; use super::capabilities::same_element; use super::element::{ - child_attributes, copy_ax_array, copy_bool_attr, copy_string_attr, count_children, - element_for_pid, fetch_node_attrs, ABSOLUTE_MAX_DEPTH, + ABSOLUTE_MAX_DEPTH, child_attributes, copy_ax_array, copy_bool_attr, copy_string_attr, + count_children, element_for_pid, fetch_node_attrs, }; use super::element_bounds::read_bounds; -use super::AXElement; #[cfg(target_os = "macos")] use accessibility_sys::{ diff --git a/crates/macos/src/tree/capabilities.rs b/crates/macos/src/tree/capabilities.rs index 3e20448..49eae2a 100644 --- a/crates/macos/src/tree/capabilities.rs +++ b/crates/macos/src/tree/capabilities.rs @@ -2,7 +2,7 @@ mod imp { use crate::tree::AXElement; use accessibility_sys::{ - kAXErrorSuccess, AXUIElementCopyActionNames, AXUIElementIsAttributeSettable, + AXUIElementCopyActionNames, AXUIElementIsAttributeSettable, kAXErrorSuccess, }; use core_foundation::{ array::CFArray, diff --git a/crates/macos/src/tree/element.rs b/crates/macos/src/tree/element.rs index 3cf07a5..c9ddac4 100644 --- a/crates/macos/src/tree/element.rs +++ b/crates/macos/src/tree/element.rs @@ -15,10 +15,10 @@ mod imp { use super::*; use crate::tree::ax_element::AXElement; use accessibility_sys::{ + AXUIElementCopyAttributeValue, AXUIElementCopyMultipleAttributeValues, + AXUIElementCreateApplication, AXUIElementRef, AXUIElementSetMessagingTimeout, kAXDescriptionAttribute, kAXEnabledAttribute, kAXErrorSuccess, kAXRoleAttribute, - kAXTitleAttribute, kAXValueAttribute, AXUIElementCopyAttributeValue, - AXUIElementCopyMultipleAttributeValues, AXUIElementCreateApplication, AXUIElementRef, - AXUIElementSetMessagingTimeout, + kAXTitleAttribute, kAXValueAttribute, }; use core_foundation::{ array::CFArray, diff --git a/crates/macos/src/tree/element_bounds.rs b/crates/macos/src/tree/element_bounds.rs index f8d1f09..deddc37 100644 --- a/crates/macos/src/tree/element_bounds.rs +++ b/crates/macos/src/tree/element_bounds.rs @@ -5,8 +5,8 @@ use super::AXElement; #[cfg(target_os = "macos")] pub fn read_bounds(el: &AXElement) -> Option { use accessibility_sys::{ - kAXErrorSuccess, kAXPositionAttribute, kAXSizeAttribute, kAXValueTypeCGPoint, - kAXValueTypeCGSize, AXUIElementCopyAttributeValue, AXValueGetValue, + AXUIElementCopyAttributeValue, AXValueGetValue, kAXErrorSuccess, kAXPositionAttribute, + kAXSizeAttribute, kAXValueTypeCGPoint, kAXValueTypeCGSize, }; use core_foundation::{ base::{CFRelease, CFTypeRef, TCFType}, diff --git a/crates/macos/src/tree/mod.rs b/crates/macos/src/tree/mod.rs index 0c43faf..8dc9643 100644 --- a/crates/macos/src/tree/mod.rs +++ b/crates/macos/src/tree/mod.rs @@ -14,8 +14,8 @@ pub use build_context::TreeBuildContext; pub use builder::{build_subtree, window_element_for}; pub use capabilities::{copy_action_names, is_attr_settable, same_element}; pub use element::{ - copy_ax_array, copy_bool_attr, copy_element_attr, copy_i64_attr, copy_string_attr, - copy_value_typed, count_children, element_for_pid, resolve_element_name, ABSOLUTE_MAX_DEPTH, + ABSOLUTE_MAX_DEPTH, copy_ax_array, copy_bool_attr, copy_element_attr, copy_i64_attr, + copy_string_attr, copy_value_typed, count_children, element_for_pid, resolve_element_name, }; pub use element_bounds::read_bounds; pub use resolve::{find_element_recursive, resolve_element_impl}; diff --git a/crates/macos/src/tree/resolve.rs b/crates/macos/src/tree/resolve.rs index 885cb4a..5ccaaa4 100644 --- a/crates/macos/src/tree/resolve.rs +++ b/crates/macos/src/tree/resolve.rs @@ -5,12 +5,12 @@ use agent_desktop_core::{ }; use rustc_hash::FxHashSet; +use super::AXElement; use super::builder::window_element_for; use super::element::{ child_attributes, copy_ax_array, copy_element_attr, copy_string_attr, element_for_pid, resolve_element_name, }; -use super::AXElement; #[cfg(target_os = "macos")] pub fn resolve_element_impl(entry: &RefEntry) -> Result { @@ -302,7 +302,7 @@ mod tests { source_app: None, source_window_title: None, root_ref: root_ref.map(String::from), - path: vec![0, 1], + path: smallvec::smallvec![0, 1], } } diff --git a/crates/macos/src/tree/surfaces.rs b/crates/macos/src/tree/surfaces.rs index ae23949..718eb85 100644 --- a/crates/macos/src/tree/surfaces.rs +++ b/crates/macos/src/tree/surfaces.rs @@ -1,7 +1,7 @@ +use super::AXElement; use super::element::{ copy_ax_array, copy_bool_attr, copy_element_attr, copy_string_attr, element_for_pid, }; -use super::AXElement; use agent_desktop_core::node::SurfaceInfo; #[cfg(target_os = "macos")] diff --git a/crates/windows/Cargo.toml b/crates/windows/Cargo.toml index 1dc5d08..c3e1033 100644 --- a/crates/windows/Cargo.toml +++ b/crates/windows/Cargo.toml @@ -7,3 +7,6 @@ license.workspace = true [dependencies] agent-desktop-core.workspace = true thiserror.workspace = true + +[lints] +workspace = true diff --git a/src/Cargo.toml b/src/Cargo.toml index ec5fbfa..e561265 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -29,3 +29,6 @@ path = "main.rs" [[test]] name = "snapshot_test" path = "tests/snapshot_test.rs" + +[lints] +workspace = true diff --git a/src/batch.rs b/src/batch.rs index daa4bc4..2f24985 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -1,13 +1,13 @@ use agent_desktop_core::{ + PermissionReport, adapter::PlatformAdapter, commands::batch::BatchCommand, error::AppError, - output::{ErrorPayload, ENVELOPE_VERSION}, - PermissionReport, + output::{ENVELOPE_VERSION, ErrorPayload}, }; -use serde::de::DeserializeOwned; use serde::Deserialize; -use serde_json::{json, Map, Value}; +use serde::de::DeserializeOwned; +use serde_json::{Map, Value, json}; use crate::{ cli::Commands, @@ -184,7 +184,7 @@ fn parse_skills(args: Value) -> Result { Some(other) => { return Err(AppError::invalid_input(format!( "Unknown skills action '{other}'" - ))) + ))); } }; Ok(SkillsArgs { action }) @@ -194,7 +194,7 @@ fn parse_skills(args: Value) -> Result { mod tests { use super::*; use crate::cli_args::Surface; - use agent_desktop_core::{adapter::PlatformAdapter, PermissionReport}; + use agent_desktop_core::{PermissionReport, adapter::PlatformAdapter}; use clap::CommandFactory; struct NoopAdapter; @@ -266,10 +266,12 @@ mod tests { assert_eq!(results[0]["version"], ENVELOPE_VERSION); assert_eq!(results[0]["command"], "missing"); assert_eq!(results[0]["error"]["code"], "INVALID_ARGS"); - assert!(results[0]["error"]["message"] - .as_str() - .unwrap() - .contains("Unknown batch command")); + assert!( + results[0]["error"]["message"] + .as_str() + .unwrap() + .contains("Unknown batch command") + ); } #[test] diff --git a/src/cli.rs b/src/cli.rs index c71293a..223c5e0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,7 +29,7 @@ const AFTER_HELP: &str = include_str!("help_after.txt"); before_help = BEFORE_HELP, after_help = AFTER_HELP, )] -pub struct Cli { +pub(crate) struct Cli { #[arg( long, short = 'v', @@ -43,7 +43,7 @@ pub struct Cli { } #[derive(Subcommand, Debug)] -pub enum Commands { +pub(crate) enum Commands { #[command(about = "Capture accessibility tree as structured JSON with @ref IDs")] Snapshot(SnapshotArgs), #[command(about = "Search elements by role, name, value, or text content")] @@ -159,7 +159,7 @@ pub enum Commands { } impl Commands { - pub fn name(&self) -> &'static str { + pub(crate) fn name(&self) -> &'static str { match self { Self::Snapshot(_) => "snapshot", Self::Find(_) => "find", diff --git a/src/cli_args.rs b/src/cli_args.rs index 4283b5e..01cd935 100644 --- a/src/cli_args.rs +++ b/src/cli_args.rs @@ -15,7 +15,7 @@ fn default_is_property() -> String { #[derive(ValueEnum, Clone, Debug, Default, Deserialize)] #[serde(rename_all = "kebab-case")] -pub enum Surface { +pub(crate) enum Surface { #[default] Window, Focused, @@ -27,7 +27,7 @@ pub enum Surface { } impl Surface { - pub fn to_core(&self) -> agent_desktop_core::adapter::SnapshotSurface { + pub(crate) fn to_core(&self) -> agent_desktop_core::adapter::SnapshotSurface { use agent_desktop_core::adapter::SnapshotSurface; match self { Self::Window => SnapshotSurface::Window, @@ -43,7 +43,7 @@ impl Surface { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct SnapshotArgs { +pub(crate) struct SnapshotArgs { #[arg(long, help = "Filter to application by name")] pub app: Option, #[arg( @@ -89,7 +89,7 @@ pub struct SnapshotArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct FindArgs { +pub(crate) struct FindArgs { #[arg(long, help = "Filter to application by name")] pub app: Option, #[arg( @@ -140,7 +140,7 @@ pub struct FindArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ScreenshotArgs { +pub(crate) struct ScreenshotArgs { #[arg(long, help = "Filter to application by name")] pub app: Option, #[arg( @@ -155,7 +155,7 @@ pub struct ScreenshotArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct GetArgs { +pub(crate) struct GetArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -171,7 +171,7 @@ pub struct GetArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct IsArgs { +pub(crate) struct IsArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -187,7 +187,7 @@ pub struct IsArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct RefArgs { +pub(crate) struct RefArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg( @@ -200,7 +200,7 @@ pub struct RefArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ListSurfacesArgs { +pub(crate) struct ListSurfacesArgs { #[arg(long, help = "Filter to application by name")] pub app: Option, } diff --git a/src/cli_args_actions.rs b/src/cli_args_actions.rs index 4dbb08b..7cf9990 100644 --- a/src/cli_args_actions.rs +++ b/src/cli_args_actions.rs @@ -19,7 +19,7 @@ fn default_scroll_direction() -> String { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct TypeArgs { +pub(crate) struct TypeArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -30,7 +30,7 @@ pub struct TypeArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct SetValueArgs { +pub(crate) struct SetValueArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -45,7 +45,7 @@ pub struct SetValueArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct SelectArgs { +pub(crate) struct SelectArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -56,7 +56,7 @@ pub struct SelectArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ScrollArgs { +pub(crate) struct ScrollArgs { #[arg(value_name = "REF", help = "Element ref from snapshot (@e1, @e2 ...)")] pub ref_id: String, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -75,7 +75,7 @@ pub struct ScrollArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct PressArgs { +pub(crate) struct PressArgs { #[arg( value_name = "COMBO", help = "Key combo: return, escape, cmd+c, shift+tab ..." @@ -87,7 +87,7 @@ pub struct PressArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct KeyComboArgs { +pub(crate) struct KeyComboArgs { #[arg( value_name = "COMBO", help = "Key or modifier to hold/release: shift, cmd, ctrl ..." @@ -97,7 +97,7 @@ pub struct KeyComboArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct HoverArgs { +pub(crate) struct HoverArgs { #[arg(value_name = "REF", help = "Element ref to hover over")] pub ref_id: Option, #[arg(long, help = "Snapshot ID returned by snapshot; omit to use latest")] @@ -110,7 +110,7 @@ pub struct HoverArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct DragCliArgs { +pub(crate) struct DragCliArgs { #[arg(long, help = "Source element ref")] pub from: Option, #[arg(long, name = "from-xy", help = "Source coordinates as x,y")] @@ -127,14 +127,14 @@ pub struct DragCliArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct MouseMoveArgs { +pub(crate) struct MouseMoveArgs { #[arg(long, help = "Absolute coordinates as x,y")] pub xy: String, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct MouseClickArgs { +pub(crate) struct MouseClickArgs { #[arg(long, help = "Absolute coordinates as x,y")] pub xy: String, #[arg( @@ -151,7 +151,7 @@ pub struct MouseClickArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct MousePointArgs { +pub(crate) struct MousePointArgs { #[arg(long, help = "Absolute coordinates as x,y")] pub xy: String, #[arg( diff --git a/src/cli_args_notifications.rs b/src/cli_args_notifications.rs index 384bbad..06d1503 100644 --- a/src/cli_args_notifications.rs +++ b/src/cli_args_notifications.rs @@ -3,7 +3,7 @@ use serde::Deserialize; #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ListNotificationsCliArgs { +pub(crate) struct ListNotificationsCliArgs { #[arg(long, help = "Filter to notifications from this app")] pub app: Option, #[arg(long, help = "Filter to notifications containing this text")] @@ -14,7 +14,7 @@ pub struct ListNotificationsCliArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct DismissNotificationCliArgs { +pub(crate) struct DismissNotificationCliArgs { #[arg(value_name = "INDEX", help = "1-based notification index from list-notifications", value_parser = clap::value_parser!(u64).range(1..))] pub index: u64, @@ -24,14 +24,14 @@ pub struct DismissNotificationCliArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct DismissAllNotificationsCliArgs { +pub(crate) struct DismissAllNotificationsCliArgs { #[arg(long, help = "Only dismiss notifications from this app")] pub app: Option, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct NotificationActionCliArgs { +pub(crate) struct NotificationActionCliArgs { #[arg(value_name = "INDEX", help = "1-based notification index from list-notifications", value_parser = clap::value_parser!(u64).range(1..))] pub index: u64, diff --git a/src/cli_args_skills.rs b/src/cli_args_skills.rs index a209027..9355382 100644 --- a/src/cli_args_skills.rs +++ b/src/cli_args_skills.rs @@ -12,13 +12,13 @@ Examples: agent-desktop skills get desktop --full # Plus every reference agent-desktop skills get desktop workflows # Single reference agent-desktop skills path # Where skills live")] -pub struct SkillsArgs { +pub(crate) struct SkillsArgs { #[command(subcommand)] pub action: Option, } #[derive(Subcommand, Debug)] -pub enum SkillsAction { +pub(crate) enum SkillsAction { #[command(about = "List bundled skills with summaries (default)")] List, #[command(about = "Print a skill's markdown to stdout")] @@ -28,7 +28,7 @@ pub enum SkillsAction { } #[derive(Args, Debug)] -pub struct SkillsGetArgs { +pub(crate) struct SkillsGetArgs { #[arg(help = "Skill name or alias (desktop, ffi, ...)")] pub name: String, #[arg( diff --git a/src/cli_args_system.rs b/src/cli_args_system.rs index 9664159..b1e38be 100644 --- a/src/cli_args_system.rs +++ b/src/cli_args_system.rs @@ -11,7 +11,7 @@ fn default_wait_timeout() -> u64 { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct LaunchArgs { +pub(crate) struct LaunchArgs { #[arg(value_name = "APP", help = "Application name or bundle ID")] pub app: String, #[arg( @@ -25,7 +25,7 @@ pub struct LaunchArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct CloseAppArgs { +pub(crate) struct CloseAppArgs { #[arg(value_name = "APP", help = "Application name")] pub app: String, #[arg(long, help = "Force-kill the process instead of quitting gracefully")] @@ -35,21 +35,21 @@ pub struct CloseAppArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ListWindowsArgs { +pub(crate) struct ListWindowsArgs { #[arg(long, help = "Filter running apps by case-insensitive name substring")] pub app: Option, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ListAppsArgs { +pub(crate) struct ListAppsArgs { #[arg(long, help = "Filter to application by name")] pub app: Option, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct FocusWindowArgs { +pub(crate) struct FocusWindowArgs { #[arg(long, name = "window-id", help = "Window ID from list-windows")] pub window_id: Option, #[arg(long, help = "Application name")] @@ -60,7 +60,7 @@ pub struct FocusWindowArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ResizeWindowCliArgs { +pub(crate) struct ResizeWindowCliArgs { #[arg(long, help = "Application name")] pub app: Option, #[arg(long, help = "New window width in pixels")] @@ -71,7 +71,7 @@ pub struct ResizeWindowCliArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct MoveWindowCliArgs { +pub(crate) struct MoveWindowCliArgs { #[arg(long, help = "Application name")] pub app: Option, #[arg(long, help = "New window X position")] @@ -82,21 +82,21 @@ pub struct MoveWindowCliArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct AppRefArgs { +pub(crate) struct AppRefArgs { #[arg(long, help = "Application name")] pub app: Option, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct ClipboardSetArgs { +pub(crate) struct ClipboardSetArgs { #[arg(value_name = "TEXT", help = "Text to write to the clipboard")] pub text: String, } #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct WaitArgs { +pub(crate) struct WaitArgs { #[arg(value_name = "MS", help = "Milliseconds to pause")] pub ms: Option, #[arg(long, help = "Block until this element ref appears in the tree")] @@ -135,7 +135,7 @@ pub struct WaitArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct PermissionsArgs { +pub(crate) struct PermissionsArgs { #[arg(long, help = "Trigger the system accessibility permission dialog")] #[serde(default)] pub request: bool, @@ -143,14 +143,14 @@ pub struct PermissionsArgs { #[derive(Parser, Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct VersionArgs { +pub(crate) struct VersionArgs { #[arg(long, help = "Output version as JSON object")] #[serde(default)] pub json: bool, } #[derive(Parser, Debug)] -pub struct BatchArgs { +pub(crate) struct BatchArgs { #[arg(value_name = "JSON", help = "JSON array of {command, args} objects")] pub commands_json: String, #[arg(long, help = "Halt the batch on the first failed command")] diff --git a/src/command_policy.rs b/src/command_policy.rs index e29ea06..0939831 100644 --- a/src/command_policy.rs +++ b/src/command_policy.rs @@ -1,6 +1,6 @@ use agent_desktop_core::{ - error::{AdapterError, AppError, ErrorCode}, PermissionReport, + error::{AdapterError, AppError, ErrorCode}, }; use crate::cli::Commands; diff --git a/src/dispatch.rs b/src/dispatch.rs index 2f2447f..82522c8 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -1,4 +1,5 @@ use agent_desktop_core::{ + PermissionReport, adapter::PlatformAdapter, commands::{ check, clear, click, clipboard_clear, clipboard_get, clipboard_set, close_app, collapse, @@ -9,7 +10,6 @@ use agent_desktop_core::{ skills, snapshot, status, toggle, triple_click, type_text, uncheck, version, wait, }, error::AppError, - PermissionReport, }; use serde_json::Value; @@ -20,7 +20,7 @@ use crate::dispatch_parse::{ parse_xy_opt, }; -pub fn dispatch( +pub(crate) fn dispatch( cmd: Commands, adapter: &dyn PlatformAdapter, permission_report: &PermissionReport, diff --git a/src/dispatch_notifications.rs b/src/dispatch_notifications.rs index c02dea8..781c97e 100644 --- a/src/dispatch_notifications.rs +++ b/src/dispatch_notifications.rs @@ -9,7 +9,7 @@ use serde_json::Value; use crate::cli::Commands; -pub fn dispatch_notification( +pub(crate) fn dispatch_notification( cmd: Commands, adapter: &dyn PlatformAdapter, ) -> Result { diff --git a/src/main.rs b/src/main.rs index 29766bc..693f4e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ mod dispatch_parse; use agent_desktop_core::{ adapter::PlatformAdapter, error::AppError, - output::{ErrorPayload, Response, ENVELOPE_VERSION}, + output::{ENVELOPE_VERSION, ErrorPayload, Response}, }; use clap::{CommandFactory, Parser}; use cli::{Cli, Commands}; @@ -157,7 +157,7 @@ fn build_adapter() -> impl agent_desktop_core::adapter::PlatformAdapter { } fn init_tracing(verbose: bool) { - use tracing_subscriber::{fmt, EnvFilter}; + use tracing_subscriber::{EnvFilter, fmt}; let filter = if verbose { "debug" } else { "warn" }; fmt() .with_env_filter(