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}