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 flex_client::AsyncChannel as Channel;
18use flex_client::fidl::RequestStream;
19use flex_fuchsia_io as fio;
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(|chan| crate::object_request::IntoAsyncChannel::into_async_channel(chan))
105 .ok()
106 } else {
107 Some(crate::object_request::IntoAsyncChannel::into_async_channel(
108 object_request.take().into_channel(),
109 ))
110 };
111 #[cfg(not(any(
112 fuchsia_api_level_at_least = "PLATFORM",
113 not(fuchsia_api_level_at_least = "NEXT")
114 )))]
115 let channel = Some(crate::object_request::IntoAsyncChannel::into_async_channel(
116 object_request.take().into_channel(),
117 ));
118
119 if let Some(channel) = channel {
120 (self.open)(scope, channel);
121 }
122 Ok(())
123 }
124}
125
126impl GetEntryInfo for Service {
127 fn entry_info(&self) -> EntryInfo {
128 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Service)
129 }
130}
131
132impl DirectoryEntry for Service {
133 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
134 request.open_service(self)
135 }
136}
137
138impl Node for Service {
139 async fn get_attributes(
140 &self,
141 requested_attributes: fio::NodeAttributesQuery,
142 ) -> Result<fio::NodeAttributes2, Status> {
143 Ok(immutable_attributes!(
144 requested_attributes,
145 Immutable {
146 protocols: fio::NodeProtocolKinds::CONNECTOR,
147 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::CONNECT,
148 }
149 ))
150 }
151}
152
153pub fn serve(
155 service: Arc<impl ServiceLike>,
156 scope: ExecutionScope,
157 protocols: &impl ProtocolsExt,
158 object_request: ObjectRequestRef<'_>,
159) -> Result<(), Status> {
160 if protocols.is_node() {
161 let options = protocols.to_node_options(service.entry_info().type_())?;
162 service.open_as_node(scope, options, object_request)
163 } else {
164 service.connect(scope, protocols.to_service_options()?, object_request)
165 }
166}