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