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