settings/inspect/
listener_logger.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
5use crate::base::SettingType;
6use fuchsia_inspect::{self as inspect, component, NumericProperty};
7use fuchsia_inspect_derive::Inspect;
8use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
9
10const LISTENER_INSPECT_NODE_NAME: &str = "active_listeners";
11
12pub struct ListenerInspectLogger {
13    /// The saved information about each setting type's active listeners.
14    listener_counts: ManagedInspectMap<ListenerInspectInfo>,
15}
16
17impl Default for ListenerInspectLogger {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23/// Information about active listeners to be written to inspect.
24///
25/// Inspect nodes are not used, but need to be held as they're deleted from inspect once they go
26/// out of scope.
27#[derive(Default, Inspect)]
28struct ListenerInspectInfo {
29    /// Node of this info.
30    inspect_node: inspect::Node,
31
32    /// Number of active listeners.
33    count: inspect::UintProperty,
34}
35
36impl ListenerInspectLogger {
37    /// Creates a new [ListenerInspectLogger] that writes to the default
38    /// [fuchsia_inspect::component::inspector()].
39    pub fn new() -> Self {
40        Self::with_inspector(component::inspector())
41    }
42
43    pub fn with_inspector(inspector: &inspect::Inspector) -> Self {
44        let listener_counts_node = inspector.root().create_child(LISTENER_INSPECT_NODE_NAME);
45        Self {
46            listener_counts: ManagedInspectMap::<ListenerInspectInfo>::with_node(
47                listener_counts_node,
48            ),
49        }
50    }
51
52    /// Adds a listener to the count for [setting_type].
53    pub fn add_listener(&mut self, setting_type: SettingType) {
54        let setting_type_str = format!("{setting_type:?}");
55        let inspect_info =
56            self.listener_counts.get_or_insert_with(setting_type_str, ListenerInspectInfo::default);
57        let _ = inspect_info.count.add(1u64);
58    }
59
60    /// Removes a listener from the count for [setting_type].
61    pub fn remove_listener(&mut self, setting_type: SettingType) {
62        let setting_type_str = format!("{setting_type:?}");
63        match self.listener_counts.map_mut().get_mut(&setting_type_str) {
64            Some(listener_inspect_info) => {
65                let _ = listener_inspect_info.count.subtract(1u64);
66            }
67            None => log::error!("Tried to subtract from nonexistent listener count"),
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use diagnostics_assertions::assert_data_tree;
76
77    #[fuchsia::test]
78    fn test_listener_logger() {
79        let inspector = inspect::Inspector::default();
80
81        let mut logger = ListenerInspectLogger::with_inspector(&inspector);
82
83        logger.add_listener(SettingType::Unknown);
84        logger.add_listener(SettingType::Unknown);
85        logger.add_listener(SettingType::Unknown);
86
87        logger.remove_listener(SettingType::Unknown);
88
89        // Since listeners were added thrice and removed once, the count at the end is 2.
90        assert_data_tree!(inspector, root: {
91            active_listeners: {
92                Unknown: {
93                    "count": 2u64,
94                }
95            }
96        });
97    }
98}