Skip to main content

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