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 zircon transport service based on the given [`ServiceMarker`].
66 ///
67 /// This is mostly useful if the compiler can't derive the type of `S` on its own.
68 pub fn new_marker(_marker: S) -> Self
69 where
70 S: ServiceMarker,
71 {
72 let service_name = S::SERVICE_NAME.to_owned();
73 let instances = vec![];
74 Self { service_name, instances, _p: PhantomData }
75 }
76
77 /// Builds an offer for a service based on the [`fidl_next::DiscoverableService`].
78 ///
79 /// This is mostly useful if the compiler can't derive the type of `S` on its own.
80 pub fn new_marker_next(_marker: S) -> Self
81 where
82 S: fidl_next::DiscoverableService,
83 {
84 Self::new_next()
85 }
86
87 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
88 /// generator function `f`. The type of the service will be derived from the result of the
89 /// generator function and it will be added with the name `name` which will be mapped to the
90 /// default instance name to child components ([`DEFAULT_SERVICE_INSTANCE`]).
91 pub fn add_default_named<O: ServiceObjTrait, F, SR>(
92 self,
93 fs: &mut ServiceFs<O>,
94 name: impl Into<String>,
95 f: F,
96 ) -> Self
97 where
98 F: Fn(SR) -> O::Output,
99 F: Clone,
100 SR: ServiceRequest<Service = S>,
101 FidlServiceMember<F, SR, O::Output>: Into<O>,
102 {
103 let name = name.into();
104 fs.dir("svc").add_fidl_service_instance(name.clone(), f);
105 self.named_default_instance(name)
106 }
107
108 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
109 /// generator function `f`. The type of the service will be derived from the result of the
110 /// generator function and it will be added with the name `name` which will be mapped to the
111 /// default instance name to child components ([`DEFAULT_SERVICE_INSTANCE`]).
112 pub fn add_default_named_next<H, O>(
113 self,
114 fs: &mut ServiceFs<O>,
115 name: impl Into<String>,
116 handler: H,
117 ) -> Self
118 where
119 O: ServiceObjTrait,
120 S: fidl_next::DiscoverableService
121 + fidl_next::DispatchServiceHandler<H, zx::Channel>
122 + 'static,
123 H: Send + Sync + 'static,
124 {
125 let name = name.into();
126 fs.dir("svc").add_fidl_next_service_instance::<S, _>(name.clone(), handler);
127 self.named_default_instance(name)
128 }
129
130 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
131 /// generator function `f`. The type of the service will be derived from the result of the
132 /// generator function and it will be added with the name `name`.
133 pub fn add_named<O: ServiceObjTrait, F, SR>(
134 self,
135 fs: &mut ServiceFs<O>,
136 name: impl Into<String>,
137 f: F,
138 ) -> Self
139 where
140 F: Fn(SR) -> O::Output,
141 F: Clone,
142 SR: ServiceRequest<Service = S>,
143 FidlServiceMember<F, SR, O::Output>: Into<O>,
144 {
145 let name = name.into();
146 fs.dir("svc").add_fidl_service_instance(name.clone(), f);
147 self.named_instance(name)
148 }
149
150 /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
151 /// generator function `f`. The type of the service will be derived from the result of the
152 /// generator function and it will be added with the name `name`.
153 pub fn add_named_next<H, O>(
154 self,
155 fs: &mut ServiceFs<O>,
156 name: impl Into<String>,
157 handler: H,
158 ) -> Self
159 where
160 O: ServiceObjTrait,
161 S: fidl_next::DiscoverableService
162 + fidl_next::DispatchServiceHandler<H, zx::Channel>
163 + 'static,
164 H: Send + Sync + 'static,
165 {
166 let name = name.into();
167 fs.dir("svc").add_fidl_next_service_instance::<S, _>(name.clone(), handler);
168 self.named_instance(name)
169 }
170
171 /// Adds the named instance as the `default` instance of this service offer (as specified
172 /// by [`DEFAULT_SERVICE_INSTANCE`]). If you are only offering a single instance that is
173 /// already called `default`, you do not need to call this.
174 pub fn named_default_instance(mut self, name: impl Into<String>) -> Self {
175 self.instances.push(NameMapping {
176 source_name: name.into(),
177 target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
178 });
179 self
180 }
181
182 /// Adds the named instance to the offer without mapping it to the default instance name.
183 /// You can use this to add additional instances offered in your outgoing directory.
184 pub fn named_instance(mut self, name: impl Into<String>) -> Self {
185 let source_name = name.into();
186 let target_name = source_name.clone();
187 self.instances.push(NameMapping { source_name, target_name });
188 self
189 }
190
191 fn build_offer(self) -> fidl_fuchsia_component_decl::Offer {
192 // if no instances were added, assume there's a single default instance
193 let mut instances = self.instances;
194 if instances.is_empty() {
195 instances.push(NameMapping {
196 source_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
197 target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
198 });
199 }
200 let service = OfferService {
201 source_name: Some(self.service_name.clone()),
202 target_name: Some(self.service_name),
203 source_instance_filter: Some(vec![DEFAULT_SERVICE_INSTANCE.to_owned()]),
204 renamed_instances: Some(instances),
205 ..Default::default()
206 };
207 fidl_fuchsia_component_decl::Offer::Service(service)
208 }
209
210 /// Finalize the construction of the [`Offer`] object for use with
211 /// [`super::NodeBuilder::add_offer`] when the service is a zircon transport
212 /// service.
213 pub fn build_zircon_offer(self) -> Offer
214 where
215 S: ServiceMarker,
216 {
217 Offer::ZirconTransport(self.build_offer())
218 }
219
220 /// Finalize the construction of the [`Offer`] object for use with
221 /// [`super::NodeBuilder::add_offer`] when the service is a zircon transport
222 /// service with the new wire bindings.
223 pub fn build_zircon_offer_next(self) -> Offer
224 where
225 S: HasServiceRequest<zx::Channel>,
226 {
227 Offer::ZirconTransport(self.build_offer())
228 }
229
230 /// Finalize the construction of the [`Offer`] object for use with
231 /// [`super::NodeBuilder::add_offer`] when the service is a driver
232 /// transport service.
233 pub fn build_driver_offer(self) -> Offer
234 where
235 S: HasServiceRequest<fdf_fidl::DriverChannel>,
236 {
237 Offer::DriverTransport(self.build_offer())
238 }
239}