1use crate::handle::Handle;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(transparent)]
11pub struct SubscriptionHandle(pub Handle);
12
13impl SubscriptionHandle {
14 #[inline]
16 #[must_use]
17 pub const fn new(id: u32, generation: u32) -> Self {
18 Self(Handle::new(id, generation))
19 }
20
21 #[inline]
23 #[must_use]
24 pub const fn null() -> Self {
25 Self(Handle::null())
26 }
27
28 #[inline]
30 #[must_use]
31 pub const fn is_null(&self) -> bool {
32 self.0.is_null()
33 }
34
35 #[inline]
37 #[must_use]
38 pub const fn raw(&self) -> Handle {
39 self.0
40 }
41}
42
43impl Default for SubscriptionHandle {
44 fn default() -> Self {
45 Self::null()
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51#[repr(u8)]
52pub enum SensorType {
53 Camera = 0,
55
56 Microphone = 1,
58
59 NetworkTap = 2,
61
62 MarketFeed = 3,
64
65 GitStream = 4,
67
68 FileSystem = 5,
70
71 SystemMetrics = 6,
73
74 Custom = 255,
76}
77
78impl SensorType {
79 #[inline]
81 #[must_use]
82 pub const fn as_str(&self) -> &'static str {
83 match self {
84 Self::Camera => "Camera",
85 Self::Microphone => "Microphone",
86 Self::NetworkTap => "NetworkTap",
87 Self::MarketFeed => "MarketFeed",
88 Self::GitStream => "GitStream",
89 Self::FileSystem => "FileSystem",
90 Self::SystemMetrics => "SystemMetrics",
91 Self::Custom => "Custom",
92 }
93 }
94
95 #[inline]
97 #[must_use]
98 pub const fn from_u8(value: u8) -> Option<Self> {
99 match value {
100 0 => Some(Self::Camera),
101 1 => Some(Self::Microphone),
102 2 => Some(Self::NetworkTap),
103 3 => Some(Self::MarketFeed),
104 4 => Some(Self::GitStream),
105 5 => Some(Self::FileSystem),
106 6 => Some(Self::SystemMetrics),
107 255 => Some(Self::Custom),
108 _ => None,
109 }
110 }
111}
112
113impl Default for SensorType {
114 fn default() -> Self {
115 Self::Custom
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123#[repr(C)]
124pub struct SensorDescriptor {
125 pub sensor_type: SensorType,
127
128 pub device_id: u64,
131
132 pub filter_hash: u64,
135
136 pub sample_rate: u32,
139}
140
141impl SensorDescriptor {
142 #[inline]
144 #[must_use]
145 pub const fn new(sensor_type: SensorType, device_id: u64) -> Self {
146 Self {
147 sensor_type,
148 device_id,
149 filter_hash: 0,
150 sample_rate: 0,
151 }
152 }
153
154 #[inline]
156 #[must_use]
157 pub const fn with_sample_rate(mut self, rate: u32) -> Self {
158 self.sample_rate = rate;
159 self
160 }
161
162 #[inline]
164 #[must_use]
165 pub const fn with_filter(mut self, filter_hash: u64) -> Self {
166 self.filter_hash = filter_hash;
167 self
168 }
169
170 #[inline]
172 #[must_use]
173 pub const fn is_downsampled(&self) -> bool {
174 self.sample_rate > 0
175 }
176
177 #[inline]
179 #[must_use]
180 pub const fn has_filter(&self) -> bool {
181 self.filter_hash != 0
182 }
183}
184
185impl Default for SensorDescriptor {
186 fn default() -> Self {
187 Self {
188 sensor_type: SensorType::Custom,
189 device_id: 0,
190 filter_hash: 0,
191 sample_rate: 0,
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_subscription_handle() {
202 let h = SubscriptionHandle::new(3, 5);
203 assert!(!h.is_null());
204 assert_eq!(h.raw().id, 3);
205 }
206
207 #[test]
208 fn test_sensor_type_roundtrip() {
209 for i in 0..=6 {
210 let st = SensorType::from_u8(i).unwrap();
211 assert_eq!(st as u8, i);
212 }
213 assert!(SensorType::from_u8(100).is_none());
214 assert_eq!(SensorType::from_u8(255), Some(SensorType::Custom));
215 }
216
217 #[test]
218 fn test_sensor_descriptor() {
219 let desc = SensorDescriptor::new(SensorType::Camera, 12345)
220 .with_sample_rate(30)
221 .with_filter(0xABCD);
222
223 assert!(desc.is_downsampled());
224 assert!(desc.has_filter());
225 assert_eq!(desc.sample_rate, 30);
226 }
227}