settings/agent/inspect/
setting_values.rs1use crate::clock;
6use fuchsia_async as fasync;
7use fuchsia_inspect::{self as inspect, Property, StringProperty, component};
8use fuchsia_inspect_derive::{Inspect, WithInspect};
9use futures::StreamExt;
10use futures::channel::mpsc::UnboundedReceiver;
11#[cfg(test)]
12use futures::channel::mpsc::UnboundedSender;
13use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
14
15const INSPECT_NODE_NAME: &str = "setting_values";
16const SETTING_TYPE_INSPECT_NODE_NAME: &str = "setting_types";
17
18#[derive(Debug, Default, Inspect)]
23struct SettingTypesInspectInfo {
24 inspect_node: inspect::Node,
25 value: inspect::StringProperty,
26}
27
28impl SettingTypesInspectInfo {
29 fn new(value: String, node: &inspect::Node, key: &str) -> Self {
30 let info = Self::default()
31 .with_inspect(node, key)
32 .expect("Failed to create SettingTypesInspectInfo node");
33 info.value.set(&value);
34 info
35 }
36}
37
38#[derive(Default, Inspect)]
43struct SettingValuesInspectInfo {
44 inspect_node: inspect::Node,
46
47 value: StringProperty,
49
50 timestamp: StringProperty,
52}
53
54pub struct AgentSetup {
55 agent: SettingValuesInspectAgent,
56 event_rx: UnboundedReceiver<(&'static str, String)>,
57}
58
59impl AgentSetup {
60 pub fn initialize(self, #[cfg(test)] mut channel_done_tx: Option<UnboundedSender<()>>) {
61 let AgentSetup { mut agent, mut event_rx } = self;
62 fasync::Task::local(async move {
63 while let Some((setting_name, setting_str)) = event_rx.next().await {
64 agent.write_raw_setting_to_inspect(setting_name, setting_str).await;
65 #[cfg(test)]
66 {
67 let _ = channel_done_tx.as_mut().map(|tx| tx.start_send(()));
68 }
69 }
70 })
71 .detach();
72 }
73}
74
75pub(crate) struct SettingValuesInspectAgent {
78 setting_values: ManagedInspectMap<SettingValuesInspectInfo>,
79 _setting_types_inspect_info: SettingTypesInspectInfo,
80}
81
82impl SettingValuesInspectAgent {
83 pub(crate) fn new(
84 settings: Vec<String>,
85 rx: UnboundedReceiver<(&'static str, String)>,
86 ) -> Option<AgentSetup> {
87 Self::create_with_node(
88 settings,
89 component::inspector().root().create_child(INSPECT_NODE_NAME),
90 rx,
91 None,
92 )
93 }
94
95 fn create_with_node(
100 mut settings: Vec<String>,
101 inspect_node: inspect::Node,
102 event_rx: UnboundedReceiver<(&'static str, String)>,
103 custom_inspector: Option<&inspect::Inspector>,
104 ) -> Option<AgentSetup> {
105 let inspector = custom_inspector.unwrap_or_else(|| component::inspector());
106
107 settings.sort();
109 let setting_types_value = format!("{settings:?}");
110 let setting_types_inspect_info = SettingTypesInspectInfo::new(
111 setting_types_value,
112 inspector.root(),
113 SETTING_TYPE_INSPECT_NODE_NAME,
114 );
115
116 let agent = Self {
117 setting_values: ManagedInspectMap::<SettingValuesInspectInfo>::with_node(inspect_node),
118 _setting_types_inspect_info: setting_types_inspect_info,
119 };
120
121 Some(AgentSetup { agent, event_rx })
122 }
123
124 async fn write_raw_setting_to_inspect(&mut self, setting_name: &'static str, value: String) {
126 let timestamp = clock::inspect_format_now();
127
128 let key_str = setting_name.to_string();
129 let setting_values = self.setting_values.get_mut(&key_str);
130
131 if let Some(setting_values_info) = setting_values {
132 setting_values_info.timestamp.set(×tamp);
134 setting_values_info.value.set(&value);
135 } else {
136 let inspect_info =
138 self.setting_values.get_or_insert_with(key_str, SettingValuesInspectInfo::default);
139 inspect_info.timestamp.set(×tamp);
140 inspect_info.value.set(&value);
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148 use diagnostics_assertions::assert_data_tree;
149 use futures::channel::mpsc;
150 use zx::MonotonicInstant;
151
152 #[fuchsia::test(allow_stalls = false)]
155 async fn test_write_inspect_on_changed() {
156 clock::mock::set(MonotonicInstant::from_nanos(0));
158
159 let inspector = inspect::Inspector::default();
160 let inspect_node = inspector.root().create_child(INSPECT_NODE_NAME);
161
162 let (mut tx, rx) = mpsc::unbounded();
163 let agent_setup = SettingValuesInspectAgent::create_with_node(
164 vec!["Unknown".to_string()],
165 inspect_node,
166 rx,
167 Some(&inspector),
168 )
169 .expect("agent missing inspect");
170
171 let (done_tx, mut done_rx) = mpsc::unbounded();
172 agent_setup.initialize(Some(done_tx));
173
174 assert_data_tree!(inspector, root: {
176 setting_types: {
177 "value": "[\"Unknown\"]",
178 },
179 setting_values: {
180 }
181 });
182
183 tx.start_send(("Unknown", "UnknownInfo(true)".to_string())).expect("sending via channel");
184 let () = done_rx.next().await.expect("should have processed event");
185
186 assert_data_tree!(inspector, root: {
188 setting_types: {
189 "value": "[\"Unknown\"]",
190 },
191 setting_values: {
192 "Unknown": {
193 value: "UnknownInfo(true)",
194 timestamp: "0.000000000",
195 }
196 }
197 });
198 }
199}