Skip to main content

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}