1use std::{
2 ffi::c_void,
3 sync::{Arc, Mutex},
4};
5
6use crate::{bridge, error::Result, ffi, PropertyList, ReachabilityFlags};
7
8pub type NetworkConnectionFlags = ReachabilityFlags;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum NetworkConnectionStatus {
14 Invalid,
16 Disconnected,
18 Connecting,
20 Connected,
22 Disconnecting,
24 Unknown(i32),
26}
27
28impl NetworkConnectionStatus {
29 pub const fn from_raw(raw: i32) -> Self {
31 match raw {
32 -1 => Self::Invalid,
33 0 => Self::Disconnected,
34 1 => Self::Connecting,
35 2 => Self::Connected,
36 3 => Self::Disconnecting,
37 other => Self::Unknown(other),
38 }
39 }
40
41 pub const fn raw_value(self) -> i32 {
43 match self {
44 Self::Invalid => -1,
45 Self::Disconnected => 0,
46 Self::Connecting => 1,
47 Self::Connected => 2,
48 Self::Disconnecting => 3,
49 Self::Unknown(raw) => raw,
50 }
51 }
52}
53
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub enum NetworkConnectionPppStatus {
57 Disconnected,
59 Initializing,
61 ConnectingLink,
63 DialOnTraffic,
65 NegotiatingLink,
67 Authenticating,
69 WaitingForCallback,
71 NegotiatingNetwork,
73 Connected,
75 Terminating,
77 DisconnectingLink,
79 HoldingLinkOff,
81 Suspended,
83 WaitingForRedial,
85 Unknown(i32),
87}
88
89impl NetworkConnectionPppStatus {
90 pub const fn from_raw(raw: i32) -> Self {
92 match raw {
93 0 => Self::Disconnected,
94 1 => Self::Initializing,
95 2 => Self::ConnectingLink,
96 3 => Self::DialOnTraffic,
97 4 => Self::NegotiatingLink,
98 5 => Self::Authenticating,
99 6 => Self::WaitingForCallback,
100 7 => Self::NegotiatingNetwork,
101 8 => Self::Connected,
102 9 => Self::Terminating,
103 10 => Self::DisconnectingLink,
104 11 => Self::HoldingLinkOff,
105 12 => Self::Suspended,
106 13 => Self::WaitingForRedial,
107 other => Self::Unknown(other),
108 }
109 }
110
111 pub const fn raw_value(self) -> i32 {
113 match self {
114 Self::Disconnected => 0,
115 Self::Initializing => 1,
116 Self::ConnectingLink => 2,
117 Self::DialOnTraffic => 3,
118 Self::NegotiatingLink => 4,
119 Self::Authenticating => 5,
120 Self::WaitingForCallback => 6,
121 Self::NegotiatingNetwork => 7,
122 Self::Connected => 8,
123 Self::Terminating => 9,
124 Self::DisconnectingLink => 10,
125 Self::HoldingLinkOff => 11,
126 Self::Suspended => 12,
127 Self::WaitingForRedial => 13,
128 Self::Unknown(raw) => raw,
129 }
130 }
131}
132
133#[derive(Clone, Debug)]
134pub struct NetworkConnectionUserPreferences {
136 pub service_id: String,
138 pub user_options: Option<PropertyList>,
140}
141
142struct CallbackState {
143 callback: Box<dyn FnMut(NetworkConnectionStatus) + Send>,
144}
145
146unsafe extern "C" fn network_connection_callback(status: i32, info: *mut c_void) {
147 if info.is_null() {
148 return;
149 }
150
151 let mutex = &*info.cast::<Mutex<CallbackState>>();
152 if let Ok(mut state) = mutex.lock() {
153 (state.callback)(NetworkConnectionStatus::from_raw(status));
154 }
155}
156
157#[derive(Clone)]
158pub struct NetworkConnection {
160 raw: bridge::OwnedHandle,
161 _callback: Option<Arc<Mutex<CallbackState>>>,
162}
163
164impl std::fmt::Debug for NetworkConnection {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("NetworkConnection").finish_non_exhaustive()
167 }
168}
169
170impl NetworkConnection {
171 pub fn type_id() -> u64 {
173 unsafe { ffi::network_connection::sc_network_connection_get_type_id() }
174 }
175
176 pub fn with_service_id(service_id: &str) -> Result<Self> {
178 Self::create(service_id, None)
179 }
180
181 pub fn with_service_id_and_callback<F>(service_id: &str, callback: F) -> Result<Self>
183 where
184 F: FnMut(NetworkConnectionStatus) + Send + 'static,
185 {
186 let state = Arc::new(Mutex::new(CallbackState {
187 callback: Box::new(callback),
188 }));
189 Self::create(service_id, Some(state))
190 }
191
192 fn create(service_id: &str, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<Self> {
193 let service_id =
194 bridge::cstring(service_id, "sc_network_connection_create_with_service_id")?;
195 let raw = unsafe {
196 ffi::network_connection::sc_network_connection_create_with_service_id(
197 service_id.as_ptr(),
198 callback
199 .as_ref()
200 .map(|_| network_connection_callback as unsafe extern "C" fn(i32, *mut c_void)),
201 callback.as_ref().map_or(std::ptr::null_mut(), |state| {
202 Arc::as_ptr(state).cast_mut().cast::<c_void>()
203 }),
204 )
205 };
206 let raw =
207 bridge::owned_handle_or_last("sc_network_connection_create_with_service_id", raw)?;
208 Ok(Self {
209 raw,
210 _callback: callback,
211 })
212 }
213
214 pub fn copy_user_preferences() -> Result<NetworkConnectionUserPreferences> {
216 let service_id = bridge::take_optional_string(unsafe {
217 ffi::network_connection::sc_network_connection_copy_user_preferences_service_id()
218 })
219 .ok_or_else(|| {
220 crate::SystemConfigurationError::last(
221 "sc_network_connection_copy_user_preferences_service_id",
222 )
223 })?;
224 let user_options = unsafe {
225 bridge::OwnedHandle::from_raw(
226 ffi::network_connection::sc_network_connection_copy_user_preferences_user_options(),
227 )
228 }
229 .map(PropertyList::from_owned_handle);
230 Ok(NetworkConnectionUserPreferences {
231 service_id,
232 user_options,
233 })
234 }
235
236 pub fn service_id(&self) -> Result<Option<String>> {
238 Ok(bridge::take_optional_string(unsafe {
239 ffi::network_connection::sc_network_connection_copy_service_id(self.raw.as_ptr())
240 }))
241 }
242
243 pub fn status(&self) -> NetworkConnectionStatus {
245 NetworkConnectionStatus::from_raw(unsafe {
246 ffi::network_connection::sc_network_connection_get_status(self.raw.as_ptr())
247 })
248 }
249
250 pub fn extended_status(&self) -> Option<PropertyList> {
252 unsafe {
253 bridge::OwnedHandle::from_raw(
254 ffi::network_connection::sc_network_connection_copy_extended_status(
255 self.raw.as_ptr(),
256 ),
257 )
258 }
259 .map(PropertyList::from_owned_handle)
260 }
261
262 pub fn statistics(&self) -> Option<PropertyList> {
264 unsafe {
265 bridge::OwnedHandle::from_raw(
266 ffi::network_connection::sc_network_connection_copy_statistics(self.raw.as_ptr()),
267 )
268 }
269 .map(PropertyList::from_owned_handle)
270 }
271
272 pub fn user_options(&self) -> Option<PropertyList> {
274 unsafe {
275 bridge::OwnedHandle::from_raw(
276 ffi::network_connection::sc_network_connection_copy_user_options(self.raw.as_ptr()),
277 )
278 }
279 .map(PropertyList::from_owned_handle)
280 }
281
282 pub fn start(&self, user_options: Option<&PropertyList>, linger: bool) -> Result<()> {
284 let ok = unsafe {
285 ffi::network_connection::sc_network_connection_start(
286 self.raw.as_ptr(),
287 user_options.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
288 u8::from(linger),
289 )
290 };
291 bridge::bool_result("sc_network_connection_start", ok)
292 }
293
294 pub fn stop(&self, force_disconnect: bool) -> Result<()> {
296 let ok = unsafe {
297 ffi::network_connection::sc_network_connection_stop(
298 self.raw.as_ptr(),
299 u8::from(force_disconnect),
300 )
301 };
302 bridge::bool_result("sc_network_connection_stop", ok)
303 }
304
305 pub fn schedule_with_run_loop_current(&self) -> Result<()> {
307 let ok = unsafe {
308 ffi::network_connection::sc_network_connection_schedule_with_run_loop_current(
309 self.raw.as_ptr(),
310 )
311 };
312 bridge::bool_result("sc_network_connection_schedule_with_run_loop_current", ok)
313 }
314
315 pub fn unschedule_from_run_loop_current(&self) -> Result<()> {
317 let ok = unsafe {
318 ffi::network_connection::sc_network_connection_unschedule_from_run_loop_current(
319 self.raw.as_ptr(),
320 )
321 };
322 bridge::bool_result("sc_network_connection_unschedule_from_run_loop_current", ok)
323 }
324
325 pub fn set_dispatch_queue_global(&self) -> Result<()> {
327 let ok = unsafe {
328 ffi::network_connection::sc_network_connection_set_dispatch_queue_global(
329 self.raw.as_ptr(),
330 )
331 };
332 bridge::bool_result("sc_network_connection_set_dispatch_queue_global", ok)
333 }
334
335 pub fn clear_dispatch_queue(&self) -> Result<()> {
337 let ok = unsafe {
338 ffi::network_connection::sc_network_connection_clear_dispatch_queue(self.raw.as_ptr())
339 };
340 bridge::bool_result("sc_network_connection_clear_dispatch_queue", ok)
341 }
342}