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}