inspect_fidl_load/
lib.rs

1// Copyright 2019 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 anyhow::{Error, format_err};
6use fidl_fuchsia_inspect_deprecated::{InspectProxy, MetricValue, PropertyValue};
7use fuchsia_async as fasync;
8use fuchsia_inspect::reader::{DiagnosticsHierarchy, Property};
9
10/// Loads an inspect node hierarchy from the given root inspect node.
11pub async fn load_hierarchy(proxy: InspectProxy) -> Result<DiagnosticsHierarchy, Error> {
12    let mut pending_nodes = vec![];
13    let root = read_node(proxy).await?;
14    pending_nodes.push(root);
15    while let Some(mut current_node) = pending_nodes.pop() {
16        if current_node.pending_children.is_empty() {
17            if pending_nodes.is_empty() {
18                return Ok(current_node.hierarchy);
19            }
20            match pending_nodes.pop() {
21                Some(mut parent) => {
22                    parent.hierarchy.children.push(current_node.hierarchy);
23                    pending_nodes.push(parent);
24                }
25                None => return Err(format_err!("failed to load hierarchy")),
26            }
27        } else {
28            let next_child = current_node.pending_children.pop().unwrap();
29            let (client, server) = zx::Channel::create();
30            current_node
31                .proxy
32                .open_child(&next_child, fidl::endpoints::ServerEnd::new(server))
33                .await?;
34            let child_proxy = InspectProxy::new(fasync::Channel::from_channel(client));
35            let child_node = read_node(child_proxy).await?;
36            pending_nodes.push(current_node);
37            pending_nodes.push(child_node);
38        }
39    }
40    Err(format_err!("failed to load hierarchy"))
41}
42
43struct PartialNodeHierarchy {
44    proxy: InspectProxy,
45    hierarchy: DiagnosticsHierarchy,
46    pending_children: Vec<String>,
47}
48
49/// Loads an inspect hierarchy from a deprecated FIDL service.
50async fn read_node(proxy: InspectProxy) -> Result<PartialNodeHierarchy, Error> {
51    let object = proxy.read_data().await?;
52    let mut properties = object
53        .properties
54        .into_iter()
55        .map(|property| match property.value {
56            PropertyValue::Str(v) => Property::String(property.key, v),
57            PropertyValue::Bytes(v) => Property::Bytes(property.key, v),
58        })
59        .collect::<Vec<Property>>();
60
61    properties.extend(object.metrics.into_iter().map(|metric| match metric.value {
62        MetricValue::IntValue(v) => Property::Int(metric.key, v),
63        MetricValue::DoubleValue(v) => Property::Double(metric.key, v),
64        MetricValue::UintValue(v) => Property::Uint(metric.key, v),
65    }));
66
67    let pending_children = proxy.list_children().await?;
68    let hierarchy = DiagnosticsHierarchy::new(&object.name, properties, vec![]);
69    Ok(PartialNodeHierarchy { proxy, hierarchy, pending_children })
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use diagnostics_assertions::assert_data_tree;
76    use fidl_fuchsia_inspect_deprecated::{
77        InspectMarker, InspectRequest, InspectRequestStream, Metric, Object, Property,
78    };
79    use futures::{TryFutureExt, TryStreamExt};
80    use maplit::{hashmap, hashset};
81    use std::collections::{HashMap, HashSet};
82    use std::sync::LazyLock;
83
84    #[fuchsia::test]
85    async fn test_load_hierarchy() -> Result<(), Error> {
86        let (client_proxy, server_stream) =
87            fidl::endpoints::create_proxy_and_stream::<InspectMarker>();
88        spawn_server(server_stream, "root".to_string());
89        let hierarchy = load_hierarchy(client_proxy).await?;
90        assert_data_tree!(hierarchy, root: {
91            double_value: 5.2,
92            a: {
93                int_value: -3i64,
94                uint_value: 2u64,
95                c: {}
96            },
97            b: {
98                bytes_value: vec![0x12u8, 0x34, 0x56],
99                string_value: "test",
100            }
101        });
102        Ok(())
103    }
104
105    static OBJECTS: LazyLock<HashMap<String, TestObject>> = LazyLock::new(|| {
106        hashmap! {
107            "root".to_string() => TestObject {
108                object: Object {
109                    name: "root".to_string(),
110                    metrics: vec![
111                        Metric {
112                            key: "double_value".to_string(),
113                            value: MetricValue::DoubleValue(5.2),
114                        },
115                    ],
116                    properties: vec![],
117                },
118                children: hashset!("a".to_string(), "b".to_string()),
119            },
120            "a".to_string() => TestObject {
121                object: Object {
122                    name: "a".to_string(),
123                    metrics: vec![
124                        Metric {
125                            key: "int_value".to_string(),
126                            value: MetricValue::IntValue(-3),
127                        },
128                        Metric {
129                            key: "uint_value".to_string(),
130                            value: MetricValue::UintValue(2),
131                        },
132                    ],
133                    properties: vec![],
134                },
135                children: hashset!("c".to_string()),
136            },
137            "b".to_string() => TestObject {
138                object: Object {
139                    name: "b".to_string(),
140                    metrics: vec![],
141                    properties: vec![
142                        Property{
143                            key: "string_value".to_string(),
144                            value: PropertyValue::Str("test".to_string()),
145                        },
146                        Property {
147                            key: "bytes_value".to_string(),
148                            value: PropertyValue::Bytes(vec![0x12u8, 0x34, 0x56]),
149                        },
150                    ],
151                },
152                children: hashset!(),
153            },
154            "c".to_string() => TestObject {
155                object: Object {
156                    name: "c".to_string(),
157                    metrics: vec![],
158                    properties: vec![],
159                },
160                children: hashset!(),
161            }
162        }
163    });
164
165    struct TestObject {
166        object: Object,
167        children: HashSet<String>,
168    }
169
170    fn spawn_server(mut stream: InspectRequestStream, object_name: String) {
171        fasync::Task::spawn(
172            async move {
173                let object = OBJECTS.get(&object_name).unwrap();
174                while let Some(req) = stream.try_next().await? {
175                    match req {
176                        InspectRequest::ReadData { responder } => {
177                            responder.send(&object.object)?;
178                        }
179                        InspectRequest::ListChildren { responder } => {
180                            responder.send(&object.children.iter().cloned().collect::<Vec<_>>())?;
181                        }
182                        InspectRequest::OpenChild { child_name, child_channel, responder } => {
183                            let stream = child_channel.into_stream();
184                            if object.children.contains(&child_name) {
185                                spawn_server(stream, child_name);
186                                responder.send(true)?;
187                            } else {
188                                responder.send(false)?;
189                            }
190                        }
191                    }
192                }
193                Ok(())
194            }
195            .unwrap_or_else(|e: Error| eprintln!("error running inspect server: {e:?}")),
196        )
197        .detach();
198    }
199}