settings/keyboard/
keyboard_fidl_handler.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::base::{SettingInfo, SettingType};
6use crate::handler::base::Request;
7use crate::ingress::{request, watch, Scoped};
8use crate::job::source::{Error as JobError, ErrorResponder};
9use crate::job::Job;
10use crate::keyboard::types::{Autorepeat, KeyboardInfo, KeymapId};
11use fidl::prelude::*;
12use fidl_fuchsia_settings::{
13    KeyboardRequest, KeyboardSetResponder, KeyboardSetSetResult, KeyboardSettings,
14    KeyboardWatchResponder,
15};
16
17impl ErrorResponder for KeyboardSetResponder {
18    fn id(&self) -> &'static str {
19        "Keyboard_Set"
20    }
21
22    fn respond(self: Box<Self>, error: fidl_fuchsia_settings::Error) -> Result<(), fidl::Error> {
23        self.send(Err(error))
24    }
25}
26
27impl request::Responder<Scoped<KeyboardSetSetResult>> for KeyboardSetResponder {
28    fn respond(self, Scoped(response): Scoped<KeyboardSetSetResult>) {
29        let _ = self.send(response);
30    }
31}
32
33impl watch::Responder<KeyboardSettings, zx::Status> for KeyboardWatchResponder {
34    fn respond(self, response: Result<KeyboardSettings, zx::Status>) {
35        match response {
36            Ok(settings) => {
37                let _ = self.send(&settings);
38            }
39            Err(error) => {
40                self.control_handle().shutdown_with_epitaph(error);
41            }
42        }
43    }
44}
45
46impl TryFrom<KeyboardRequest> for Job {
47    type Error = JobError;
48
49    fn try_from(item: KeyboardRequest) -> Result<Self, Self::Error> {
50        #[allow(unreachable_patterns)]
51        match item {
52            KeyboardRequest::Set { settings, responder } => match to_request(settings) {
53                Ok(request) => {
54                    Ok(request::Work::new(SettingType::Keyboard, request, responder).into())
55                }
56                Err(e) => {
57                    log::error!(
58                        "Transferring from KeyboardSettings to a Set request has an error: {:?}",
59                        e
60                    );
61                    Err(JobError::InvalidInput(Box::new(responder)))
62                }
63            },
64            KeyboardRequest::Watch { responder } => {
65                Ok(watch::Work::new_job(SettingType::Keyboard, responder))
66            }
67            _ => {
68                log::warn!("Received a call to an unsupported API: {:?}", item);
69                Err(JobError::Unsupported)
70            }
71        }
72    }
73}
74
75impl From<SettingInfo> for KeyboardSettings {
76    fn from(response: SettingInfo) -> Self {
77        if let SettingInfo::Keyboard(info) = response {
78            return KeyboardSettings {
79                keymap: info.keymap.map(KeymapId::into),
80                autorepeat: info.autorepeat.map(Autorepeat::into),
81                ..Default::default()
82            };
83        }
84
85        panic!("incorrect value sent to keyboard");
86    }
87}
88
89fn to_request(settings: KeyboardSettings) -> Result<Request, String> {
90    let autorepeat: Option<Autorepeat> = settings.autorepeat.map(|src| src.into());
91    let keymap = settings.keymap.map(KeymapId::try_from).transpose()?;
92    Ok(Request::SetKeyboardInfo(KeyboardInfo { keymap, autorepeat }))
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::job::{execution, work};
99    use assert_matches::assert_matches;
100    use fidl_fuchsia_settings::{KeyboardMarker, KeyboardRequestStream};
101    use futures::StreamExt;
102
103    #[fuchsia::test]
104    fn test_request_from_settings_empty() {
105        let request = to_request(KeyboardSettings::default()).unwrap();
106
107        assert_eq!(
108            request,
109            Request::SetKeyboardInfo(KeyboardInfo { keymap: None, autorepeat: None })
110        );
111    }
112
113    #[fuchsia::test]
114    fn test_request_from_settings_error() {
115        let keyboard_settings = KeyboardSettings {
116            keymap: Some(fidl_fuchsia_input::KeymapId::unknown()),
117            ..Default::default()
118        };
119
120        assert!(format!("{:?}", to_request(keyboard_settings).unwrap_err())
121            .contains("Received an invalid keymap id:"));
122    }
123
124    #[fuchsia::test]
125    fn test_request_from_settings() {
126        use crate::keyboard::types::Autorepeat;
127
128        const KEYMAP_ID: fidl_fuchsia_input::KeymapId = fidl_fuchsia_input::KeymapId::FrAzerty;
129        const DELAY: i64 = 1;
130        const PERIOD: i64 = 2;
131        const AUTOREPEAT: fidl_fuchsia_settings::Autorepeat =
132            fidl_fuchsia_settings::Autorepeat { delay: DELAY, period: PERIOD };
133
134        let keyboard_settings = KeyboardSettings {
135            keymap: Some(KEYMAP_ID),
136            autorepeat: Some(AUTOREPEAT),
137            ..Default::default()
138        };
139
140        let request = to_request(keyboard_settings).unwrap();
141
142        assert_eq!(
143            request,
144            Request::SetKeyboardInfo(KeyboardInfo {
145                keymap: Some(KeymapId::FrAzerty),
146                autorepeat: Some(Autorepeat { delay: DELAY, period: PERIOD }),
147            })
148        );
149    }
150
151    #[fuchsia::test(allow_stalls = false)]
152    async fn try_from_set_converts_supplied_params() {
153        let (proxy, server) = fidl::endpoints::create_proxy::<KeyboardMarker>();
154        let _fut = proxy.set(&KeyboardSettings {
155            keymap: Some(fidl_fuchsia_input::KeymapId::FrAzerty),
156            ..Default::default()
157        });
158        let mut request_stream: KeyboardRequestStream = server.into_stream();
159        let request = request_stream
160            .next()
161            .await
162            .expect("should have on request before stream is closed")
163            .expect("should have gotten a request");
164        let job = Job::try_from(request);
165        let job = job.as_ref();
166        assert_matches!(job.map(|j| j.workload()), Ok(work::Load::Independent(_)));
167        assert_matches!(job.map(|j| j.execution_type()), Ok(execution::Type::Independent));
168    }
169
170    #[fuchsia::test(allow_stalls = false)]
171    async fn try_from_watch_converts_supplied_params() {
172        let (proxy, server) = fidl::endpoints::create_proxy::<KeyboardMarker>();
173        let _fut = proxy.watch();
174        let mut request_stream: KeyboardRequestStream = server.into_stream();
175        let request = request_stream
176            .next()
177            .await
178            .expect("should have on request before stream is closed")
179            .expect("should have gotten a request");
180        let job = Job::try_from(request);
181        let job = job.as_ref();
182        assert_matches!(job.map(|j| j.workload()), Ok(work::Load::Sequential(_, _)));
183        assert_matches!(job.map(|j| j.execution_type()), Ok(execution::Type::Sequential(_)));
184    }
185}