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;
15#[cfg(any(fuchsia_api_level_at_least = "PLATFORM", not(fuchsia_api_level_at_least = "NEXT")))]
16use crate::object_request::ObjectRequestSend;
17use fidl::endpoints::RequestStream;
18use fidl_fuchsia_io as fio;
19use fuchsia_async::Channel;
20use futures::future::Future;
21use std::sync::Arc;
22use zx_status::Status;
23
24pub trait ServiceLike: Node {
26 fn connect(
28 &self,
29 scope: ExecutionScope,
30 options: ServiceOptions,
31 object_request: ObjectRequestRef<'_>,
32 ) -> Result<(), Status>;
33}
34
35#[derive(Default)]
36pub struct ServiceOptions;
37
38pub fn host<ServerRequestStream, CreateServer, Task>(create_server: CreateServer) -> Arc<Service>
51where
52 ServerRequestStream: RequestStream,
53 CreateServer: Fn(ServerRequestStream) -> Task + Send + Sync + 'static,
54 Task: Future<Output = ()> + Send + 'static,
55{
56 endpoint(move |scope, channel| {
57 let requests = RequestStream::from_channel(channel);
58 let task = create_server(requests);
59 let _ = scope.spawn(task);
62 })
63}
64
65pub fn endpoint<Open>(open: Open) -> Arc<Service>
74where
75 Open: Fn(ExecutionScope, Channel) + Send + Sync + 'static,
76{
77 Arc::new(Service { open: Box::new(open) })
78}
79
80pub struct Service {
86 open: Box<dyn Fn(ExecutionScope, Channel) + Send + Sync>,
87}
88
89impl ServiceLike for Service {
90 fn connect(
91 &self,
92 scope: ExecutionScope,
93 _options: ServiceOptions,
94 object_request: ObjectRequestRef<'_>,
95 ) -> Result<(), Status> {
96 #[cfg(any(
97 fuchsia_api_level_at_least = "PLATFORM",
98 not(fuchsia_api_level_at_least = "NEXT")
99 ))]
100 let channel = if object_request.what_to_send() == ObjectRequestSend::OnOpen {
101 object_request
102 .take()
103 .into_channel_after_sending_on_open(fio::NodeInfoDeprecated::Service(fio::Service))
104 .map(Channel::from_channel)
105 .ok()
106 } else {
107 Some(Channel::from_channel(object_request.take().into_channel()))
108 };
109 #[cfg(not(any(
110 fuchsia_api_level_at_least = "PLATFORM",
111 not(fuchsia_api_level_at_least = "NEXT")
112 )))]
113 let channel = Some(Channel::from_channel(object_request.take().into_channel()));
114
115 if let Some(channel) = channel {
116 (self.open)(scope, channel);
117 }
118 Ok(())
119 }
120}
121
122impl GetEntryInfo for Service {
123 fn entry_info(&self) -> EntryInfo {
124 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Service)
125 }
126}
127
128impl DirectoryEntry for Service {
129 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
130 request.open_service(self)
131 }
132}
133
134impl Node for Service {
135 async fn get_attributes(
136 &self,
137 requested_attributes: fio::NodeAttributesQuery,
138 ) -> Result<fio::NodeAttributes2, Status> {
139 Ok(immutable_attributes!(
140 requested_attributes,
141 Immutable {
142 protocols: fio::NodeProtocolKinds::CONNECTOR,
143 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::CONNECT,
144 }
145 ))
146 }
147}
148
149pub fn serve(
151 service: Arc<impl ServiceLike>,
152 scope: ExecutionScope,
153 protocols: &impl ProtocolsExt,
154 object_request: ObjectRequestRef<'_>,
155) -> Result<(), Status> {
156 if protocols.is_node() {
157 let options = protocols.to_node_options(service.entry_info().type_())?;
158 service.open_as_node(scope, options, object_request)
159 } else {
160 service.connect(scope, protocols.to_service_options()?, object_request)
161 }
162}