1pub type Result<T> = anyhow::Result<T>;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum WindowAction {
6 Minimize,
7 Maximize,
8 Restore,
9 Close,
10}
11
12#[cfg(target_os = "windows")]
13mod browser;
14mod clipboard;
15mod desktop;
16mod dialog;
17#[cfg(target_os = "windows")]
18mod element;
19#[cfg(not(target_os = "windows"))]
20#[path = "element_stub.rs"]
21mod element;
22mod element_info;
23mod error;
24mod input;
25mod locator;
26mod mouse;
27mod mouse_hook;
28mod overlay;
29mod process;
30mod selector;
31mod task_view;
32mod taskbar;
33mod uia_probe;
34mod util;
35mod window;
36
37#[cfg(target_os = "windows")]
38mod element_tree;
39#[cfg(target_os = "windows")]
40mod windows_info;
41
42pub use clipboard::*;
43pub use desktop::*;
44pub use dialog::*;
45pub use element::*;
46pub use element_info::*;
47pub use error::*;
48pub use input::*;
49pub use locator::*;
50pub use mouse::*;
51pub use mouse_hook::*;
52pub use overlay::*;
53pub use process::*;
54pub use selector::*;
55pub use task_view::*;
56pub use taskbar::*;
57pub use uia_probe::*;
58pub use window::*;
59
60#[cfg(target_os = "windows")]
61pub use element_tree::*;
62#[cfg(target_os = "windows")]
63pub use windows_info::*;
64
65#[cfg(target_os = "windows")]
69pub fn run_workflow_str(
70 yaml: &str,
71 params: &std::collections::HashMap<String, String>,
72) -> Result<()> {
73 let workflow =
74 ui_automata::yaml::WorkflowFile::load_from_str(yaml, params).map_err(anyhow::Error::msg)?;
75 let desktop = Desktop::new();
76 let mut executor = ui_automata::Executor::new(desktop);
77 log::info!("running workflow '{}' ...", workflow.name);
78 workflow
79 .run(&mut executor, None, None)
80 .map(|_| ())
81 .map_err(anyhow::Error::msg)
82}
83
84const OWN_CRATES: &[&str] = &["automata_browser", "automata_windows", "ui_automata"];
86
87fn make_file_dispatch(log_path: &std::path::Path) -> Option<fern::Dispatch> {
90 match fern::log_file(log_path) {
91 Ok(file) => {
92 let mut dispatch = fern::Dispatch::new()
93 .level(log::LevelFilter::Info)
94 .format(|out, message, record| {
95 out.finish(format_args!(
96 "[{}] [{}] {}",
97 chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ"),
98 record.level(),
99 message
100 ))
101 })
102 .chain(file);
103 for krate in OWN_CRATES {
104 dispatch = dispatch.level_for(*krate, log::LevelFilter::Debug);
105 }
106 Some(dispatch)
107 }
108 Err(e) => {
109 eprintln!("could not open log file {}: {e}", log_path.display());
110 None
111 }
112 }
113}
114
115pub fn init_logging_file_only(log_path: &std::path::Path) {
119 let mut dispatch = fern::Dispatch::new();
120 if let Some(file_dispatch) = make_file_dispatch(log_path) {
121 dispatch = dispatch.chain(file_dispatch);
122 }
123 if let Err(e) = dispatch.apply() {
124 eprintln!("failed to initialise logging: {e}");
125 }
126}
127
128#[cfg(target_os = "windows")]
133pub fn send_ctrl_c(pid: u32) -> bool {
134 use windows::Win32::System::Console::{CTRL_C_EVENT, GenerateConsoleCtrlEvent};
135 unsafe { GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid).is_ok() }
136}
137
138#[cfg(not(target_os = "windows"))]
139pub fn send_ctrl_c(_pid: u32) -> bool {
140 false
141}
142
143pub fn init_logging(log_path: Option<&std::path::Path>) {
148 let stdout_level = std::env::var("RUST_LOG")
149 .ok()
150 .and_then(|v| v.parse().ok())
151 .unwrap_or(log::LevelFilter::Info);
152
153 let colors = fern::colors::ColoredLevelConfig::new()
154 .error(fern::colors::Color::Red)
155 .warn(fern::colors::Color::Yellow)
156 .info(fern::colors::Color::Green)
157 .debug(fern::colors::Color::Cyan)
158 .trace(fern::colors::Color::White);
159
160 let stdout_dispatch = fern::Dispatch::new()
161 .level(stdout_level)
162 .format(move |out, message, record| {
163 out.finish(format_args!(
164 "[{} {:<5} {}] {}",
165 chrono::Utc::now().format("%Y-%m-%dT%H:%M:%SZ"),
166 colors.color(record.level()),
167 record.target(),
168 message
169 ))
170 })
171 .chain(std::io::stdout());
172
173 let mut dispatch = fern::Dispatch::new().chain(stdout_dispatch);
174
175 if let Some(path) = log_path {
176 if let Some(file_dispatch) = make_file_dispatch(path) {
177 dispatch = dispatch.chain(file_dispatch);
178 }
179 }
180
181 if let Err(e) = dispatch.apply() {
182 eprintln!("failed to initialise logging: {e}");
183 }
184}
185
186pub fn init_com() {
188 #[cfg(target_os = "windows")]
189 unsafe {
190 use windows::Win32::System::Com::{COINIT_MULTITHREADED, CoInitializeEx};
191 let _ = CoInitializeEx(None, COINIT_MULTITHREADED);
192 }
193}