Skip to main content

uiautomator_cli/
error.rs

1//! 错误处理模块
2//!
3//! 提供友好的错误消息和彩色输出
4
5use colored::Colorize;
6use std::fmt;
7
8/// CLI 错误类型
9#[allow(dead_code)]
10#[derive(Debug, Clone)]
11/// # Examples
12///
13/// ```
14/// use uiautomator_cli::error::CliError;
15///
16/// let err = CliError::DeviceNotFound;
17/// assert!(!err.to_string().is_empty());
18/// ```
19pub enum CliError {
20    /// 未找到连接的设备
21    DeviceNotFound,
22
23    /// 检测到多个设备,需要指定序列号
24    MultipleDevices(Vec<String>),
25
26    /// 安装失败
27    InstallFailed(String),
28
29    /// ADB 错误
30    AdbError(String),
31
32    /// 服务错误
33    ServiceError(String),
34
35    /// 资源错误
36    ResourceError(String),
37}
38
39/// 错误消息构建器,用于减少重复代码
40#[allow(dead_code)]
41struct ErrorMessageBuilder {
42    title: String,
43    reasons: Vec<String>,
44    solutions: Vec<String>,
45}
46
47#[allow(dead_code)]
48impl ErrorMessageBuilder {
49    fn new(title: impl Into<String>) -> Self {
50        Self {
51            title: title.into(),
52            reasons: Vec::new(),
53            solutions: Vec::new(),
54        }
55    }
56
57    fn add_reason(mut self, reason: impl Into<String>) -> Self {
58        self.reasons.push(reason.into());
59        self
60    }
61
62    fn add_solution(mut self, solution: impl Into<String>) -> Self {
63        self.solutions.push(solution.into());
64        self
65    }
66
67    fn build(&self) -> String {
68        let mut message = self.title.clone();
69
70        if !self.reasons.is_empty() {
71            message.push_str("\n\n可能的原因:");
72            for (i, reason) in self.reasons.iter().enumerate() {
73                message.push_str(&format!("\n  {}. {}", i + 1, reason));
74            }
75        }
76
77        if !self.solutions.is_empty() {
78            message.push_str("\n\n解决方案:");
79            for (i, solution) in self.solutions.iter().enumerate() {
80                message.push_str(&format!("\n  {}. {}", i + 1, solution));
81            }
82        }
83
84        message
85    }
86
87    fn build_colored(&self) -> String {
88        let mut message = self.title.red().bold().to_string();
89
90        if !self.reasons.is_empty() {
91            message.push_str(&format!("\n\n{}", "可能的原因:".yellow().bold()));
92            for (i, reason) in self.reasons.iter().enumerate() {
93                message.push_str(&format!("\n  {}. {}", i + 1, reason.yellow()));
94            }
95        }
96
97        if !self.solutions.is_empty() {
98            message.push_str(&format!("\n\n{}", "解决方案:".green().bold()));
99            for (i, solution) in self.solutions.iter().enumerate() {
100                message.push_str(&format!("\n  {}. {}", i + 1, solution.green()));
101            }
102        }
103
104        message
105    }
106}
107
108impl CliError {
109    /// 获取彩色格式的错误消息
110    #[allow(dead_code)]
111    /// # Examples
112    ///
113    /// ```
114    /// use uiautomator_cli::error::CliError;
115    ///
116    /// let msg = CliError::DeviceNotFound.colored_message();
117    /// assert!(!msg.is_empty());
118    /// ```
119    pub fn colored_message(&self) -> String {
120        match self {
121            CliError::DeviceNotFound => ErrorMessageBuilder::new("未找到连接的设备")
122                .add_reason("设备未通过 USB 连接")
123                .add_reason("ADB 服务未启动")
124                .add_reason("设备未启用 USB 调试")
125                .add_solution("检查 USB 连接")
126                .add_solution("运行 'adb devices' 确认设备可见")
127                .add_solution("在设备上启用 USB 调试模式")
128                .build_colored(),
129
130            CliError::MultipleDevices(devices) => {
131                let device_list = devices
132                    .iter()
133                    .map(|d| format!("  - {}", d))
134                    .collect::<Vec<_>>()
135                    .join("\n");
136
137                format!(
138                    "{}\n\n{}\n{}\n\n{}\n{}",
139                    "检测到多个设备".red().bold(),
140                    "已连接的设备:".yellow().bold(),
141                    device_list.yellow(),
142                    "解决方案:".green().bold(),
143                    format!(
144                        "  使用 --serial 参数指定设备,例如: uiautomator --serial {} init",
145                        devices[0]
146                    )
147                    .green(),
148                )
149            }
150
151            CliError::InstallFailed(reason) => {
152                ErrorMessageBuilder::new(format!("安装失败: {}", reason))
153                    .add_reason("设备存储空间不足")
154                    .add_reason("权限不足")
155                    .add_solution("检查设备存储空间")
156                    .add_solution("尝试使用 --force 选项重新安装")
157                    .build_colored()
158            }
159
160            CliError::AdbError(error) => ErrorMessageBuilder::new(format!("ADB 错误: {}", error))
161                .add_reason("ADB 服务未运行")
162                .add_reason("设备连接不稳定")
163                .add_solution("重启 ADB 服务: adb kill-server && adb start-server")
164                .add_solution("重新连接设备")
165                .build_colored(),
166
167            CliError::ServiceError(error) => {
168                ErrorMessageBuilder::new(format!("服务错误: {}", error))
169                    .add_reason("ATX-Agent 未正确安装")
170                    .add_reason("服务启动超时")
171                    .add_solution("尝试重新安装: uiautomator init --force")
172                    .add_solution("检查设备日志: adb logcat | grep atx-agent")
173                    .build_colored()
174            }
175
176            CliError::ResourceError(error) => {
177                ErrorMessageBuilder::new(format!("资源错误: {}", error))
178                    .add_solution("请重新下载 CLI 工具或从源码重新编译")
179                    .build_colored()
180            }
181        }
182    }
183}
184
185impl fmt::Display for CliError {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        let message = match self {
188            CliError::DeviceNotFound => ErrorMessageBuilder::new("未找到连接的设备")
189                .add_reason("设备未通过 USB 连接")
190                .add_reason("ADB 服务未启动")
191                .add_reason("设备未启用 USB 调试")
192                .add_solution("检查 USB 连接")
193                .add_solution("运行 'adb devices' 确认设备可见")
194                .add_solution("在设备上启用 USB 调试模式")
195                .build(),
196
197            CliError::MultipleDevices(devices) => {
198                let device_list = devices
199                    .iter()
200                    .map(|d| format!("  - {}", d))
201                    .collect::<Vec<_>>()
202                    .join("\n");
203
204                format!(
205                    "检测到多个设备\n\n已连接的设备:\n{}\n\n解决方案:\n  使用 --serial 参数指定设备,例如: uiautomator --serial {} init",
206                    device_list,
207                    devices[0]
208                )
209            }
210
211            CliError::InstallFailed(reason) => {
212                ErrorMessageBuilder::new(format!("安装失败: {}", reason))
213                    .add_reason("设备存储空间不足")
214                    .add_reason("权限不足")
215                    .add_solution("检查设备存储空间")
216                    .add_solution("尝试使用 --force 选项重新安装")
217                    .build()
218            }
219
220            CliError::AdbError(error) => ErrorMessageBuilder::new(format!("ADB 错误: {}", error))
221                .add_reason("ADB 服务未运行")
222                .add_reason("设备连接不稳定")
223                .add_solution("重启 ADB 服务: adb kill-server && adb start-server")
224                .add_solution("重新连接设备")
225                .build(),
226
227            CliError::ServiceError(error) => {
228                ErrorMessageBuilder::new(format!("服务错误: {}", error))
229                    .add_reason("ATX-Agent 未正确安装")
230                    .add_reason("服务启动超时")
231                    .add_solution("尝试重新安装: uiautomator init --force")
232                    .add_solution("检查设备日志: adb logcat | grep atx-agent")
233                    .build()
234            }
235
236            CliError::ResourceError(error) => {
237                ErrorMessageBuilder::new(format!("资源错误: {}", error))
238                    .add_solution("请重新下载 CLI 工具或从源码重新编译")
239                    .build()
240            }
241        };
242
243        write!(f, "{}", message)
244    }
245}
246
247impl std::error::Error for CliError {}
248
249// 实现从其他错误类型的转换
250impl From<anyhow::Error> for CliError {
251    fn from(error: anyhow::Error) -> Self {
252        CliError::AdbError(error.to_string())
253    }
254}
255
256impl From<std::io::Error> for CliError {
257    fn from(error: std::io::Error) -> Self {
258        CliError::AdbError(error.to_string())
259    }
260}