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