relay_event_schema/protocol/contexts/
device.rs

1use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, Value};
2use uuid::Uuid;
3
4use crate::processor::ProcessValue;
5
6/// Device information.
7///
8/// Device context describes the device that caused the event. This is most appropriate for mobile
9/// applications.
10#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
11pub struct DeviceContext {
12    /// Name of the device.
13    #[metastructure(pii = "maybe")]
14    pub name: Annotated<String>,
15
16    /// Family of the device model.
17    ///
18    /// This is usually the common part of model names across generations. For instance, `iPhone`
19    /// would be a reasonable family, so would be `Samsung Galaxy`.
20    pub family: Annotated<String>,
21
22    /// Device model.
23    ///
24    /// This, for example, can be `Samsung Galaxy S3`.
25    pub model: Annotated<String>,
26
27    /// Device model (internal identifier).
28    ///
29    /// An internal hardware revision to identify the device exactly.
30    pub model_id: Annotated<String>,
31
32    /// Native cpu architecture of the device.
33    pub arch: Annotated<String>,
34
35    /// Current battery level in %.
36    ///
37    /// If the device has a battery, this can be a floating point value defining the battery level
38    /// (in the range 0-100).
39    pub battery_level: Annotated<f64>,
40
41    /// Current screen orientation.
42    ///
43    /// This can be a string `portrait` or `landscape` to define the orientation of a device.
44    pub orientation: Annotated<String>,
45
46    /// Manufacturer of the device.
47    pub manufacturer: Annotated<String>,
48
49    /// Brand of the device.
50    pub brand: Annotated<String>,
51
52    /// Device screen resolution.
53    ///
54    /// (e.g.: 800x600, 3040x1444)
55    #[metastructure(pii = "maybe")]
56    pub screen_resolution: Annotated<String>,
57
58    /// Width of the screen in pixels.
59    #[metastructure(pii = "maybe")]
60    pub screen_width_pixels: Annotated<u64>,
61
62    /// Height of the screen in pixels.
63    #[metastructure(pii = "maybe")]
64    pub screen_height_pixels: Annotated<u64>,
65
66    /// Device screen density.
67    #[metastructure(pii = "maybe")]
68    pub screen_density: Annotated<f64>,
69
70    /// Screen density as dots-per-inch.
71    #[metastructure(pii = "maybe")]
72    pub screen_dpi: Annotated<u64>,
73
74    /// Whether the device was online or not.
75    pub online: Annotated<bool>,
76
77    /// Whether the device was charging or not.
78    pub charging: Annotated<bool>,
79
80    /// Whether the device was low on memory.
81    pub low_memory: Annotated<bool>,
82
83    /// Simulator/prod indicator.
84    pub simulator: Annotated<bool>,
85
86    /// Total memory available in bytes.
87    #[metastructure(pii = "maybe")]
88    pub memory_size: Annotated<u64>,
89
90    /// How much memory is still available in bytes.
91    #[metastructure(pii = "maybe")]
92    pub free_memory: Annotated<u64>,
93
94    /// How much memory is usable for the app in bytes.
95    #[metastructure(pii = "maybe")]
96    pub usable_memory: Annotated<u64>,
97
98    /// Total storage size of the device in bytes.
99    #[metastructure(pii = "maybe")]
100    pub storage_size: Annotated<u64>,
101
102    /// How much storage is free in bytes.
103    #[metastructure(pii = "maybe")]
104    pub free_storage: Annotated<u64>,
105
106    /// Total size of the attached external storage in bytes (eg: android SDK card).
107    #[metastructure(pii = "maybe")]
108    pub external_storage_size: Annotated<u64>,
109
110    /// Free size of the attached external storage in bytes (eg: android SDK card).
111    #[metastructure(pii = "maybe")]
112    pub external_free_storage: Annotated<u64>,
113
114    /// Indicator when the device was booted.
115    #[metastructure(pii = "maybe")]
116    pub boot_time: Annotated<String>,
117
118    /// Timezone of the device.
119    #[metastructure(pii = "maybe")]
120    pub timezone: Annotated<String>,
121
122    /// ISO 639-1 code of the locale the device is set to.
123    #[metastructure(pii = "maybe")]
124    pub locale: Annotated<String>,
125
126    /// Number of "logical processors".
127    ///
128    /// For example, 8.
129    pub processor_count: Annotated<u64>,
130
131    /// CPU description.
132    ///
133    /// For example, Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz.
134    #[metastructure(pii = "maybe")]
135    pub cpu_description: Annotated<String>,
136
137    /// Processor frequency in MHz.
138    ///
139    /// Note that the actual CPU frequency might vary depending on current load and
140    /// power conditions, especially on low-powered devices like phones and laptops.
141    pub processor_frequency: Annotated<u64>,
142
143    /// Kind of device the application is running on.
144    ///
145    /// For example, `Unknown`, `Handheld`, `Console`, `Desktop`.
146    #[metastructure(pii = "maybe")]
147    pub device_type: Annotated<String>,
148
149    /// Status of the device's battery.
150    ///
151    /// For example, `Unknown`, `Charging`, `Discharging`, `NotCharging`, `Full`.
152    #[metastructure(pii = "maybe")]
153    pub battery_status: Annotated<String>,
154
155    /// Unique device identifier.
156    #[metastructure(pii = "true")]
157    pub device_unique_identifier: Annotated<String>,
158
159    /// Whether vibration is available on the device.
160    pub supports_vibration: Annotated<bool>,
161
162    /// Whether the accelerometer is available on the device.
163    pub supports_accelerometer: Annotated<bool>,
164
165    /// Whether the gyroscope is available on the device.
166    pub supports_gyroscope: Annotated<bool>,
167
168    /// Whether audio is available on the device.
169    pub supports_audio: Annotated<bool>,
170
171    /// Whether location support is available on the device.
172    pub supports_location_service: Annotated<bool>,
173
174    /// UUID of the device.
175    #[metastructure(pii = "maybe")]
176    pub uuid: Annotated<Uuid>,
177
178    /// Additional arbitrary fields for forwards compatibility
179    #[metastructure(additional_properties, retain = true, pii = "maybe")]
180    pub other: Object<Value>,
181}
182
183impl super::DefaultContext for DeviceContext {
184    fn default_key() -> &'static str {
185        "device"
186    }
187
188    fn from_context(context: super::Context) -> Option<Self> {
189        match context {
190            super::Context::Device(c) => Some(*c),
191            _ => None,
192        }
193    }
194
195    fn cast(context: &super::Context) -> Option<&Self> {
196        match context {
197            super::Context::Device(c) => Some(c),
198            _ => None,
199        }
200    }
201
202    fn cast_mut(context: &mut super::Context) -> Option<&mut Self> {
203        match context {
204            super::Context::Device(c) => Some(c),
205            _ => None,
206        }
207    }
208
209    fn into_context(self) -> super::Context {
210        super::Context::Device(Box::new(self))
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use uuid::uuid;
217
218    use super::*;
219    use crate::protocol::Context;
220
221    #[test]
222    fn test_device_context_roundtrip() {
223        let json = r#"{
224  "name": "iphone",
225  "family": "iphone",
226  "model": "iphone7,3",
227  "model_id": "AH223",
228  "arch": "arm64",
229  "battery_level": 58.5,
230  "orientation": "landscape",
231  "manufacturer": "Apple",
232  "brand": "iphone",
233  "screen_resolution": "800x600",
234  "screen_width_pixels": 1920,
235  "screen_height_pixels": 1080,
236  "screen_density": 1.1,
237  "screen_dpi": 1,
238  "online": true,
239  "charging": false,
240  "low_memory": false,
241  "simulator": true,
242  "memory_size": 3137978368,
243  "free_memory": 322781184,
244  "usable_memory": 2843525120,
245  "storage_size": 63989469184,
246  "free_storage": 31994734592,
247  "external_storage_size": 2097152,
248  "external_free_storage": 2097152,
249  "boot_time": "2018-02-08T12:52:12Z",
250  "timezone": "Europe/Vienna",
251  "locale": "de-AT",
252  "processor_count": 8,
253  "cpu_description": "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz",
254  "processor_frequency": 2400,
255  "device_type": "Handheld",
256  "battery_status": "Charging",
257  "device_unique_identifier": "1234567",
258  "supports_vibration": true,
259  "supports_accelerometer": true,
260  "supports_gyroscope": true,
261  "supports_audio": true,
262  "supports_location_service": true,
263  "uuid": "abadcade-feed-dead-beef-baddadfeeded",
264  "other": "value",
265  "type": "device"
266}"#;
267        let context = Annotated::new(Context::Device(Box::new(DeviceContext {
268            name: Annotated::new("iphone".to_string()),
269            family: Annotated::new("iphone".to_string()),
270            model: Annotated::new("iphone7,3".to_string()),
271            model_id: Annotated::new("AH223".to_string()),
272            arch: Annotated::new("arm64".to_string()),
273            battery_level: Annotated::new(58.5),
274            orientation: Annotated::new("landscape".to_string()),
275            simulator: Annotated::new(true),
276            manufacturer: Annotated::new("Apple".to_string()),
277            brand: Annotated::new("iphone".to_string()),
278            screen_resolution: Annotated::new("800x600".to_string()),
279            screen_width_pixels: Annotated::new(1920),
280            screen_height_pixels: Annotated::new(1080),
281            screen_density: Annotated::new(1.1),
282            screen_dpi: Annotated::new(1),
283            online: Annotated::new(true),
284            charging: Annotated::new(false),
285            low_memory: Annotated::new(false),
286            memory_size: Annotated::new(3_137_978_368),
287            free_memory: Annotated::new(322_781_184),
288            usable_memory: Annotated::new(2_843_525_120),
289            storage_size: Annotated::new(63_989_469_184),
290            free_storage: Annotated::new(31_994_734_592),
291            external_storage_size: Annotated::new(2_097_152),
292            external_free_storage: Annotated::new(2_097_152),
293            boot_time: Annotated::new("2018-02-08T12:52:12Z".to_string()),
294            timezone: Annotated::new("Europe/Vienna".to_string()),
295            locale: Annotated::new("de-AT".to_string()),
296            processor_count: Annotated::new(8),
297            cpu_description: Annotated::new(
298                "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz".to_string(),
299            ),
300            processor_frequency: Annotated::new(2400),
301            device_type: Annotated::new("Handheld".to_string()),
302            battery_status: Annotated::new("Charging".to_string()),
303            device_unique_identifier: Annotated::new("1234567".to_string()),
304            supports_vibration: Annotated::new(true),
305            supports_accelerometer: Annotated::new(true),
306            supports_gyroscope: Annotated::new(true),
307            supports_audio: Annotated::new(true),
308            supports_location_service: Annotated::new(true),
309            uuid: Annotated::new(uuid!("abadcade-feed-dead-beef-baddadfeeded")),
310            other: {
311                let mut map = Object::new();
312                map.insert(
313                    "other".to_string(),
314                    Annotated::new(Value::String("value".to_string())),
315                );
316                map
317            },
318        })));
319
320        assert_eq!(context, Annotated::from_json(json).unwrap());
321        assert_eq!(json, context.to_json_pretty().unwrap());
322    }
323}