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 #[allow(clippy::new_ret_no_self)]
85 pub(crate) fn new(
86 settings: Vec<String>,
87 rx: UnboundedReceiver<(&'static str, String)>,
88 ) -> Option<AgentSetup> {
89 Self::create_with_node(
90 settings,
91 component::inspector().root().create_child(INSPECT_NODE_NAME),
92 rx,
93 None,
94 )
95 }
96
97 fn create_with_node(
102 mut settings: Vec<String>,
103 inspect_node: inspect::Node,
104 event_rx: UnboundedReceiver<(&'static str, String)>,
105 custom_inspector: Option<&inspect::Inspector>,
106 ) -> Option<AgentSetup> {
107 let inspector = custom_inspector.unwrap_or_else(|| component::inspector());
108
109 settings.sort();
111 let setting_types_value = format!("{settings:?}");
112 let setting_types_inspect_info = SettingTypesInspectInfo::new(
113 setting_types_value,
114 inspector.root(),
115 SETTING_TYPE_INSPECT_NODE_NAME,
116 );
117
118 let agent = Self {
119 setting_values: ManagedInspectMap::<SettingValuesInspectInfo>::with_node(inspect_node),
120 _setting_types_inspect_info: setting_types_inspect_info,
121 };
122
123 Some(AgentSetup { agent, event_rx })
124 }
125
126 async fn write_raw_setting_to_inspect(&mut self, setting_name: &'static str, value: String) {
128 let timestamp = clock::inspect_format_now();
129
130 let key_str = setting_name.to_string();
131 let setting_values = self.setting_values.get_mut(&key_str);
132
133 if let Some(setting_values_info) = setting_values {
134 setting_values_info.timestamp.set(×tamp);
136 setting_values_info.value.set(&value);
137 } else {
138 let inspect_info =
140 self.setting_values.get_or_insert_with(key_str, SettingValuesInspectInfo::default);
141 inspect_info.timestamp.set(×tamp);
142 inspect_info.value.set(&value);
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use diagnostics_assertions::assert_data_tree;
151 use futures::channel::mpsc;
152 use zx::MonotonicInstant;
153
154 #[fuchsia::test(allow_stalls = false)]
157 async fn test_write_inspect_on_changed() {
158 clock::mock::set(MonotonicInstant::from_nanos(0));
160
161 let inspector = inspect::Inspector::default();
162 let inspect_node = inspector.root().create_child(INSPECT_NODE_NAME);
163
164 let (mut tx, rx) = mpsc::unbounded();
165 let agent_setup = SettingValuesInspectAgent::create_with_node(
166 vec!["Unknown".to_string()],
167 inspect_node,
168 rx,
169 Some(&inspector),
170 )
171 .expect("agent missing inspect");
172
173 let (done_tx, mut done_rx) = mpsc::unbounded();
174 agent_setup.initialize(Some(done_tx));
175
176 assert_data_tree!(inspector, root: {
178 setting_types: {
179 "value": "[\"Unknown\"]",
180 },
181 setting_values: {
182 }
183 });
184
185 tx.start_send(("Unknown", "UnknownInfo(true)".to_string())).expect("sending via channel");
186 let () = done_rx.next().await.expect("should have processed event");
187
188 assert_data_tree!(inspector, root: {
190 setting_types: {
191 "value": "[\"Unknown\"]",
192 },
193 setting_values: {
194 "Unknown": {
195 value: "UnknownInfo(true)",
196 timestamp: "0.000000000",
197 }
198 }
199 });
200 }
201}