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