inspect/
defer.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support for deferring inspection requests.
5
6use super::InspectMut;
7use super::InternalNode;
8use super::Request;
9use super::RequestRoot;
10use super::Response;
11use super::SensitivityLevel;
12use super::UpdateRequest;
13use super::Value;
14use alloc::borrow::ToOwned;
15use alloc::string::String;
16use mesh::MeshPayload;
17
18impl Request<'_> {
19    /// Defers the inspection request, producing a value that can be sent to
20    /// another thread or context to continue the inspection asynchronously.
21    pub fn defer(self) -> Deferred {
22        let (send, recv) = mesh::oneshot();
23        *self.node = InternalNode::Deferred(recv);
24        Deferred {
25            path: self.path.to_owned(),
26            value: self.value.map(|x| x.to_owned()),
27            depth: self.depth,
28            node: send,
29            sensitivity: self.sensitivity,
30        }
31    }
32}
33
34impl UpdateRequest<'_> {
35    /// Defers this update request, returning an object that can be sent across
36    /// threads or processes and then used to report the update result at a
37    /// later time.
38    pub fn defer(self) -> DeferredUpdate {
39        let (send, recv) = mesh::oneshot();
40        *self.node = InternalNode::Deferred(recv);
41        DeferredUpdate {
42            value: self.value.to_owned(),
43            node: send,
44        }
45    }
46}
47
48/// A deferred inspection, which can provide inspection results asynchronously
49/// from a call to [`inspect`](crate::Inspect::inspect).
50#[derive(Debug, MeshPayload)]
51pub struct Deferred {
52    path: String,
53    value: Option<String>,
54    depth: usize,
55    node: mesh::OneshotSender<InternalNode>,
56    sensitivity: SensitivityLevel,
57}
58
59impl Deferred {
60    /// Inspect an object as part of a deferred inspection.
61    pub fn inspect(self, mut obj: impl InspectMut) {
62        let mut root = self.root();
63        obj.inspect_mut(root.request());
64        let node = root.node;
65        self.node.send(node);
66    }
67
68    /// Responds to the deferred inspection, calling `f` with a [`Response`].
69    pub fn respond<F: FnOnce(&mut Response<'_>)>(self, f: F) {
70        let mut root = self.root();
71        f(&mut root.request().respond());
72        let node = root.node;
73        self.node.send(node);
74    }
75
76    /// Responds to the deferred request with a value.
77    pub fn value(self, value: Value) {
78        let mut root = self.root();
79        root.request().value(value);
80        let node = root.node;
81        self.node.send(node);
82    }
83
84    /// Returns an object used for handling an update request.
85    ///
86    /// If this is not an update request, returns `Err(self)`.
87    pub fn update(self) -> Result<DeferredUpdate, Self> {
88        if self.value.is_some() && self.path.is_empty() {
89            Ok(DeferredUpdate {
90                value: self.value.unwrap(),
91                node: self.node,
92            })
93        } else {
94            Err(self)
95        }
96    }
97
98    fn root(&self) -> RequestRoot<'_> {
99        RequestRoot::new(
100            &self.path,
101            self.depth,
102            self.value.as_deref(),
103            self.sensitivity,
104        )
105    }
106
107    /// Removes this node from the inspection output.
108    pub fn ignore(self) {
109        self.node.send(InternalNode::Ignored);
110    }
111
112    /// Gets the request information for sending to a remote node via a non-mesh
113    /// communication mechanism, for use with [`complete_external`][].
114    ///
115    /// You don't need this if you are communicating with the remote object via
116    /// mesh. In that case, just send this object to the remote object over a
117    /// mesh channel, and then use [`respond`][] or similar methods to
118    /// handle the request.
119    ///
120    /// Use this when the remote object is across some other communication
121    /// boundary, such as gRPC. In that case, you will be responsible for using
122    /// [`inspect`][] on the remote node to handle the request, and to serializing
123    /// and deserializing the [`Node`][] structure across the communications
124    /// boundary.
125    ///
126    /// [`inspect`]: crate::inspect
127    /// [`Node`]: crate::Node
128    /// [`respond`]: Self::respond
129    /// [`complete_external`]: Self::complete_external
130    #[cfg(feature = "initiate")]
131    pub fn external_request(&self) -> ExternalRequest<'_> {
132        ExternalRequest {
133            path: &self.path,
134            sensitivity: self.sensitivity,
135            request_type: match &self.value {
136                None => ExternalRequestType::Inspect { depth: self.depth },
137                Some(value) => ExternalRequestType::Update { value },
138            },
139        }
140    }
141
142    /// Complete the request with a [`Node`][] and a [`SensitivityLevel`].
143    ///
144    /// See [`external_request`][] for details on how to use this.
145    ///
146    /// [`Node`]: crate::Node
147    /// [`external_request`]: Self::external_request
148    #[cfg(feature = "initiate")]
149    pub fn complete_external(self, node: super::Node, sensitivity: SensitivityLevel) {
150        // If the returned sensitivity level is not allowed for this request, drop it.
151        if sensitivity > self.sensitivity {
152            return;
153        }
154        if let Some(node) = InternalNode::from_node(node, self.sensitivity) {
155            // Add the prefixed path back on as a sequence of directory nodes. This
156            // is necessary so that they can be skipped in post-processing.
157            let node =
158                self.path
159                    .split('/')
160                    .filter(|s| !s.is_empty())
161                    .rev()
162                    .fold(node, |node, name| {
163                        InternalNode::DirResolved(alloc::vec![crate::InternalEntry {
164                            name: name.to_owned(),
165                            node,
166                            sensitivity,
167                        }])
168                    });
169
170            self.node.send(node);
171        }
172    }
173
174    /// Gets the sensitivity level for this request.
175    pub fn sensitivity(&self) -> SensitivityLevel {
176        self.sensitivity
177    }
178}
179
180impl InternalNode {
181    #[cfg(feature = "initiate")]
182    pub(crate) fn from_node(
183        value: crate::Node,
184        request_sensitivity: SensitivityLevel,
185    ) -> Option<Self> {
186        use crate::Error;
187        use crate::InternalError;
188        use crate::Node;
189
190        let node = match value {
191            Node::Unevaluated => Self::Unevaluated,
192            Node::Failed(err) => Self::Failed(match err {
193                Error::NotFound => return None,
194                Error::Unresolved => InternalError::Unresolved,
195                Error::Mesh(err) => InternalError::Mesh(err),
196                Error::Immutable => InternalError::Immutable,
197                Error::Update(err) => InternalError::Update(err),
198                Error::NotADirectory => InternalError::NotADirectory,
199                Error::Internal => return None,
200            }),
201            Node::Value(v) => Self::Value(v),
202            Node::Dir(children) => Self::DirResolved(
203                children
204                    .into_iter()
205                    .filter_map(|e| {
206                        // If the returned sensitivity level is not allowed for this request, drop it.
207                        if e.sensitivity > request_sensitivity {
208                            return None;
209                        }
210                        InternalNode::from_node(e.node, request_sensitivity).map(|v| {
211                            crate::InternalEntry {
212                                name: e.name,
213                                node: v,
214                                sensitivity: e.sensitivity,
215                            }
216                        })
217                    })
218                    .collect(),
219            ),
220        };
221        Some(node)
222    }
223}
224
225/// Return value from [`Deferred::external_request`], specifying parameters for
226/// a remote inspection.
227#[cfg(feature = "initiate")]
228pub struct ExternalRequest<'a> {
229    /// The remaining path of the request.
230    pub path: &'a str,
231    /// The request type and associated data.
232    pub request_type: ExternalRequestType<'a>,
233    /// The sensitivity level of the request.
234    pub sensitivity: SensitivityLevel,
235}
236
237/// The request type associated with [`ExternalRequest`].
238#[cfg(feature = "initiate")]
239pub enum ExternalRequestType<'a> {
240    /// An inspection request.
241    Inspect {
242        /// The depth to which to recurse.
243        depth: usize,
244    },
245    /// An update request.
246    Update {
247        /// The value to update to.
248        value: &'a str,
249    },
250}
251
252/// A deferred inspection, which can provide inspection results asynchronously
253/// from a call to [`inspect`](crate::Inspect::inspect).
254#[derive(Debug, MeshPayload)]
255pub struct DeferredUpdate {
256    value: String,
257    node: mesh::OneshotSender<InternalNode>,
258}
259
260impl DeferredUpdate {
261    /// Gets the requested new value.
262    pub fn new_value(&self) -> &str {
263        &self.value
264    }
265
266    /// Report that the update succeeded, with a new value of `value`.
267    pub fn succeed(self, value: Value) {
268        self.node.send(InternalNode::Value(value));
269    }
270
271    /// Report that the update failed, with the reason in `err`.
272    pub fn fail<E: Into<alloc::boxed::Box<dyn core::error::Error + Send + Sync>>>(self, err: E) {
273        self.node.send(InternalNode::failed(err.into()));
274    }
275}