fuchsia_inspect/writer/types/
string_array.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::writer::private::InspectTypeInternal;
6use crate::writer::{ArrayProperty, Inner, InnerValueType, InspectType, State};
7use inspect_format::BlockIndex;
8use std::borrow::Cow;
9
10#[derive(Debug, PartialEq, Eq, Default)]
11pub struct StringArrayProperty {
12    inner: Inner<InnerValueType>,
13}
14
15impl InspectType for StringArrayProperty {}
16
17impl InspectTypeInternal for StringArrayProperty {
18    fn new(state: State, block_index: BlockIndex) -> Self {
19        Self { inner: Inner::new(state, block_index) }
20    }
21
22    fn is_valid(&self) -> bool {
23        self.inner.is_valid()
24    }
25
26    fn new_no_op() -> Self {
27        Self { inner: Inner::None }
28    }
29
30    fn state(&self) -> Option<State> {
31        Some(self.inner.inner_ref()?.state.clone())
32    }
33
34    fn block_index(&self) -> Option<BlockIndex> {
35        Some(self.inner.inner_ref()?.block_index)
36    }
37
38    fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, f: F) -> R {
39        match self.inner.inner_ref() {
40            None => {
41                // If the node was a no-op we still execute the `update_fn` even if all operations
42                // inside it will be no-ops to return `R`.
43                f(self)
44            }
45            Some(inner_ref) => {
46                // Silently ignore the error when fail to lock (as in any regular operation).
47                // All operations performed in the `update_fn` won't update the vmo
48                // generation count since we'll be holding one lock here.
49                inner_ref.state.begin_transaction();
50                let result = f(self);
51                inner_ref.state.end_transaction();
52                result
53            }
54        }
55    }
56}
57
58impl ArrayProperty for StringArrayProperty {
59    type Type<'a> = Cow<'a, str>;
60
61    fn set<'a>(&self, index: usize, value: impl Into<Self::Type<'a>>) {
62        if let Some(ref inner_ref) = self.inner.inner_ref() {
63            inner_ref
64                .state
65                .try_lock()
66                .and_then(|mut state| {
67                    state.set_array_string_slot(inner_ref.block_index, index, value.into())
68                })
69                .ok();
70        }
71    }
72
73    fn clear(&self) {
74        if let Some(ref inner_ref) = self.inner.inner_ref() {
75            inner_ref
76                .state
77                .try_lock()
78                .and_then(|mut state| state.clear_array(inner_ref.block_index, 0))
79                .ok();
80        }
81    }
82}
83
84impl Drop for StringArrayProperty {
85    fn drop(&mut self) {
86        self.clear();
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::writer::testing_utils::GetBlockExt;
94    use crate::writer::Length;
95    use crate::{assert_update_is_atomic, Inspector};
96    use diagnostics_assertions::assert_json_diff;
97
98    impl StringArrayProperty {
99        pub fn load_string_slot(&self, slot: usize) -> Option<String> {
100            self.inner.inner_ref().and_then(|inner_ref| {
101                inner_ref.state.try_lock().ok().and_then(|state| {
102                    let index =
103                        state.get_block(self.block_index().unwrap()).get_string_index_at(slot)?;
104                    state.load_string(index).ok()
105                })
106            })
107        }
108    }
109
110    #[fuchsia::test]
111    fn string_array_property() {
112        let inspector = Inspector::default();
113        let root = inspector.root();
114        let node = root.create_child("node");
115
116        {
117            let array = node.create_string_array("string_array", 5);
118            assert_eq!(array.len().unwrap(), 5);
119            node.get_block::<_, inspect_format::Node>(|node_block| {
120                assert_eq!(node_block.child_count(), 1);
121            });
122
123            array.set(0, "0");
124            array.set(1, "1");
125            array.set(2, "2");
126            array.set(3, "3");
127            array.set(4, "4");
128
129            // this should fail silently
130            array.set(5, "5");
131            assert!(array.load_string_slot(5).is_none());
132
133            let expected: Vec<String> =
134                vec!["0".into(), "1".into(), "2".into(), "3".into(), "4".into()];
135
136            assert_json_diff!(inspector, root: {
137                node: {
138                    string_array: expected,
139                },
140            });
141
142            array.clear();
143
144            let expected: Vec<String> = vec![String::new(); 5];
145
146            assert_json_diff!(inspector, root: {
147                node: {
148                    string_array: expected,
149                },
150            });
151
152            assert!(array.load_string_slot(5).is_none());
153        }
154
155        node.get_block::<_, inspect_format::Node>(|node_block| {
156            assert_eq!(node_block.child_count(), 0);
157        });
158    }
159
160    #[fuchsia::test]
161    fn property_atomics() {
162        let inspector = Inspector::default();
163        let array = inspector.root().create_string_array("string_array", 5);
164
165        assert_update_is_atomic!(array, |array| {
166            array.set(0, "0");
167            array.set(1, "1");
168            array.set(2, "2");
169            array.set(3, "3");
170            array.set(4, "4");
171        });
172    }
173}