Skip to main content

fission_core/
platform_wifi.rs

1//! Wi-Fi host capabilities.
2
3use crate::capability::{CapabilityType, OperationCapability};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
7pub enum WifiPermission {
8    #[default]
9    Unknown,
10    Granted,
11    Denied,
12    Restricted,
13}
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
16pub enum WifiSecurity {
17    Open,
18    Wep,
19    Wpa,
20    #[default]
21    Wpa2,
22    Wpa3,
23    Enterprise,
24    Unknown,
25}
26
27#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
28pub struct WifiNetwork {
29    pub ssid: String,
30    pub bssid: Option<String>,
31    pub rssi: Option<i16>,
32    pub frequency_mhz: Option<u32>,
33    pub security: WifiSecurity,
34    pub connected: bool,
35}
36
37#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
38pub struct WifiAvailability {
39    pub permission: WifiPermission,
40    pub enabled: bool,
41    pub connected_network: Option<WifiNetwork>,
42}
43
44#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
45pub struct WifiPermissionRequest {
46    pub reason: Option<String>,
47}
48
49#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
50pub struct WifiScanRequest {
51    pub ssid_prefix: Option<String>,
52    pub include_hidden: bool,
53    pub timeout_ms: Option<u64>,
54}
55
56#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
57pub struct WifiScanResult {
58    pub networks: Vec<WifiNetwork>,
59}
60
61#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
62pub struct WifiConnectRequest {
63    pub ssid: String,
64    pub passphrase: Option<String>,
65    pub security: WifiSecurity,
66    pub hidden: bool,
67}
68
69#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
70pub struct WifiConnection {
71    pub network: WifiNetwork,
72}
73
74#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
75pub struct WifiDisconnectRequest {
76    pub ssid: Option<String>,
77}
78
79#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
80pub struct WifiError {
81    pub code: String,
82    pub message: String,
83}
84
85impl WifiError {
86    /// Creates a portable wi-fi error payload.
87    ///
88    /// `code` should be a stable, machine-readable reason such as
89    /// `unsupported`, `permission_denied`, or `timeout`. `message` should be a
90    /// concise human-readable explanation suitable for logs or developer-facing
91    /// diagnostics.
92    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
93        Self {
94            code: code.into(),
95            message: message.into(),
96        }
97    }
98
99    /// Creates the standard unsupported-operation error for this capability.
100    ///
101    /// `operation` should name the attempted wi-fi operation. Use this
102    /// from hosts that implement the capability contract but cannot provide this
103    /// operation on the current platform or hardware.
104    pub fn unsupported(operation: impl Into<String>) -> Self {
105        Self::new(
106            "unsupported",
107            format!(
108                "Wi-Fi operation `{}` is not supported by this host",
109                operation.into()
110            ),
111        )
112    }
113}
114
115pub struct GetWifiAvailabilityCapability;
116impl OperationCapability for GetWifiAvailabilityCapability {
117    type Request = ();
118    type Ok = WifiAvailability;
119    type Err = WifiError;
120}
121
122pub struct RequestWifiPermissionCapability;
123impl OperationCapability for RequestWifiPermissionCapability {
124    type Request = WifiPermissionRequest;
125    type Ok = WifiPermission;
126    type Err = WifiError;
127}
128
129pub struct ScanWifiNetworksCapability;
130impl OperationCapability for ScanWifiNetworksCapability {
131    type Request = WifiScanRequest;
132    type Ok = WifiScanResult;
133    type Err = WifiError;
134}
135
136pub struct ConnectWifiNetworkCapability;
137impl OperationCapability for ConnectWifiNetworkCapability {
138    type Request = WifiConnectRequest;
139    type Ok = WifiConnection;
140    type Err = WifiError;
141}
142
143pub struct DisconnectWifiNetworkCapability;
144impl OperationCapability for DisconnectWifiNetworkCapability {
145    type Request = WifiDisconnectRequest;
146    type Ok = ();
147    type Err = WifiError;
148}
149
150pub const GET_WIFI_AVAILABILITY: CapabilityType<GetWifiAvailabilityCapability> =
151    CapabilityType::new("fission.wifi.get_availability");
152pub const REQUEST_WIFI_PERMISSION: CapabilityType<RequestWifiPermissionCapability> =
153    CapabilityType::new("fission.wifi.request_permission");
154pub const SCAN_WIFI_NETWORKS: CapabilityType<ScanWifiNetworksCapability> =
155    CapabilityType::new("fission.wifi.scan_networks");
156pub const CONNECT_WIFI_NETWORK: CapabilityType<ConnectWifiNetworkCapability> =
157    CapabilityType::new("fission.wifi.connect_network");
158pub const DISCONNECT_WIFI_NETWORK: CapabilityType<DisconnectWifiNetworkCapability> =
159    CapabilityType::new("fission.wifi.disconnect_network");
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn wifi_scan_result_round_trips() {
167        let result = WifiScanResult {
168            networks: vec![WifiNetwork {
169                ssid: "Fission".into(),
170                bssid: Some("00:11:22:33:44:55".into()),
171                rssi: Some(-50),
172                frequency_mhz: Some(5_200),
173                security: WifiSecurity::Wpa3,
174                connected: false,
175            }],
176        };
177
178        let bytes = serde_json::to_vec(&result).unwrap();
179        let decoded: WifiScanResult = serde_json::from_slice(&bytes).unwrap();
180
181        assert_eq!(decoded, result);
182    }
183}