fuchsia_inspect_contrib/
content_publisher.rs1use anyhow::{Context, Error};
6use fidl::endpoints::ClientEnd;
7use fidl_fuchsia_inspect::{
8 InspectSinkMarker, InspectSinkPublishRequest, TreeContent, TreeGetContentResponder, TreeMarker,
9 TreeRequest, TreeRequestStream,
10};
11use fuchsia_inspect::Inspector;
12use futures::{Stream, StreamExt};
13use std::pin::Pin;
14use std::task::{Context as TaskContext, Poll};
15
16pub struct PublishOptions {
18 pub inspect_sink_client: ClientEnd<InspectSinkMarker>,
20}
21
22pub struct ContentResponder {
24 responder: TreeGetContentResponder,
25}
26
27impl ContentResponder {
28 pub fn send(self, inspector: Inspector) -> Result<(), Error> {
31 let vmo = inspector.frozen_vmo_copy().context("failed to copy vmo")?;
32 let size = vmo.get_size().context("failed to get vmo size")?;
33 let content = TreeContent {
34 buffer: Some(fidl_fuchsia_mem::Buffer { vmo, size }),
35 ..Default::default()
36 };
37
38 self.responder.send(content).context("failed to send response")?;
39 Ok(())
40 }
41}
42
43pub struct ContentPublisher {
45 stream: TreeRequestStream,
46}
47
48impl Stream for ContentPublisher {
49 type Item = ContentResponder;
50
51 fn poll_next(mut self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Option<Self::Item>> {
52 loop {
53 match self.stream.poll_next_unpin(cx) {
54 Poll::Ready(Some(Ok(TreeRequest::GetContent { responder }))) => {
55 return Poll::Ready(Some(ContentResponder { responder }));
56 }
57 Poll::Ready(Some(Ok(_))) => {
58 continue;
61 }
62 Poll::Ready(Some(Err(_))) => {
63 return Poll::Ready(None);
65 }
66 Poll::Ready(None) => return Poll::Ready(None),
67 Poll::Pending => return Poll::Pending,
68 }
69 }
70 }
71}
72
73pub fn content_publisher(options: PublishOptions) -> Result<ContentPublisher, Error> {
96 let (tree_client, tree_stream) = fidl::endpoints::create_request_stream::<TreeMarker>();
97
98 let inspect_sink = options.inspect_sink_client.into_proxy();
99
100 inspect_sink
101 .publish(InspectSinkPublishRequest { tree: Some(tree_client), ..Default::default() })
102 .context("failed to publish tree")?;
103
104 Ok(ContentPublisher { stream: tree_stream })
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use diagnostics_assertions::assert_data_tree;
111 use fidl_fuchsia_inspect::InspectSinkRequest;
112 use fuchsia_async as fasync;
113 use fuchsia_inspect::reader::read;
114 use futures::StreamExt;
115
116 #[fuchsia::test]
117 async fn test_content_publisher_usage() {
118 let (sink_client, mut sink_stream) =
119 fidl::endpoints::create_request_stream::<InspectSinkMarker>();
120
121 let options = PublishOptions { inspect_sink_client: sink_client };
122
123 let mut publisher = content_publisher(options).expect("failed to create publisher");
124
125 let tree_proxy = match sink_stream.next().await {
126 Some(Ok(InspectSinkRequest::Publish { payload, .. })) => {
127 payload.tree.expect("tree channel missing").into_proxy()
128 }
129 _ => panic!("Expected Publish request"),
130 };
131
132 let _task = fasync::Task::spawn(async move {
135 let mut val = 0;
136 while let Some(responder) = publisher.next().await {
137 let inspector = Inspector::default();
139 inspector.root().record_int("dynamic_data", val);
140 inspector.root().record_string("status", "ok");
141
142 responder.send(inspector).expect("failed to send inspector");
144 val += 1
145 }
146 });
147
148 let hierarchy = read(&tree_proxy).await.expect("failed to read from tree proxy");
150
151 assert_data_tree!(hierarchy, root: {
153 dynamic_data: 0i64,
154 status: "ok",
155 });
156
157 let hierarchy = read(&tree_proxy).await.expect("failed to read from tree proxy");
159
160 assert_data_tree!(hierarchy, root: {
162 dynamic_data: 1i64,
163 status: "ok",
164 });
165 }
166}