fdf_component/node/
offers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::marker::PhantomData;

use fidl::endpoints::{ServiceMarker, ServiceRequest};
use fidl_fuchsia_component_decl::{NameMapping, OfferService};
use fidl_fuchsia_driver_framework::Offer;
use fuchsia_component::server::{FidlServiceMember, ServiceFs, ServiceObjTrait};
use fuchsia_component::DEFAULT_SERVICE_INSTANCE;

/// A builder for creating [`Offer`]-compatible values for [`crate::NodeBuilder::add_offer`] that
/// service a zircon fidl service.
///
/// The methods on this that start with `add_` are helpers that will both register a service handler
/// with a [`ServiceFs`] and register the instance name in the structure. If you're handling adding
/// your service handlers to the outgoing directory yourself, you can just use the non-`add_`
/// methods to register them.
///
/// If no calls to add any instances are made, then when this is transformed into a service offer
/// it will be as if a single default instance with the default name was added.
pub struct ZirconServiceOffer<S> {
    service_name: String,
    instances: Vec<NameMapping>,
    _p: PhantomData<S>,
}

impl<S> ZirconServiceOffer<S> {
    /// Builds an offer for a zircon transport service based on the [`ServiceMarker`] for `S`.
    ///
    /// If the compiler can't deduce the type of `S` (which may be the case if you're not using the
    /// `add_` methods to add to a [`ServiceFs`] at the same time), you can use [`Self::new_marker`]
    /// to make it explicit.
    pub fn new() -> Self
    where
        S: ServiceMarker,
    {
        let service_name = S::SERVICE_NAME.to_owned();
        let instances = vec![];
        Self { service_name, instances, _p: PhantomData }
    }

    /// Builds an offer for a zircon transport service based on the given [`ServiceMarker`].
    ///
    /// This is mostly useful if the compiler can't derive the type of `S` on its own.
    pub fn new_marker(_marker: S) -> Self
    where
        S: ServiceMarker,
    {
        let service_name = S::SERVICE_NAME.to_owned();
        let instances = vec![];
        Self { service_name, instances, _p: PhantomData }
    }

    /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
    /// generator function `f`. The type of the service will be derived from the result of the
    /// generator function and it will be added with the name `name` which will be mapped to the
    /// default instance name to child components ([`DEFAULT_SERVICE_INSTANCE`]).
    pub fn add_default_named<O: ServiceObjTrait, F, SR>(
        self,
        fs: &mut ServiceFs<O>,
        name: impl Into<String>,
        f: F,
    ) -> Self
    where
        F: Fn(SR) -> O::Output,
        F: Clone,
        SR: ServiceRequest<Service = S>,
        FidlServiceMember<F, SR, O::Output>: Into<O>,
    {
        let name = name.into();
        fs.dir("svc").add_fidl_service_instance(name.clone(), f);
        self.named_default_instance(name)
    }

    /// Adds the given service instance to this offer and to the [`ServiceFs`] passed in, using the
    /// generator function `f`. The type of the service will be derived from the result of the
    /// generator function and it will be added with the name `name`.
    pub fn add_named<O: ServiceObjTrait, F, SR>(
        self,
        fs: &mut ServiceFs<O>,
        name: impl Into<String>,
        f: F,
    ) -> Self
    where
        F: Fn(SR) -> O::Output,
        F: Clone,
        SR: ServiceRequest<Service = S>,
        FidlServiceMember<F, SR, O::Output>: Into<O>,
    {
        let name = name.into();
        fs.dir("svc").add_fidl_service_instance(name.clone(), f);
        self.named_instance(name)
    }

    /// Adds the named instance as the `default` instance of this service offer (as specified
    /// by [`DEFAULT_SERVICE_INSTANCE`]). If you are only offering a single instance that is
    /// already called `default`, you do not need to call this.
    pub fn named_default_instance(mut self, name: impl Into<String>) -> Self {
        self.instances.push(NameMapping {
            source_name: name.into(),
            target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
        });
        self
    }

    /// Adds the named instance to the offer without mapping it to the default instance name.
    /// You can use this to add additional instances offered in your outgoing directory.
    pub fn named_instance(mut self, name: impl Into<String>) -> Self {
        let source_name = name.into();
        let target_name = source_name.clone();
        self.instances.push(NameMapping { source_name, target_name });
        self
    }

    /// Finalize the construction of the [`Offer`] object for use with
    /// [`super::NodeBuilder::add_offer`].
    pub fn build(self) -> Offer {
        // if no instances were added, assume there's a single default instance
        let mut instances = self.instances;
        if instances.is_empty() {
            instances.push(NameMapping {
                source_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
                target_name: DEFAULT_SERVICE_INSTANCE.to_owned(),
            });
        }
        let service = OfferService {
            source_name: Some(self.service_name.clone()),
            target_name: Some(self.service_name),
            renamed_instances: Some(instances),
            ..Default::default()
        };
        Offer::ZirconTransport(fidl_fuchsia_component_decl::Offer::Service(service))
    }
}