1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::fmt;
4use std::sync::{Arc, Mutex};
5
6use crate::config::ADBConfig;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub enum DeviceStatus {
11 Online,
12 Offline,
13 Unauthorized,
14 Recovery,
15 Sideload,
16 Bootloader,
17 Other(String),
18}
19
20impl fmt::Display for DeviceStatus {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 DeviceStatus::Online => write!(f, "online"),
24 DeviceStatus::Offline => write!(f, "offline"),
25 DeviceStatus::Unauthorized => write!(f, "unauthorized"),
26 DeviceStatus::Recovery => write!(f, "recovery"),
27 DeviceStatus::Sideload => write!(f, "sideload"),
28 DeviceStatus::Bootloader => write!(f, "bootloader"),
29 DeviceStatus::Other(s) => write!(f, "{}", s),
30 }
31 }
32}
33
34impl From<&str> for DeviceStatus {
35 fn from(s: &str) -> Self {
36 match s.to_lowercase().as_str() {
37 "device" | "online" => DeviceStatus::Online,
38 "offline" => DeviceStatus::Offline,
39 "unauthorized" => DeviceStatus::Unauthorized,
40 "recovery" => DeviceStatus::Recovery,
41 "sideload" => DeviceStatus::Sideload,
42 "bootloader" | "fastboot" => DeviceStatus::Bootloader,
43 _ => DeviceStatus::Other(s.to_string()),
44 }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ADBDevice {
51 pub id: String,
52 pub name: String,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub model: Option<String>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub product: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub transport_id: Option<String>,
59 pub status: DeviceStatus,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub properties: Option<HashMap<String, String>>,
62}
63
64impl ADBDevice {
65 pub fn new(id: &str, status: impl Into<DeviceStatus>) -> Self {
67 Self {
68 id: id.to_string(),
69 name: format!("Device {}", id),
70 model: None,
71 product: None,
72 transport_id: None,
73 status: status.into(),
74 properties: None,
75 }
76 }
77
78 pub fn is_online(&self) -> bool {
80 self.status == DeviceStatus::Online
81 }
82
83 pub fn with_name(mut self, name: &str) -> Self {
85 self.name = name.to_string();
86 self
87 }
88
89 pub fn with_model(mut self, model: &str) -> Self {
91 self.model = Some(model.to_string());
92 self
93 }
94
95 pub fn with_product(mut self, product: &str) -> Self {
97 self.product = Some(product.to_string());
98 self
99 }
100
101 pub fn with_transport_id(mut self, transport_id: &str) -> Self {
103 self.transport_id = Some(transport_id.to_string());
104 self
105 }
106
107 pub fn add_property(mut self, key: &str, value: &str) -> Self {
109 if self.properties.is_none() {
110 self.properties = Some(HashMap::new());
111 }
112
113 if let Some(props) = &mut self.properties {
114 props.insert(key.to_string(), value.to_string());
115 }
116
117 self
118 }
119}
120
121type DevicePool = HashMap<String, Arc<Mutex<std::process::Child>>>;
123
124#[derive(Clone, Debug)]
126pub struct ADB {
127 pub config: ADBConfig,
128 pub(crate) connections: Arc<Mutex<DevicePool>>,
129}
130
131impl ADB {
132 pub fn new(config: Option<ADBConfig>) -> Self {
134 Self {
135 config: config.unwrap_or_default(),
136 connections: Arc::new(Mutex::new(HashMap::new())),
137 }
138 }
139
140 pub fn adb_path(&self) -> &std::path::PathBuf {
142 &self.config.path
143 }
144}