1use std::{
2 error::Error,
3 future::Future,
4 io,
5 pin::Pin,
6 sync::mpsc::{self, Receiver, TryRecvError},
7};
8
9use crate::{
10 app::runtime::{
11 RuntimeBackendDriver,
12 RuntimeEvent,
13 RuntimeRequest,
14 ScanSnapshot,
15 },
16 network::ConnectionRequest,
17 wifi::WifiNetwork,
18};
19
20pub type BackendFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
21
22pub trait NetworkBackend {
23 fn connected_ssid(&self) -> Result<Option<String>, Box<dyn Error>>;
24 fn adapter_name(&self) -> Result<Option<String>, Box<dyn Error>>;
25 fn scan_networks(
26 &self,
27 ) -> BackendFuture<'_, Result<Vec<WifiNetwork>, Box<dyn Error>>>;
28 fn connect(
29 &self,
30 request: ConnectionRequest<'_>,
31 ) -> Result<(), Box<dyn Error>>;
32 fn disconnect(&self, network: &WifiNetwork) -> Result<(), Box<dyn Error>>;
33}
34
35fn runtime_channel_closed_error() -> Box<dyn Error> {
36 io::Error::other("runtime backend event channel closed").into()
37}
38
39fn poll_pending_event(
40 pending_event: &mut Option<Receiver<RuntimeEvent>>,
41) -> Result<Option<RuntimeEvent>, Box<dyn Error>> {
42 match pending_event.as_mut() {
43 Some(receiver) => match receiver.try_recv() {
44 Ok(event) => {
45 *pending_event = None;
46 Ok(Some(event))
47 }
48 Err(TryRecvError::Empty) => Ok(None),
49 Err(TryRecvError::Disconnected) => {
50 *pending_event = None;
51 Err(runtime_channel_closed_error())
52 }
53 },
54 None => Ok(None),
55 }
56}
57
58#[cfg(feature = "demo")]
59#[derive(Debug, Default, Clone, Copy)]
60pub struct DemoNetworkBackend;
61
62#[cfg(feature = "demo")]
63impl NetworkBackend for DemoNetworkBackend {
64 fn connected_ssid(&self) -> Result<Option<String>, Box<dyn Error>> {
65 crate::network::demo::get_connected_ssid()
66 }
67
68 fn adapter_name(&self) -> Result<Option<String>, Box<dyn Error>> {
69 crate::network::demo::get_wifi_adapter_name()
70 }
71
72 fn scan_networks(
73 &self,
74 ) -> BackendFuture<'_, Result<Vec<WifiNetwork>, Box<dyn Error>>> {
75 Box::pin(crate::network::demo::scan_wifi_networks())
76 }
77
78 fn connect(
79 &self,
80 request: ConnectionRequest<'_>,
81 ) -> Result<(), Box<dyn Error>> {
82 crate::network::demo::connect_to_network(request)
83 }
84
85 fn disconnect(&self, network: &WifiNetwork) -> Result<(), Box<dyn Error>> {
86 crate::network::demo::disconnect_from_network(network)
87 }
88}
89
90#[cfg(feature = "demo")]
91#[derive(Default)]
92struct DemoRuntimeDriver {
93 pending_event: Option<Receiver<RuntimeEvent>>,
94}
95
96#[cfg(feature = "demo")]
97impl RuntimeBackendDriver for DemoRuntimeDriver {
98 fn begin(&mut self, request: RuntimeRequest) {
99 let (sender, receiver) = mpsc::channel();
100 let event = match request {
101 RuntimeRequest::Scan => RuntimeEvent::Scan(Ok(ScanSnapshot {
102 networks: crate::network::demo::demo_networks(),
103 adapter_name: crate::network::demo::get_wifi_adapter_name()
104 .ok()
105 .flatten(),
106 })),
107 RuntimeRequest::Connect {
108 network,
109 passphrase,
110 } => {
111 let result = match passphrase.as_deref() {
112 Some(passphrase) => {
113 crate::network::demo::connect_to_network(
114 ConnectionRequest::Secured {
115 network: &network,
116 passphrase,
117 },
118 )
119 }
120 None => crate::network::demo::connect_to_network(
121 ConnectionRequest::Open { network: &network },
122 ),
123 };
124 RuntimeEvent::Connect(result.map_err(|error| error.to_string()))
125 }
126 RuntimeRequest::Disconnect { network } => RuntimeEvent::Disconnect(
127 crate::network::demo::disconnect_from_network(&network)
128 .map_err(|error| error.to_string()),
129 ),
130 };
131 let _ = sender.send(event);
132 self.pending_event = Some(receiver);
133 }
134
135 fn poll_event(&mut self) -> Result<Option<RuntimeEvent>, Box<dyn Error>> {
136 poll_pending_event(&mut self.pending_event)
137 }
138}
139
140#[cfg(feature = "demo")]
141pub(crate) fn default_runtime_driver() -> Box<dyn RuntimeBackendDriver> {
142 Box::new(DemoRuntimeDriver::default())
143}
144
145#[cfg(not(feature = "demo"))]
146#[derive(Debug, Default, Clone, Copy)]
147pub struct NetworkManagerBackend;
148
149#[cfg(not(feature = "demo"))]
150impl NetworkBackend for NetworkManagerBackend {
151 fn connected_ssid(&self) -> Result<Option<String>, Box<dyn Error>> {
152 crate::network::networkmanager::get_connected_ssid()
153 }
154
155 fn adapter_name(&self) -> Result<Option<String>, Box<dyn Error>> {
156 crate::network::networkmanager::get_wifi_adapter_name()
157 }
158
159 fn scan_networks(
160 &self,
161 ) -> BackendFuture<'_, Result<Vec<WifiNetwork>, Box<dyn Error>>> {
162 Box::pin(crate::network::networkmanager::scan_wifi_networks())
163 }
164
165 fn connect(
166 &self,
167 request: ConnectionRequest<'_>,
168 ) -> Result<(), Box<dyn Error>> {
169 crate::network::networkmanager::connect_to_network(request)
170 }
171
172 fn disconnect(&self, network: &WifiNetwork) -> Result<(), Box<dyn Error>> {
173 crate::network::networkmanager::disconnect_from_network(network)
174 }
175}
176
177#[cfg(not(feature = "demo"))]
178#[derive(Default)]
179struct NetworkManagerRuntimeDriver {
180 pending_event: Option<Receiver<RuntimeEvent>>,
181}
182
183#[cfg(not(feature = "demo"))]
184impl RuntimeBackendDriver for NetworkManagerRuntimeDriver {
185 fn begin(&mut self, request: RuntimeRequest) {
186 let (sender, receiver) = mpsc::channel();
187
188 match request {
189 RuntimeRequest::Scan => {
190 tokio::spawn(async move {
191 let event = match tokio::task::spawn_blocking(|| {
192 let networks = crate::network::networkmanager::scan_wifi_networks_blocking();
193 let adapter_name = crate::network::networkmanager::get_wifi_adapter_name()
194 .ok()
195 .flatten();
196
197 match networks {
198 Ok(networks) => RuntimeEvent::Scan(Ok(ScanSnapshot {
199 networks,
200 adapter_name,
201 })),
202 Err(error) => RuntimeEvent::Scan(Err(error.to_string())),
203 }
204 })
205 .await
206 {
207 Ok(event) => event,
208 Err(error) => RuntimeEvent::Scan(Err(format!(
209 "runtime scan task failed: {error}"
210 ))),
211 };
212
213 let _ = sender.send(event);
214 });
215 }
216 RuntimeRequest::Connect {
217 network,
218 passphrase,
219 } => {
220 tokio::spawn(async move {
221 let event = match tokio::task::spawn_blocking(move || {
222 let result = match passphrase.as_deref() {
223 Some(passphrase) => crate::network::networkmanager::connect_to_network(
224 ConnectionRequest::Secured {
225 network: &network,
226 passphrase,
227 },
228 ),
229 None => crate::network::networkmanager::connect_to_network(
230 ConnectionRequest::Open { network: &network },
231 ),
232 };
233
234 RuntimeEvent::Connect(result.map_err(|error| error.to_string()))
235 })
236 .await
237 {
238 Ok(event) => event,
239 Err(error) => RuntimeEvent::Connect(Err(format!(
240 "runtime connect task failed: {error}"
241 ))),
242 };
243
244 let _ = sender.send(event);
245 });
246 }
247 RuntimeRequest::Disconnect { network } => {
248 tokio::spawn(async move {
249 let event = match tokio::task::spawn_blocking(move || {
250 RuntimeEvent::Disconnect(
251 crate::network::networkmanager::disconnect_from_network(&network)
252 .map_err(|error| error.to_string()),
253 )
254 })
255 .await
256 {
257 Ok(event) => event,
258 Err(error) => RuntimeEvent::Disconnect(Err(format!(
259 "runtime disconnect task failed: {error}"
260 ))),
261 };
262
263 let _ = sender.send(event);
264 });
265 }
266 }
267
268 self.pending_event = Some(receiver);
269 }
270
271 fn poll_event(&mut self) -> Result<Option<RuntimeEvent>, Box<dyn Error>> {
272 poll_pending_event(&mut self.pending_event)
273 }
274}
275
276#[cfg(not(feature = "demo"))]
277pub(crate) fn default_runtime_driver() -> Box<dyn RuntimeBackendDriver> {
278 Box::new(NetworkManagerRuntimeDriver::default())
279}
280
281#[cfg(feature = "demo")]
282pub fn default_backend() -> Box<dyn NetworkBackend> {
283 Box::new(DemoNetworkBackend)
284}
285
286#[cfg(not(feature = "demo"))]
287pub fn default_backend() -> Box<dyn NetworkBackend> {
288 Box::new(NetworkManagerBackend)
289}