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