fidl_fuchsia_ui_focus_ext/
lib.rs

1// Copyright 2022 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
5//! Extensions for `fidl_fuchsia_ui_focus`.
6
7use fidl_fuchsia_ui_focus::{FocusChain, FocusKoidChain};
8use fidl_fuchsia_ui_views_ext::ViewRefExt;
9use fuchsia_scenic as scenic;
10use zx::{Koid, Status};
11
12/// Extension trait for [`fidl_fuchsia_ui_focus::FocusChain`] and
13/// [`fidl_fuchsia_ui_focus::FocusKoidChain`].
14pub trait FocusChainExt
15where
16    Self: Sized,
17{
18    /// Returns the number of views in the chain.
19    fn len(&self) -> usize;
20
21    /// Returns true if there are no views in the chain.
22    fn is_empty(&self) -> bool {
23        self.len() == 0
24    }
25
26    /// Creates a new chain, in which all of the `ViewRef`s (or koids) have been duplicated from the
27    /// original. Returns an error if duplicating any of the `ViewRef`s fails.
28    fn duplicate(&self) -> Result<Self, Status>;
29
30    /// Returns a fallible iterator over the chain's koids. If any koid cannot be retrieved, the
31    /// iterator yields an error in its place.
32    fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_>;
33
34    /// Returns true if the two chains' `ViewRef`s correspond to the same koids, in the same
35    /// order. If any of the koids cannot be retrieved, returns an error.
36    fn equivalent<O: FocusChainExt>(&self, other: &O) -> Result<bool, Status> {
37        let self_len = self.len();
38        if self_len != other.len() {
39            return Ok(false);
40        }
41        if self_len == 0 {
42            return Ok(true);
43        }
44
45        let mut zipped = std::iter::zip(self.koids(), other.koids());
46        while let Some((a, b)) = zipped.next() {
47            if a? != b? {
48                return Ok(false);
49            }
50        }
51        Ok(true)
52    }
53
54    /// Converts this chain into a [`FocusKoidChain`], which contains just koids instead of
55    /// [`ViewRef`]s.
56    fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status>;
57}
58
59impl FocusChainExt for FocusChain {
60    fn len(&self) -> usize {
61        match self.focus_chain.as_ref() {
62            None => 0,
63            Some(v) => v.len(),
64        }
65    }
66
67    fn duplicate(&self) -> Result<Self, Status> {
68        let v = match self.focus_chain.as_ref() {
69            None => None,
70            Some(v) => Some(
71                v.iter().map(|vr| scenic::duplicate_view_ref(vr)).collect::<Result<Vec<_>, _>>()?,
72            ),
73        };
74        let output = FocusChain { focus_chain: v, ..Default::default() };
75        Ok(output)
76    }
77
78    fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_> {
79        match &self.focus_chain {
80            None => Box::new(std::iter::empty()),
81            Some(v) => Box::new(v.iter().map(|vr| vr.get_koid())),
82        }
83    }
84
85    fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status> {
86        let raw_koids = match self.focus_chain.as_ref() {
87            None => None,
88            Some(_) => Some(
89                self.koids()
90                    .map(|result| -> Result<u64, Status> { result.map(|koid| koid.raw_koid()) })
91                    .collect::<Result<Vec<_>, _>>()?,
92            ),
93        };
94        Ok(FocusKoidChain { focus_chain: raw_koids, ..Default::default() })
95    }
96}
97
98impl FocusChainExt for FocusKoidChain {
99    fn len(&self) -> usize {
100        match self.focus_chain.as_ref() {
101            None => 0,
102            Some(v) => v.len(),
103        }
104    }
105
106    /// Clones this `FocusKoidChain`.
107    fn duplicate(&self) -> Result<Self, Status> {
108        Ok(self.clone())
109    }
110
111    fn koids(&self) -> Box<dyn ExactSizeIterator<Item = Result<Koid, Status>> + '_> {
112        match &self.focus_chain {
113            None => Box::new(std::iter::empty()),
114            Some(v) => Box::new(v.iter().map(|raw| Ok(Koid::from_raw(*raw)))),
115        }
116    }
117
118    /// Clones this `FocusKoidChain`.
119    fn to_focus_koid_chain(&self) -> Result<FocusKoidChain, Status> {
120        self.duplicate()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use fidl_fuchsia_ui_focus_test_helpers::make_focus_chain;
128
129    #[test]
130    fn focus_chain_duplicate() {
131        let (source, _control_refs) = make_focus_chain(2);
132        let target = source.duplicate().expect("error in duplicate()");
133
134        let source_koids = source.koids().collect::<Result<Vec<_>, _>>().unwrap();
135        let target_koids = target.koids().collect::<Result<Vec<_>, _>>().unwrap();
136
137        assert_eq!(source_koids, target_koids,);
138    }
139
140    #[test]
141    fn focus_chain_duplicate_empty() {
142        let source = FocusChain::default();
143        let target = source.duplicate().expect("error in duplicate()");
144
145        assert_eq!(target.focus_chain, None);
146    }
147
148    #[test]
149    fn focus_chain_equivalent_empty() {
150        let a = FocusChain::default();
151        let b = FocusChain::default();
152        assert!(a.equivalent(&b).unwrap());
153
154        let (a, _) = make_focus_chain(0);
155        let (b, _) = make_focus_chain(0);
156        assert!(a.equivalent(&b).unwrap());
157    }
158
159    #[test]
160    fn focus_chain_equivalent_same_lengths() {
161        let (a, _a) = make_focus_chain(3);
162        let (b, _b) = make_focus_chain(3);
163        assert!(!a.equivalent(&b).unwrap());
164    }
165
166    #[test]
167    fn focus_chain_equivalent_different_lengths() {
168        let (a, _a) = make_focus_chain(3);
169        let (b, _b) = make_focus_chain(5);
170        assert!(!a.equivalent(&b).unwrap());
171    }
172
173    #[test]
174    fn focus_chain_equivalent_duplicates() {
175        let (a, _a) = make_focus_chain(3);
176        let b = a.duplicate().expect("duplicate");
177        assert!(a.equivalent(&b).unwrap());
178    }
179
180    #[test]
181    fn focus_chain_to_focus_koid_chain() {
182        let (focus_chain, _view_control_refs) = make_focus_chain(2);
183        let raw_koids = vec![
184            focus_chain.focus_chain.as_ref().unwrap()[0].get_koid().unwrap().raw_koid(),
185            focus_chain.focus_chain.as_ref().unwrap()[1].get_koid().unwrap().raw_koid(),
186        ];
187
188        let expected = FocusKoidChain { focus_chain: Some(raw_koids), ..Default::default() };
189
190        let actual = focus_chain.to_focus_koid_chain().unwrap();
191
192        assert_eq!(expected, actual);
193    }
194
195    #[test]
196    fn focus_chain_equivalent_focus_koid_chain() {
197        let (chain_a, _vcr_a) = make_focus_chain(2);
198        let (chain_b, _vcr_b) = make_focus_chain(2);
199
200        let koid_chain_a = chain_a.to_focus_koid_chain().unwrap();
201        let koid_chain_b = chain_b.to_focus_koid_chain().unwrap();
202
203        assert!(chain_a.equivalent(&koid_chain_a).unwrap());
204        assert!(!chain_a.equivalent(&koid_chain_b).unwrap())
205    }
206}