use crate::execution_scope::ExecutionScope;
use crate::node::{self, Node};
use crate::ProtocolsExt;
use fidl::endpoints::{ControlHandle, ProtocolMarker, RequestStream, ServerEnd};
use fidl::epitaph::ChannelEpitaphExt;
use fidl::{AsHandleRef, HandleBased};
use futures::future::BoxFuture;
use std::future::Future;
use std::sync::Arc;
use zx_status::Status;
use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
#[derive(Debug)]
pub struct ObjectRequest {
object_request: fidl::Channel,
what_to_send: ObjectRequestSend,
attributes: fio::NodeAttributesQuery,
create_attributes: Option<Box<fio::MutableNodeAttributes>>,
pub truncate: bool,
}
impl ObjectRequest {
pub(crate) fn new(
object_request: fidl::Channel,
what_to_send: ObjectRequestSend,
attributes: fio::NodeAttributesQuery,
create_attributes: Option<&fio::MutableNodeAttributes>,
truncate: bool,
) -> Self {
assert!(!object_request.is_invalid_handle());
let create_attributes = create_attributes.map(|a| Box::new(a.clone()));
Self { object_request, what_to_send, attributes, create_attributes, truncate }
}
pub fn new3(flags: fio::Flags, options: &fio::Options, object_request: fidl::Channel) -> Self {
ObjectRequest::new(
object_request,
if flags.get_representation() {
ObjectRequestSend::OnRepresentation
} else {
ObjectRequestSend::Nothing
},
options.attributes.unwrap_or(fio::NodeAttributesQuery::empty()),
options.create_attributes.as_ref(),
flags.is_truncate(),
)
}
pub(crate) fn what_to_send(&self) -> ObjectRequestSend {
self.what_to_send
}
pub fn attributes(&self) -> fio::NodeAttributesQuery {
self.attributes
}
pub fn create_attributes(&self) -> Option<&fio::MutableNodeAttributes> {
self.create_attributes.as_deref()
}
pub fn options(&self) -> fio::Options {
fio::Options {
attributes: (!self.attributes.is_empty()).then_some(self.attributes),
create_attributes: self
.create_attributes
.as_ref()
.map(|a| fio::MutableNodeAttributes::clone(&a)),
..Default::default()
}
}
pub async fn into_request_stream<T: Representation>(
self,
connection: &T,
) -> Result<<T::Protocol as ProtocolMarker>::RequestStream, Status> {
let stream = fio::NodeRequestStream::from_channel(fasync::Channel::from_channel(
self.object_request,
));
match self.what_to_send {
ObjectRequestSend::OnOpen => {
let control_handle = stream.control_handle();
let node_info = connection.node_info().await.map_err(|s| {
control_handle.shutdown_with_epitaph(s);
s
})?;
send_on_open(&stream.control_handle(), node_info)?;
}
ObjectRequestSend::OnRepresentation => {
let control_handle = stream.control_handle();
let representation =
connection.get_representation(self.attributes).await.map_err(|s| {
control_handle.shutdown_with_epitaph(s);
s
})?;
control_handle
.send_on_representation(representation)
.map_err(|_| Status::PEER_CLOSED)?;
}
ObjectRequestSend::Nothing => {}
}
Ok(stream.cast_stream())
}
pub fn into_server_end<T>(self) -> ServerEnd<T> {
ServerEnd::new(self.object_request)
}
pub fn into_channel(self) -> fidl::Channel {
self.object_request
}
pub fn into_channel_after_sending_on_open(
self,
node_info: fio::NodeInfoDeprecated,
) -> Result<fidl::Channel, Status> {
let stream = fio::NodeRequestStream::from_channel(fasync::Channel::from_channel(
self.object_request,
));
send_on_open(&stream.control_handle(), node_info)?;
let (inner, _is_terminated) = stream.into_inner();
Ok(Arc::try_unwrap(inner).unwrap().into_channel().into())
}
pub fn shutdown(self, status: Status) {
if self.object_request.is_invalid_handle() {
return;
}
if let ObjectRequestSend::OnOpen = self.what_to_send {
let (_, control_handle) = ServerEnd::<fio::NodeMarker>::new(self.object_request)
.into_stream_and_control_handle();
let _ = control_handle.send_on_open_(status.into_raw(), None);
control_handle.shutdown_with_epitaph(status);
} else {
let _ = self.object_request.close_with_epitaph(status);
}
}
pub fn handle<T>(
mut self,
f: impl FnOnce(ObjectRequestRef<'_>) -> Result<T, Status>,
) -> Option<T> {
match f(&mut self) {
Ok(o) => Some(o),
Err(s) => {
self.shutdown(s);
None
}
}
}
pub fn spawn<F, Fut>(self, scope: &ExecutionScope, f: F)
where
for<'a> F:
FnOnce(ObjectRequestRef<'a>) -> BoxFuture<'a, Result<Fut, Status>> + Send + 'static,
Fut: Future<Output = ()> + Send,
{
scope.spawn(async {
let fut = {
let mut this = self;
match f(&mut this).await {
Err(s) => {
this.shutdown(s);
return;
}
Ok(fut) => fut,
}
};
fut.await
});
}
pub async fn wait_till_ready(&self) -> bool {
if !matches!(self.what_to_send, ObjectRequestSend::Nothing) {
return true;
}
let signals = fasync::OnSignalsRef::new(
self.object_request.as_handle_ref(),
fidl::Signals::OBJECT_READABLE | fidl::Signals::CHANNEL_PEER_CLOSED,
)
.await
.unwrap();
signals.contains(fidl::Signals::OBJECT_READABLE)
}
pub fn take(&mut self) -> ObjectRequest {
assert!(!self.object_request.is_invalid_handle());
Self {
object_request: std::mem::replace(
&mut self.object_request,
fidl::Handle::invalid().into(),
),
what_to_send: self.what_to_send,
attributes: self.attributes,
create_attributes: self.create_attributes.take(),
truncate: self.truncate,
}
}
pub fn create_connection<N: Node, F: Future<Output = ()> + Send + 'static, P: ProtocolsExt>(
&mut self,
scope: ExecutionScope,
node: Arc<N>,
protocols: P,
f: impl FnOnce(ExecutionScope, Arc<N>, P, &mut Self) -> Result<F, Status>,
) -> Result<BoxFuture<'static, ()>, Status> {
assert!(!self.object_request.is_invalid_handle());
if protocols.is_node() {
Ok(Box::pin(node::Connection::create(scope, node, protocols, self)?))
} else {
Ok(Box::pin(f(scope, node, protocols, self)?))
}
}
pub fn spawn_connection<N: Node, F: Future<Output = ()> + Send + 'static, P: ProtocolsExt>(
&mut self,
scope: ExecutionScope,
node: Arc<N>,
protocols: P,
f: impl FnOnce(ExecutionScope, Arc<N>, P, &mut Self) -> Result<F, Status>,
) -> Result<(), Status> {
assert!(!self.object_request.is_invalid_handle());
if protocols.is_node() {
scope.spawn(node::Connection::create(scope.clone(), node, protocols, self)?);
} else {
scope.spawn(f(scope.clone(), node, protocols, self)?);
}
Ok(())
}
}
pub type ObjectRequestRef<'a> = &'a mut ObjectRequest;
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(dead_code)]
pub(crate) enum ObjectRequestSend {
OnOpen,
OnRepresentation,
Nothing,
}
pub trait Representation {
type Protocol: ProtocolMarker;
fn get_representation(
&self,
requested_attributes: fio::NodeAttributesQuery,
) -> impl Future<Output = Result<fio::Representation, Status>> + Send;
fn node_info(&self) -> impl Future<Output = Result<fio::NodeInfoDeprecated, Status>> + Send;
}
pub trait ToObjectRequest: ProtocolsExt {
fn to_object_request(&self, object_request: impl Into<fidl::Handle>) -> ObjectRequest;
}
impl ToObjectRequest for fio::OpenFlags {
fn to_object_request(&self, object_request: impl Into<fidl::Handle>) -> ObjectRequest {
ObjectRequest::new(
object_request.into().into(),
if self.contains(fio::OpenFlags::DESCRIBE) {
ObjectRequestSend::OnOpen
} else {
ObjectRequestSend::Nothing
},
fio::NodeAttributesQuery::empty(),
None,
self.is_truncate(),
)
}
}
impl ToObjectRequest for fio::Flags {
fn to_object_request(&self, object_request: impl Into<fidl::Handle>) -> ObjectRequest {
ObjectRequest::new3(*self, &Default::default(), object_request.into().into())
}
}
fn send_on_open(
control_handle: &fio::NodeControlHandle,
node_info: fio::NodeInfoDeprecated,
) -> Result<(), Status> {
control_handle
.send_on_open_(Status::OK.into_raw(), Some(node_info))
.map_err(|_| Status::PEER_CLOSED)
}