1#[cfg(test)]
8mod tests;
9
10use crate::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
11use crate::execution_scope::ExecutionScope;
12use crate::node::Node;
13use crate::object_request::{ObjectRequestRef, ObjectRequestSend};
14use crate::{immutable_attributes, ProtocolsExt};
15use fidl::endpoints::RequestStream;
16use fidl_fuchsia_io as fio;
17use fuchsia_async::Channel;
18use futures::future::Future;
19use std::sync::Arc;
20use zx_status::Status;
21
22pub trait ServiceLike: Node {
24 fn connect(
26 &self,
27 scope: ExecutionScope,
28 options: ServiceOptions,
29 object_request: ObjectRequestRef<'_>,
30 ) -> Result<(), Status>;
31}
32
33pub struct ServiceOptions;
34
35pub fn host<ServerRequestStream, CreateServer, Task>(create_server: CreateServer) -> Arc<Service>
48where
49 ServerRequestStream: RequestStream,
50 CreateServer: Fn(ServerRequestStream) -> Task + Send + Sync + 'static,
51 Task: Future<Output = ()> + Send + 'static,
52{
53 endpoint(move |scope, channel| {
54 let requests = RequestStream::from_channel(channel);
55 let task = create_server(requests);
56 let _ = scope.spawn(task);
59 })
60}
61
62pub fn endpoint<Open>(open: Open) -> Arc<Service>
71where
72 Open: Fn(ExecutionScope, Channel) + Send + Sync + 'static,
73{
74 Arc::new(Service { open: Box::new(open) })
75}
76
77pub struct Service {
83 open: Box<dyn Fn(ExecutionScope, Channel) + Send + Sync>,
84}
85
86impl ServiceLike for Service {
87 fn connect(
88 &self,
89 scope: ExecutionScope,
90 _options: ServiceOptions,
91 object_request: ObjectRequestRef<'_>,
92 ) -> Result<(), Status> {
93 if object_request.what_to_send() == ObjectRequestSend::OnOpen {
94 if let Ok(channel) = object_request
95 .take()
96 .into_channel_after_sending_on_open(fio::NodeInfoDeprecated::Service(fio::Service))
97 .map(Channel::from_channel)
98 {
99 (self.open)(scope, channel);
100 }
101 } else {
102 let channel = Channel::from_channel(object_request.take().into_channel());
103 (self.open)(scope, channel);
104 }
105 Ok(())
106 }
107}
108
109impl GetEntryInfo for Service {
110 fn entry_info(&self) -> EntryInfo {
111 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Service)
112 }
113}
114
115impl DirectoryEntry for Service {
116 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
117 request.open_service(self)
118 }
119}
120
121impl Node for Service {
122 async fn get_attributes(
123 &self,
124 requested_attributes: fio::NodeAttributesQuery,
125 ) -> Result<fio::NodeAttributes2, Status> {
126 Ok(immutable_attributes!(
127 requested_attributes,
128 Immutable {
129 protocols: fio::NodeProtocolKinds::CONNECTOR,
130 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::CONNECT,
131 }
132 ))
133 }
134}
135
136pub fn serve(
138 service: Arc<impl ServiceLike>,
139 scope: ExecutionScope,
140 protocols: &impl ProtocolsExt,
141 object_request: ObjectRequestRef<'_>,
142) -> Result<(), Status> {
143 if protocols.is_node() {
144 let options = protocols.to_node_options(service.entry_info().type_())?;
145 service.open_as_node(scope, options, object_request)
146 } else {
147 service.connect(scope, protocols.to_service_options()?, object_request)
148 }
149}