adbutils/errors.rs
1//! Error types. Port of `adbutils/errors.py`.
2
3use std::sync::LazyLock;
4
5use regex::Regex;
6
7/// Result alias used throughout the crate.
8pub type Result<T> = std::result::Result<T, AdbError>;
9
10/// All adb errors. Mirrors the Python exception hierarchy, which is flat under
11/// `AdbError`; here the variants distinguish the former subclasses.
12#[derive(Debug, thiserror::Error)]
13pub enum AdbError {
14 /// Generic adb error carrying the server-reported message (`AdbError`).
15 #[error("{0}")]
16 Adb(String),
17
18 /// Timeout communicating with the adb server (`AdbTimeout`).
19 #[error("adb timeout: {0}")]
20 Timeout(String),
21
22 /// Connection error talking to the adb server (`AdbConnectionError`).
23 #[error("adb connection error: {0}")]
24 Connection(String),
25
26 /// APK install failure (`AdbInstallError`). `reason` is parsed from the
27 /// `Failure [REASON]` marker in the `pm install` output.
28 #[error("{output}")]
29 Install { reason: String, output: String },
30
31 /// Sync sub-protocol error (`AdbSyncError`).
32 #[error("adb sync error: {0}")]
33 Sync(String),
34
35 /// Underlying IO error.
36 #[error(transparent)]
37 Io(#[from] std::io::Error),
38}
39
40impl AdbError {
41 /// Build an [`AdbError::Adb`] from anything stringy.
42 pub fn adb(msg: impl Into<String>) -> Self {
43 AdbError::Adb(msg.into())
44 }
45
46 /// Parse `pm install` output into an [`AdbError::Install`], extracting the
47 /// bracketed reason from a `Failure [REASON]` line (default `"Unknown"`).
48 /// Mirrors `AdbInstallError.__init__` (`errors.py:24`).
49 pub fn install(output: impl Into<String>) -> Self {
50 static RE: LazyLock<Regex> =
51 LazyLock::new(|| Regex::new(r"Failure \[([A-Z_]+).*\]").unwrap());
52 let output = output.into();
53 let reason = RE
54 .captures(&output)
55 .and_then(|c| c.get(1))
56 .map(|m| m.as_str().to_string())
57 .unwrap_or_else(|| "Unknown".to_string());
58 AdbError::Install { reason, output }
59 }
60}