adb_kit/
utils.rs

1use crate::error::{ADBError, ADBResult};
2use std::collections::HashMap;
3use std::path::{Path, PathBuf};
4use std::time::{Duration, Instant};
5use log::warn;
6use regex::Regex;
7use std::fs;
8use rand::Rng;
9
10/// 使用指数退避策略重试操作
11pub fn retry_with_backoff<F, T>(max_retries: u32, initial_delay_ms: u64, f: F) -> ADBResult<T>
12where
13    F: Fn() -> ADBResult<T>,
14{
15    let mut retries = 0;
16    let mut delay = initial_delay_ms;
17
18    loop {
19        match f() {
20            Ok(result) => return Ok(result),
21            Err(e) => {
22                retries += 1;
23                if retries > max_retries {
24                    return Err(e);
25                }
26
27                warn!(
28                    "操作失败 (重试 {}/{}), 延迟 {}ms: {}",
29                    retries, max_retries, delay, e
30                );
31
32                std::thread::sleep(Duration::from_millis(delay));
33                // 指数退避策略:下次延迟时间翻倍但不超过 10 秒
34                delay = (delay * 2).min(10000);
35            }
36        }
37    }
38}
39
40/// 带超时执行操作
41pub fn with_timeout<F, T>(timeout_ms: u64, f: F) -> ADBResult<T>
42where
43    F: FnOnce() -> ADBResult<T> + Send + 'static,
44    T: Send + 'static,
45{
46    let timeout = Duration::from_millis(timeout_ms);
47
48    // 创建通道用于跨线程通信
49    let (sender, receiver) = std::sync::mpsc::channel();
50
51    // 在新线程中执行操作
52    std::thread::spawn(move || {
53        let result = f();
54        let _ = sender.send(result);
55    });
56
57    // 等待结果或超时
58    match receiver.recv_timeout(timeout) {
59        Ok(result) => result,
60        Err(_) => Err(ADBError::TimeoutError {
61            message: "操作超时".to_string(),
62            duration: timeout,
63        }),
64    }
65}
66
67/// 根据条件轮询等待
68pub fn wait_with_polling<F, C>(
69    timeout_ms: u64,
70    poll_interval_ms: u64,
71    condition_fn: F,
72    callback: Option<C>,
73) -> ADBResult<bool>
74where
75    F: Fn() -> ADBResult<bool>,
76    C: Fn(u64),
77{
78    let start = Instant::now();
79    let timeout = Duration::from_millis(timeout_ms);
80    let interval = Duration::from_millis(poll_interval_ms);
81
82    loop {
83        // 检查是否超时
84        let elapsed = start.elapsed();
85        if elapsed > timeout {
86            return Ok(false);
87        }
88
89        // 如果提供了回调函数,则执行
90        if let Some(cb) = &callback {
91            cb(elapsed.as_millis() as u64);
92        }
93
94        // 检查条件
95        match condition_fn() {
96            Ok(true) => return Ok(true),
97            Ok(false) => {
98                // 条件未满足,继续等待
99                std::thread::sleep(interval);
100            }
101            Err(e) => {
102                // 检查条件时出错
103                warn!("检查条件时出错: {}", e);
104                std::thread::sleep(interval);
105            }
106        }
107    }
108}
109
110/// 解析 getprop 输出为属性 HashMap
111pub fn parse_properties(output: &str) -> HashMap<String, String> {
112    let mut properties = HashMap::new();
113    let re = Regex::new(r"^\[([^\]]+)\]:\s*\[([^\]]*)\]$").unwrap_or_else(|_| {
114        // 如果正则表达式无效,使用一个永远不会匹配的模式
115        Regex::new(r"^$").unwrap()
116    });
117
118    for line in output.lines() {
119        if let Some(caps) = re.captures(line.trim()) {
120            if let (Some(key), Some(value)) = (caps.get(1), caps.get(2)) {
121                properties.insert(key.as_str().to_string(), value.as_str().to_string());
122            }
123        }
124    }
125
126    properties
127}
128
129/// 清理不完整的分包
130pub fn cleanup_partial_files(path_pattern: &str) -> ADBResult<()> {
131    let pattern = format!("{}*", path_pattern);
132    let glob_paths = match glob::glob(&pattern) {
133        Ok(paths) => paths,
134        Err(e) => {
135            return Err(ADBError::FileError(format!(
136                "无法匹配部分文件: {}",
137                e
138            )))
139        }
140    };
141
142    for entry in glob_paths {
143        match entry {
144            Ok(path) => {
145                if path.is_file() {
146                    if let Err(e) = fs::remove_file(&path) {
147                        warn!("无法删除临时文件 {:?}: {}", path, e);
148                    }
149                }
150            }
151            Err(e) => warn!("无法访问文件路径: {}", e),
152        }
153    }
154
155    Ok(())
156}
157
158/// 创建临时目录
159pub fn create_temp_dir_path(prefix: &str) -> ADBResult<PathBuf> {
160    let temp_dir = std::env::temp_dir();
161
162    // 生成随机字符串
163    let random_string: String = rand::rng().sample_iter(&rand::distr::Alphanumeric)
164        .take(10)
165        .map(char::from)
166        .collect();
167
168    let dir_name = format!("{}_{}", prefix, random_string);
169    let full_path = temp_dir.join(dir_name);
170
171    // 确保目录存在
172    fs::create_dir_all(&full_path).map_err(|e| {
173        ADBError::FileError(format!("无法创建临时目录: {}", e))
174    })?;
175
176    Ok(full_path)
177}
178
179/// 检查路径是否是有效的 APK 文件
180pub fn is_valid_apk(path: &Path) -> bool {
181    if !path.exists() || !path.is_file() {
182        return false;
183    }
184
185    // 检查扩展名
186    if let Some(ext) = path.extension() {
187        return ext == "apk";
188    }
189
190    false
191}
192
193/// 获取字符串中的数字部分
194pub fn extract_number(s: &str) -> Option<i32> {
195    let re = Regex::new(r"\d+").ok()?;
196    re.find(s)
197        .map(|m| m.as_str())
198        .and_then(|num_str| num_str.parse::<i32>().ok())
199}
200
201/// 格式化大小 (字节转换为 KB/MB/GB)
202pub fn format_size(size_bytes: u64) -> String {
203    const KB: u64 = 1024;
204    const MB: u64 = KB * 1024;
205    const GB: u64 = MB * 1024;
206
207    if size_bytes >= GB {
208        format!("{:.2} GB", size_bytes as f64 / GB as f64)
209    } else if size_bytes >= MB {
210        format!("{:.2} MB", size_bytes as f64 / MB as f64)
211    } else if size_bytes >= KB {
212        format!("{:.2} KB", size_bytes as f64 / KB as f64)
213    } else {
214        format!("{} B", size_bytes)
215    }
216}
217
218/// 解析命令行参数
219pub fn parse_args(args: &[String]) -> HashMap<String, String> {
220    let mut result = HashMap::new();
221    let mut i = 0;
222
223    while i < args.len() {
224        if args[i].starts_with("--") {
225            let key = args[i][2..].to_string();
226
227            if i + 1 < args.len() && !args[i + 1].starts_with("--") {
228                result.insert(key, args[i + 1].clone());
229                i += 2;
230            } else {
231                result.insert(key, "true".to_string());
232                i += 1;
233            }
234        } else {
235            i += 1;
236        }
237    }
238
239    result
240}
241
242/// 获取迭代器中的第 N 行
243pub fn get_line_at<'a>(lines: &mut std::str::Lines<'a>, index: usize) -> Option<&'a str> {
244    lines.nth(index)
245}
246
247/// 安全地解析数字
248pub fn parse_number<T: std::str::FromStr>(s: &str) -> Option<T> {
249    s.trim().parse::<T>().ok()
250}
251
252/// 检查字符串是否包含任何给定的关键字
253pub fn contains_any(s: &str, keywords: &[&str]) -> bool {
254    for keyword in keywords {
255        if s.contains(keyword) {
256            return true;
257        }
258    }
259    false
260}
261
262/// 将秒数转换为人类可读的时间格式 (HH:MM:SS)
263pub fn format_duration(seconds: u64) -> String {
264    let hours = seconds / 3600;
265    let minutes = (seconds % 3600) / 60;
266    let secs = seconds % 60;
267
268    format!("{:02}:{:02}:{:02}", hours, minutes, secs)
269}