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.
45use fidl::endpoints::ClientEnd;
6use fidl_fuchsia_driver_framework::{
7 NodeAddArgs, NodeControllerMarker, NodeMarker, NodeProperty2, NodeProxy, Offer,
8};
9use log::error;
10use zx::Status;
1112mod offers;
13mod properties;
1415pub use offers::*;
16pub use properties::*;
1718/// Holds on to a [`NodeProxy`] and provides simplified methods for adding child nodes.
19pub struct Node(NodeProxy);
2021impl 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.
30pub async fn add_owned_child(
31&self,
32 args: NodeAddArgs,
33 ) -> Result<(ClientEnd<NodeMarker>, ClientEnd<NodeControllerMarker>), Status> {
34let (child_controller, child_controller_server) = fidl::endpoints::create_endpoints();
35let (child_node, child_node_server) = fidl::endpoints::create_endpoints();
36self.proxy()
37 .add_child(args, child_controller_server, Some(child_node_server))
38 .await
39.map_err(|err| {
40error!("transport error trying to create child node: {err}");
41 Status::INTERNAL
42 })?
43.map_err(|err| {
44error!("failed to create child node: {err:?}");
45 Status::INTERNAL
46 })?;
47Ok((child_node, child_controller))
48 }
4950/// 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.
58pub async fn add_child(
59&self,
60 args: NodeAddArgs,
61 ) -> Result<ClientEnd<NodeControllerMarker>, Status> {
62let (child_controller, child_controller_server) = fidl::endpoints::create_endpoints();
63self.proxy()
64 .add_child(args, child_controller_server, None)
65 .await
66.map_err(|err| {
67error!("transport error trying to create child node: {err}");
68 Status::INTERNAL
69 })?
70.map_err(|err| {
71error!("failed to create child node: {err:?}");
72 Status::INTERNAL
73 })?;
74Ok(child_controller)
75 }
7677/// Accesses the underlying [`NodeProxy`].
78pub fn proxy(&self) -> &NodeProxy {
79&self.0
80}
81}
8283impl From<NodeProxy> for Node {
84fn from(value: NodeProxy) -> Self {
85 Node(value)
86 }
87}
8889/// A builder for adding a child node to an existing [`Node`].
90pub struct NodeBuilder(NodeAddArgs);
9192impl NodeBuilder {
93/// Creates a new [`NodeAddBuilder`] with the given `node_name` already set.
94pub fn new(node_name: impl Into<String>) -> Self {
95Self(NodeAddArgs { name: Some(node_name.into()), ..Default::default() })
96 }
9798/// 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.
102pub fn add_property(mut self, key: impl Into<String>, value: impl Into<PropertyValue>) -> Self {
103let key = key.into();
104let value = value.into().0;
105self.0.properties2.get_or_insert_with(|| vec![]).push(NodeProperty2 { key, value });
106self
107}
108109/// Adds a service offer to the node. The `offer` can be built with the
110 /// [`offers::ZirconServiceOffer`] builder.
111pub fn add_offer(mut self, offer: Offer) -> Self {
112self.0.offers2.get_or_insert_with(|| vec![]).push(offer);
113self
114}
115116/// Triggers the callback if |condition| is true.
117pub fn pipe_if<F>(self, condition: bool, callback: F) -> Self
118where
119F: FnOnce(Self) -> Self,
120 {
121if condition {
122 callback(self)
123 } else {
124self
125}
126 }
127128/// Triggers the callback if |value| has a some value in it.
129pub fn pipe_opt<T, F>(self, value: Option<T>, callback: F) -> Self
130where
131F: FnOnce(Self, T) -> Self,
132 {
133if let Some(value) = value {
134 callback(self, value)
135 } else {
136self
137}
138 }
139140/// Finalize the construction of the node for use with [`Node::add_child`] or
141 /// [`Node::add_owned_child`].
142pub fn build(self) -> NodeAddArgs {
143self.0
144}
145}