fdf_component/
node.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl::endpoints::ClientEnd;
6use fidl_fuchsia_driver_framework::{
7    NodeAddArgs, NodeControllerMarker, NodeMarker, NodeProperty2, NodeProxy, Offer,
8};
9use log::error;
10use zx::Status;
11
12mod offers;
13mod properties;
14
15pub use offers::*;
16pub use properties::*;
17
18/// Holds on to a [`NodeProxy`] and provides simplified methods for adding child nodes.
19pub struct Node(NodeProxy);
20
21impl Node {
22    /// Adds an owned child node to this node and returns the [`ClientEnd`]s for its
23    /// `NodeController` and `Node`. Use a [`NodeBuilder`] to create the `args` argument.
24    ///
25    /// If you don't need the `NodeController`, it is safe to drop it, but the node will be removed
26    /// if the client end for the `Node` is dropped.
27    ///
28    /// Logs an error message and returns [`Status::INTERNAL`] if there's an error adding the
29    /// child node.
30    pub async fn add_owned_child(
31        &self,
32        args: NodeAddArgs,
33    ) -> Result<(ClientEnd<NodeMarker>, ClientEnd<NodeControllerMarker>), Status> {
34        let (child_controller, child_controller_server) = fidl::endpoints::create_endpoints();
35        let (child_node, child_node_server) = fidl::endpoints::create_endpoints();
36        self.proxy()
37            .add_child(args, child_controller_server, Some(child_node_server))
38            .await
39            .map_err(|err| {
40                error!("transport error trying to create child node: {err}");
41                Status::INTERNAL
42            })?
43            .map_err(|err| {
44                error!("failed to create child node: {err:?}");
45                Status::INTERNAL
46            })?;
47        Ok((child_node, child_controller))
48    }
49
50    /// Adds an owned child node to this node and returns the [`ClientEnd`]s for its
51    /// `NodeController`. Use a [`NodeBuilder`] to create the `args` argument.
52    ///
53    /// If you don't need the `NodeController`, it is safe to drop it. The driver runtime will
54    /// attempt to find a driver to bind the node to.
55    ///
56    /// Logs an error message and returns [`Status::INTERNAL`] if there's an error adding the
57    /// child node.
58    pub async fn add_child(
59        &self,
60        args: NodeAddArgs,
61    ) -> Result<ClientEnd<NodeControllerMarker>, Status> {
62        let (child_controller, child_controller_server) = fidl::endpoints::create_endpoints();
63        self.proxy()
64            .add_child(args, child_controller_server, None)
65            .await
66            .map_err(|err| {
67                error!("transport error trying to create child node: {err}");
68                Status::INTERNAL
69            })?
70            .map_err(|err| {
71                error!("failed to create child node: {err:?}");
72                Status::INTERNAL
73            })?;
74        Ok(child_controller)
75    }
76
77    /// Accesses the underlying [`NodeProxy`].
78    pub fn proxy(&self) -> &NodeProxy {
79        &self.0
80    }
81}
82
83impl From<NodeProxy> for Node {
84    fn from(value: NodeProxy) -> Self {
85        Node(value)
86    }
87}
88
89/// A builder for adding a child node to an existing [`Node`].
90pub struct NodeBuilder(NodeAddArgs);
91
92impl NodeBuilder {
93    /// Creates a new [`NodeAddBuilder`] with the given `node_name` already set.
94    pub fn new(node_name: impl Into<String>) -> Self {
95        Self(NodeAddArgs { name: Some(node_name.into()), ..Default::default() })
96    }
97
98    /// Adds a property to the node. The `key` argument is something that can convert to a
99    /// [`PropertyKey`], which includes strings and [`u32`] integers. The `value` argument is
100    /// something that can convert into a [`PropertyValue`], which includes strings, [`u32`]
101    /// integers, and [`bool`] values.
102    pub fn add_property(mut self, key: impl Into<String>, value: impl Into<PropertyValue>) -> Self {
103        let key = key.into();
104        let value = value.into().0;
105        self.0.properties2.get_or_insert_with(|| vec![]).push(NodeProperty2 { key, value });
106        self
107    }
108
109    /// Adds a service offer to the node. The `offer` can be built with the
110    /// [`offers::ZirconServiceOffer`] builder.
111    pub fn add_offer(mut self, offer: Offer) -> Self {
112        self.0.offers2.get_or_insert_with(|| vec![]).push(offer);
113        self
114    }
115
116    /// Triggers the callback if |condition| is true.
117    pub fn pipe_if<F>(self, condition: bool, callback: F) -> Self
118    where
119        F: FnOnce(Self) -> Self,
120    {
121        if condition {
122            callback(self)
123        } else {
124            self
125        }
126    }
127
128    /// Triggers the callback if |value| has a some value in it.
129    pub fn pipe_opt<T, F>(self, value: Option<T>, callback: F) -> Self
130    where
131        F: FnOnce(Self, T) -> Self,
132    {
133        if let Some(value) = value {
134            callback(self, value)
135        } else {
136            self
137        }
138    }
139
140    /// Finalize the construction of the node for use with [`Node::add_child`] or
141    /// [`Node::add_owned_child`].
142    pub fn build(self) -> NodeAddArgs {
143        self.0
144    }
145}