fdf_component/node/offers.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 std::marker::PhantomData;
6
7use fidl::endpoints::{ServiceMarker, ServiceRequest};
8use fidl_fuchsia_component_decl::{NameMapping, OfferService};
9use fidl_fuchsia_driver_framework::Offer;
10use fuchsia_component::DEFAULT_SERVICE_INSTANCE;
11use fuchsia_component::server::{FidlServiceMember, ServiceFs, ServiceObjTrait};
12
13/// A builder for creating [`Offer`]-compatible values for [`crate::NodeBuilder::add_offer`] that
14/// service a zircon fidl service.
15///
16/// The methods on this that start with `add_` are helpers that will both register a service handler
17/// with a [`ServiceFs`] and register the instance name in the structure. If you're handling adding
18/// your service handlers to the outgoing directory yourself, you can just use the non-`add_`
19/// methods to register them.
20///
21/// If no calls to add any instances are made, then when this is transformed into a service offer
22/// it will be as if a single default instance with the default name was added.
23pub struct ZirconServiceOffer<S> {
24 service_name: String,
25 instances: Vec<NameMapping>,
26 _p: PhantomData<S>,
27}
28
29impl<S> ZirconServiceOffer<S> {
30 /// Builds an offer for a zircon transport service based on the [`ServiceMarker`] for `S`.
31 ///
32 /// If the compiler can't deduce the type of `S` (which may be the case if you're not using the
33 /// `add_` methods to add to a [`ServiceFs`] at the same time), you can use [`Self::new_marker`]
34 /// to make it explicit.
35 pub fn new() -> Self
36 where
37 S: ServiceMarker,
38 {
39 let service_name = S::SERVICE_NAME.to_owned();
40 let instances = vec![];
41 Self { service_name, instances, _p: PhantomData }
42 }
43
44 /// Builds an offer for a zircon transport service based on the [`DiscoverableService`] for `S`.
45 ///
46 /// If the compiler can't deduce the type of `S` (which may be the case if you're not using the
47 /// `add_` methods to add to a [`ServiceFs`] at the same time), you can use
48 /// [`Self::new_marker_next`] to make it explicit.
49 pub fn new_next() -> Self
50 where
51 S: fidl_next::DiscoverableService,
52 {
53 let service_name = S::SERVICE_NAME.to_owned();
54 let instances = vec![];
55 Self { service_name, instances, _p: PhantomData }
56 }
57
58 /// Builds an offer for a zircon transport service based on the given [`ServiceMarker`].
59 ///
60 /// This is mostly useful if the compiler can't derive the type of `S` on its own.
61 pub fn new_marker(_marker: S) -> Self
62 where
63 S: ServiceMarker,
64 {
65 let service_name = S::SERVICE_NAME.to_owned();
66 let instances = vec![];
67 Self { service_name, instances, _p: PhantomData }
68 }
69
70 /// Builds an offer for a zircon transport service based on the [`DiscoverableService`].
71 ///
72 /// This is mostly useful if the compiler can't derive the type of `S` on its own.
73 pub fn new_marker_next(_marker: S) -> Self
74 where
75 S: fidl_next::DiscoverableService,
76 {
77 Self::new_next()
78 }
79
80 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
81 /// generator function `f`. The type of the service will be derived from the result of the
82 /// generator function and it will be added with the name `name` which will be mapped to the
83 /// default instance name to child components ([`DEFAULT_SERVICE_INSTANCE`]).
84 pub fn add_default_named<O: ServiceObjTrait, F, SR>(
85 self,
86 fs: &mut ServiceFs<O>,
87 name: impl Into<String>,
88 f: F,
89 ) -> Self
90 where
91 F: Fn(SR) -> O::Output,
92 F: Clone,
93 SR: ServiceRequest<Service = S>,
94 FidlServiceMember<F, SR, O::Output>: Into<O>,
95 {
96 let name = name.into();
97 fs.dir("svc").add_fidl_service_instance(name.clone(), f);
98 self.named_default_instance(name)
99 }
100
101 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
102 /// generator function `f`. The type of the service will be derived from the result of the
103 /// generator function and it will be added with the name `name` which will be mapped to the
104 /// default instance name to child components ([`DEFAULT_SERVICE_INSTANCE`]).
105 pub fn add_default_named_next<H, O>(
106 self,
107 fs: &mut ServiceFs<O>,
108 name: impl Into<String>,
109 handler: H,
110 ) -> Self
111 where
112 O: ServiceObjTrait,
113 S: fidl_next::DiscoverableService
114 + fidl_next::DispatchServiceHandler<H, zx::Channel>
115 + 'static,
116 H: Send + Sync + 'static,
117 {
118 let name = name.into();
119 fs.dir("svc").add_fidl_next_service_instance::<S, _>(name.clone(), handler);
120 self.named_default_instance(name)
121 }
122
123 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
124 /// generator function `f`. The type of the service will be derived from the result of the
125 /// generator function and it will be added with the name `name`.
126 pub fn add_named<O: ServiceObjTrait, F, SR>(
127 self,
128 fs: &mut ServiceFs<O>,
129 name: impl Into<String>,
130 f: F,
131 ) -> Self
132 where
133 F: Fn(SR) -> O::Output,
134 F: Clone,
135 SR: ServiceRequest<Service = S>,
136 FidlServiceMember<F, SR, O::Output>: Into<O>,
137 {
138 let name = name.into();
139 fs.dir("svc").add_fidl_service_instance(name.clone(), f);
140 self.named_instance(name)
141 }
142
143 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
144 /// generator function `f`. The type of the service will be derived from the result of the
145 /// generator function and it will be added with the name `name`.
146 pub fn add_named_next<H, O>(
147 self,
148 fs: &mut ServiceFs<O>,
149 name: impl Into<String>,
150 handler: H,
151 ) -> Self
152 where
153 O: ServiceObjTrait,
154 S: fidl_next::DiscoverableService
155 + fidl_next::DispatchServiceHandler<H, zx::Channel>
156 + 'static,
157 H: Send + Sync + 'static,
158 {
159 let name = name.into();
160 fs.dir("svc").add_fidl_next_service_instance::<S, _>(name.clone(), handler);
161 self.named_instance(name)
162 }
163
164 /// Adds the named instance as the `default` instance of this service offer (as specified
165 /// by [`DEFAULT_SERVICE_INSTANCE`]). If you are only offering a single instance that is
166 /// already called `default`, you do not need to call this.
167 pub fn named_default_instance(mut self, name: impl Into<String>) -> Self {
168 self.instances.push(NameMapping {
169 source_name: name.into(),
170 target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
171 });
172 self
173 }
174
175 /// Adds the named instance to the offer without mapping it to the default instance name.
176 /// You can use this to add additional instances offered in your outgoing directory.
177 pub fn named_instance(mut self, name: impl Into<String>) -> Self {
178 let source_name = name.into();
179 let target_name = source_name.clone();
180 self.instances.push(NameMapping { source_name, target_name });
181 self
182 }
183
184 /// Finalize the construction of the [`Offer`] object for use with
185 /// [`super::NodeBuilder::add_offer`].
186 pub fn build(self) -> Offer {
187 // if no instances were added, assume there's a single default instance
188 let mut instances = self.instances;
189 if instances.is_empty() {
190 instances.push(NameMapping {
191 source_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
192 target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
193 });
194 }
195 let service = OfferService {
196 source_name: Some(self.service_name.clone()),
197 target_name: Some(self.service_name),
198 source_instance_filter: Some(vec![DEFAULT_SERVICE_INSTANCE.to_owned()]),
199 renamed_instances: Some(instances),
200 ..Default::default()
201 };
202 Offer::ZirconTransport(fidl_fuchsia_component_decl::Offer::Service(service))
203 }
204}