1use bluez_generated::OrgBluezAdapter1Properties;
2use dbus::Path;
3use serde::{Deserialize, Serialize};
4use std::fmt::{self, Display, Formatter};
5
6use crate::Modalias;
7use crate::{AddressType, BluetoothError, MacAddress};
8
9#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
11pub struct AdapterId {
12 #[serde(with = "crate::serde_path")]
13 pub(crate) object_path: Path<'static>,
14}
15
16impl AdapterId {
17 pub(crate) fn new(object_path: &str) -> Self {
18 Self {
19 object_path: object_path.to_owned().into(),
20 }
21 }
22}
23
24impl From<AdapterId> for Path<'static> {
25 fn from(id: AdapterId) -> Self {
26 id.object_path
27 }
28}
29
30impl Display for AdapterId {
31 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
32 write!(
33 f,
34 "{}",
35 self.object_path
36 .to_string()
37 .strip_prefix("/org/bluez/")
38 .ok_or(fmt::Error)?
39 )
40 }
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct AdapterInfo {
46 pub id: AdapterId,
48 pub mac_address: MacAddress,
50 pub address_type: AddressType,
52 pub name: String,
54 pub alias: String,
56 pub modalias: Modalias,
58 pub powered: bool,
60 pub discovering: bool,
62}
63
64impl AdapterInfo {
65 pub(crate) fn from_properties(
66 id: AdapterId,
67 adapter_properties: OrgBluezAdapter1Properties,
68 ) -> Result<AdapterInfo, BluetoothError> {
69 let mac_address = adapter_properties
70 .address()
71 .ok_or(BluetoothError::RequiredPropertyMissing("Address"))?
72 .parse()?;
73 let address_type = adapter_properties
74 .address_type()
75 .ok_or(BluetoothError::RequiredPropertyMissing("AddressType"))?
76 .parse()?;
77 let modalias = adapter_properties
78 .modalias()
79 .ok_or(BluetoothError::RequiredPropertyMissing("Modalias"))?
80 .parse()?;
81
82 Ok(AdapterInfo {
83 id,
84 mac_address,
85 address_type,
86 name: adapter_properties
87 .name()
88 .ok_or(BluetoothError::RequiredPropertyMissing("Name"))?
89 .to_owned(),
90 alias: adapter_properties
91 .alias()
92 .ok_or(BluetoothError::RequiredPropertyMissing("Alias"))?
93 .to_owned(),
94 modalias,
95 powered: adapter_properties
96 .powered()
97 .ok_or(BluetoothError::RequiredPropertyMissing("Powered"))?,
98 discovering: adapter_properties
99 .discovering()
100 .ok_or(BluetoothError::RequiredPropertyMissing("Discovering"))?,
101 })
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use dbus::arg::{PropMap, Variant};
108 use std::collections::HashMap;
109
110 use super::*;
111
112 #[test]
113 fn adapter_info_minimal() {
114 let id = AdapterId::new("/org/bluez/hci0");
115 let mut adapter_properties: PropMap = HashMap::new();
116 adapter_properties.insert(
117 "Address".to_string(),
118 Variant(Box::new("00:11:22:33:44:55".to_string())),
119 );
120 adapter_properties.insert(
121 "AddressType".to_string(),
122 Variant(Box::new("public".to_string())),
123 );
124 adapter_properties.insert("Name".to_string(), Variant(Box::new("name".to_string())));
125 adapter_properties.insert("Alias".to_string(), Variant(Box::new("alias".to_string())));
126 adapter_properties.insert(
127 "Modalias".to_string(),
128 Variant(Box::new("usb:v1234p5678d90AB".to_string())),
129 );
130 adapter_properties.insert("Powered".to_string(), Variant(Box::new(false)));
131 adapter_properties.insert("Discovering".to_string(), Variant(Box::new(false)));
132
133 let adapter = AdapterInfo::from_properties(
134 id.clone(),
135 OrgBluezAdapter1Properties(&adapter_properties),
136 )
137 .unwrap();
138 assert_eq!(
139 adapter,
140 AdapterInfo {
141 id,
142 mac_address: "00:11:22:33:44:55".parse().unwrap(),
143 address_type: AddressType::Public,
144 name: "name".to_string(),
145 alias: "alias".to_string(),
146 modalias: Modalias {
147 vendor_id: 0x1234,
148 product_id: 0x5678,
149 device_id: 0x90ab
150 },
151 powered: false,
152 discovering: false
153 }
154 )
155 }
156
157 #[test]
158 fn to_string() {
159 let adapter_id = AdapterId::new("/org/bluez/hci0");
160 assert_eq!(adapter_id.to_string(), "hci0");
161 }
162}