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