gardesk/garwarp / 6b364de

Browse files

add portal option parsing

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
6b364de8ba34e423a9f7a34ff4a8ec5035df15e9
Parents
47ba17e
Tree
844f0a0

3 changed files

StatusFile+-
M garwarp/src/dbus.rs 21 0
M garwarp/src/main.rs 1 0
A garwarp/src/portal_options.rs 185 0
garwarp/src/dbus.rsmodified
@@ -9,6 +9,9 @@ use zbus::{
99
 
1010
 use crate::error::{PortalError, map_portal_error};
1111
 use crate::portal::derive_request_id_from_handle;
12
+use crate::portal_options::{
13
+    parse_app_chooser_options, parse_file_chooser_options, parse_screenshot_options,
14
+};
1215
 
1316
 pub const BACKEND_DBUS_NAME: &str = "org.freedesktop.impl.portal.desktop.garwarp";
1417
 pub const BACKEND_OBJECT_PATH: &str = "/org/freedesktop/portal/desktop";
@@ -69,6 +72,9 @@ impl ScreenshotPortal {
6972
         _options: HashMap<String, OwnedValue>,
7073
         #[zbus(header)] header: Header<'_>,
7174
     ) -> PortalMethodReply {
75
+        if let Err(error) = parse_screenshot_options(&_options) {
76
+            return failure_response(&error);
77
+        }
7278
         if let Err(error) = request_id_for_call(&handle, header) {
7379
             return failure_response(&error);
7480
         }
@@ -83,6 +89,9 @@ impl ScreenshotPortal {
8389
         _options: HashMap<String, OwnedValue>,
8490
         #[zbus(header)] header: Header<'_>,
8591
     ) -> PortalMethodReply {
92
+        if let Err(error) = parse_screenshot_options(&_options) {
93
+            return failure_response(&error);
94
+        }
8695
         if let Err(error) = request_id_for_call(&handle, header) {
8796
             return failure_response(&error);
8897
         }
@@ -109,6 +118,9 @@ impl FileChooserPortal {
109118
         _options: HashMap<String, OwnedValue>,
110119
         #[zbus(header)] header: Header<'_>,
111120
     ) -> PortalMethodReply {
121
+        if let Err(error) = parse_file_chooser_options(&_options) {
122
+            return failure_response(&error);
123
+        }
112124
         if let Err(error) = request_id_for_call(&handle, header) {
113125
             return failure_response(&error);
114126
         }
@@ -124,6 +136,9 @@ impl FileChooserPortal {
124136
         _options: HashMap<String, OwnedValue>,
125137
         #[zbus(header)] header: Header<'_>,
126138
     ) -> PortalMethodReply {
139
+        if let Err(error) = parse_file_chooser_options(&_options) {
140
+            return failure_response(&error);
141
+        }
127142
         if let Err(error) = request_id_for_call(&handle, header) {
128143
             return failure_response(&error);
129144
         }
@@ -139,6 +154,9 @@ impl FileChooserPortal {
139154
         _options: HashMap<String, OwnedValue>,
140155
         #[zbus(header)] header: Header<'_>,
141156
     ) -> PortalMethodReply {
157
+        if let Err(error) = parse_file_chooser_options(&_options) {
158
+            return failure_response(&error);
159
+        }
142160
         if let Err(error) = request_id_for_call(&handle, header) {
143161
             return failure_response(&error);
144162
         }
@@ -165,6 +183,9 @@ impl AppChooserPortal {
165183
         _options: HashMap<String, OwnedValue>,
166184
         #[zbus(header)] header: Header<'_>,
167185
     ) -> PortalMethodReply {
186
+        if let Err(error) = parse_app_chooser_options(&_options) {
187
+            return failure_response(&error);
188
+        }
168189
         if let Err(error) = request_id_for_call(&handle, header) {
169190
             return failure_response(&error);
170191
         }
garwarp/src/main.rsmodified
@@ -5,6 +5,7 @@ mod error;
55
 mod lock;
66
 mod logging;
77
 mod portal;
8
+mod portal_options;
89
 mod request;
910
 mod request_store;
1011
 mod runtime;
garwarp/src/portal_options.rsadded
@@ -0,0 +1,185 @@
1
+#![allow(dead_code)]
2
+
3
+use std::collections::HashMap;
4
+
5
+use zbus::zvariant::OwnedValue;
6
+
7
+use crate::error::PortalError;
8
+
9
+pub type PortalOptions = HashMap<String, OwnedValue>;
10
+
11
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
12
+pub struct ScreenshotOptions {
13
+    pub modal: Option<bool>,
14
+    pub interactive: Option<bool>,
15
+    pub permission_store_checked: Option<bool>,
16
+}
17
+
18
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
19
+pub struct FileChooserOptions {
20
+    pub accept_label: Option<String>,
21
+    pub modal: Option<bool>,
22
+    pub multiple: Option<bool>,
23
+    pub directory: Option<bool>,
24
+    pub current_name: Option<String>,
25
+}
26
+
27
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
28
+pub struct AppChooserOptions {
29
+    pub last_choice: Option<String>,
30
+    pub modal: Option<bool>,
31
+    pub content_type: Option<String>,
32
+    pub uri: Option<String>,
33
+    pub filename: Option<String>,
34
+    pub activation_token: Option<String>,
35
+}
36
+
37
+pub fn parse_screenshot_options(options: &PortalOptions) -> Result<ScreenshotOptions, PortalError> {
38
+    Ok(ScreenshotOptions {
39
+        modal: bool_option(options, "modal")?,
40
+        interactive: bool_option(options, "interactive")?,
41
+        permission_store_checked: bool_option(options, "permission_store_checked")?,
42
+    })
43
+}
44
+
45
+pub fn parse_file_chooser_options(
46
+    options: &PortalOptions,
47
+) -> Result<FileChooserOptions, PortalError> {
48
+    Ok(FileChooserOptions {
49
+        accept_label: string_option(options, "accept_label")?,
50
+        modal: bool_option(options, "modal")?,
51
+        multiple: bool_option(options, "multiple")?,
52
+        directory: bool_option(options, "directory")?,
53
+        current_name: string_option(options, "current_name")?,
54
+    })
55
+}
56
+
57
+pub fn parse_app_chooser_options(
58
+    options: &PortalOptions,
59
+) -> Result<AppChooserOptions, PortalError> {
60
+    Ok(AppChooserOptions {
61
+        last_choice: string_option(options, "last_choice")?,
62
+        modal: bool_option(options, "modal")?,
63
+        content_type: string_option(options, "content_type")?,
64
+        uri: string_option(options, "uri")?,
65
+        filename: string_option(options, "filename")?,
66
+        activation_token: string_option(options, "activation_token")?,
67
+    })
68
+}
69
+
70
+fn bool_option(options: &PortalOptions, key: &str) -> Result<Option<bool>, PortalError> {
71
+    options
72
+        .get(key)
73
+        .map(|value| bool::try_from(value).map_err(|_| PortalError::InvalidRequestPayload))
74
+        .transpose()
75
+}
76
+
77
+fn string_option(options: &PortalOptions, key: &str) -> Result<Option<String>, PortalError> {
78
+    options
79
+        .get(key)
80
+        .map(|value| {
81
+            <&str>::try_from(value)
82
+                .map(|value| value.to_string())
83
+                .map_err(|_| PortalError::InvalidRequestPayload)
84
+        })
85
+        .transpose()
86
+}
87
+
88
+#[cfg(test)]
89
+mod tests {
90
+    use super::{
91
+        AppChooserOptions, FileChooserOptions, PortalOptions, ScreenshotOptions,
92
+        parse_app_chooser_options, parse_file_chooser_options, parse_screenshot_options,
93
+    };
94
+    use crate::error::PortalError;
95
+    use zbus::zvariant::{OwnedValue, Str};
96
+
97
+    #[test]
98
+    fn parse_screenshot_options_accepts_supported_keys() {
99
+        let mut options = PortalOptions::new();
100
+        options.insert("modal".to_string(), OwnedValue::from(true));
101
+        options.insert("interactive".to_string(), OwnedValue::from(false));
102
+        options.insert(
103
+            "permission_store_checked".to_string(),
104
+            OwnedValue::from(true),
105
+        );
106
+
107
+        let parsed = parse_screenshot_options(&options).expect("screenshot options");
108
+        assert_eq!(
109
+            parsed,
110
+            ScreenshotOptions {
111
+                modal: Some(true),
112
+                interactive: Some(false),
113
+                permission_store_checked: Some(true),
114
+            }
115
+        );
116
+    }
117
+
118
+    #[test]
119
+    fn parse_screenshot_options_rejects_wrong_type() {
120
+        let mut options = PortalOptions::new();
121
+        options.insert("modal".to_string(), str_value("yes"));
122
+
123
+        let parsed = parse_screenshot_options(&options);
124
+        assert_eq!(parsed, Err(PortalError::InvalidRequestPayload));
125
+    }
126
+
127
+    #[test]
128
+    fn parse_file_chooser_options_accepts_supported_keys() {
129
+        let mut options = PortalOptions::new();
130
+        options.insert("accept_label".to_string(), str_value("Open"));
131
+        options.insert("modal".to_string(), OwnedValue::from(true));
132
+        options.insert("multiple".to_string(), OwnedValue::from(false));
133
+        options.insert("directory".to_string(), OwnedValue::from(false));
134
+        options.insert("current_name".to_string(), str_value("image.png"));
135
+
136
+        let parsed = parse_file_chooser_options(&options).expect("file chooser options");
137
+        assert_eq!(
138
+            parsed,
139
+            FileChooserOptions {
140
+                accept_label: Some("Open".to_string()),
141
+                modal: Some(true),
142
+                multiple: Some(false),
143
+                directory: Some(false),
144
+                current_name: Some("image.png".to_string()),
145
+            }
146
+        );
147
+    }
148
+
149
+    #[test]
150
+    fn parse_app_chooser_options_accepts_supported_keys() {
151
+        let mut options = PortalOptions::new();
152
+        options.insert("last_choice".to_string(), str_value("org.test.Viewer"));
153
+        options.insert("modal".to_string(), OwnedValue::from(true));
154
+        options.insert("content_type".to_string(), str_value("image/png"));
155
+        options.insert("uri".to_string(), str_value("file:///tmp/a.png"));
156
+        options.insert("filename".to_string(), str_value("a.png"));
157
+        options.insert("activation_token".to_string(), str_value("token-1"));
158
+
159
+        let parsed = parse_app_chooser_options(&options).expect("app chooser options");
160
+        assert_eq!(
161
+            parsed,
162
+            AppChooserOptions {
163
+                last_choice: Some("org.test.Viewer".to_string()),
164
+                modal: Some(true),
165
+                content_type: Some("image/png".to_string()),
166
+                uri: Some("file:///tmp/a.png".to_string()),
167
+                filename: Some("a.png".to_string()),
168
+                activation_token: Some("token-1".to_string()),
169
+            }
170
+        );
171
+    }
172
+
173
+    #[test]
174
+    fn parse_app_chooser_options_rejects_wrong_type() {
175
+        let mut options = PortalOptions::new();
176
+        options.insert("activation_token".to_string(), OwnedValue::from(99_u32));
177
+
178
+        let parsed = parse_app_chooser_options(&options);
179
+        assert_eq!(parsed, Err(PortalError::InvalidRequestPayload));
180
+    }
181
+
182
+    fn str_value(value: &str) -> OwnedValue {
183
+        OwnedValue::from(Str::from(value))
184
+    }
185
+}