blob: 3ced5ce8c8d33f9a450ad0a2dd84f1d2755e0f1f [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! The `Service` trait and its trait-object wrappers.
use {
fidl::endpoints::{RequestStream, UnifiedServiceRequest},
fuchsia_async as fasync, fuchsia_zircon as zx,
std::marker::PhantomData,
};
/// `Service` connects channels to service instances.
///
/// Note that this trait is implemented by the `FidlService` type.
pub trait Service {
/// The type of the value yielded by the `spawn_service` callback.
type Output;
/// Create a new instance of the service on the provided `zx::Channel`.
///
/// The value returned by this function will be yielded from the stream
/// output of the `ServiceFs` type.
fn connect(&mut self, channel: zx::Channel) -> Option<Self::Output>;
/// Create a new instance of the service on the provided `zx::Channel`.
/// The service is hosted at the given `path`.
///
/// The value returned by this function will be yielded from the stream
/// output of the `ServiceFs` type.
fn connect_at(&mut self, _path: &str, channel: zx::Channel) -> Option<Self::Output> {
self.connect(channel)
}
}
impl<F, O> Service for F
where
F: FnMut(zx::Channel) -> Option<O>,
{
type Output = O;
fn connect(&mut self, channel: zx::Channel) -> Option<Self::Output> {
(self)(channel)
}
}
/// A wrapper for functions from `RequestStream` to `Output` which implements
/// `Service`.
///
/// This type throws away channels that cannot be converted to the appropriate
/// `RequestStream` type.
pub struct FidlService<F, RS, Output>
where
F: FnMut(RS) -> Output,
RS: RequestStream,
{
f: F,
marker: PhantomData<fn(RS) -> Output>,
}
impl<F, RS, Output> From<F> for FidlService<F, RS, Output>
where
F: FnMut(RS) -> Output,
RS: RequestStream,
{
fn from(f: F) -> Self {
Self { f, marker: PhantomData }
}
}
impl<F, RS, Output> Service for FidlService<F, RS, Output>
where
F: FnMut(RS) -> Output,
RS: RequestStream,
{
type Output = Output;
fn connect(&mut self, channel: zx::Channel) -> Option<Self::Output> {
match fasync::Channel::from_channel(channel) {
Ok(chan) => Some((self.f)(RS::from_channel(chan))),
Err(e) => {
eprintln!("ServiceFs failed to convert channel to fasync channel: {:?}", e);
None
}
}
}
}
/// A wrapper for functions from `UnifiedServiceRequest` to `Output` which implements
/// `Service`.
///
/// This type throws away channels that cannot be converted to the appropriate
/// `UnifiedServiceRequest` type.
pub struct FidlServiceMember<F, USR, Output>
where
F: FnMut(USR) -> Output,
USR: UnifiedServiceRequest,
{
f: F,
marker: PhantomData<fn(USR) -> Output>,
}
impl<F, USR, Output> From<F> for FidlServiceMember<F, USR, Output>
where
F: FnMut(USR) -> Output,
USR: UnifiedServiceRequest,
{
fn from(f: F) -> Self {
Self { f, marker: PhantomData }
}
}
impl<F, USR, Output> Service for FidlServiceMember<F, USR, Output>
where
F: FnMut(USR) -> Output,
USR: UnifiedServiceRequest,
{
type Output = Output;
fn connect(&mut self, _channel: zx::Channel) -> Option<Self::Output> {
// FidlServiceMember needs to know which member to dispatch to.
unimplemented!();
}
fn connect_at(&mut self, path: &str, channel: zx::Channel) -> Option<Self::Output> {
match fasync::Channel::from_channel(channel) {
Ok(chan) => Some((self.f)(USR::dispatch(path, chan))),
Err(e) => {
eprintln!("ServiceFs failed to convert channel to fasync channel: {:?}", e);
None
}
}
}
}
/// A `!Send` (non-thread-safe) trait object encapsulating a `Service` with
/// the given `Output` type.
///
/// Types which implement the `Service` trait can be converted to objects of
/// this type via the `From`/`Into` traits.
pub struct ServiceObjLocal<'a, Output>(Box<dyn Service<Output = Output> + 'a>);
impl<'a, S: Service + 'a> From<S> for ServiceObjLocal<'a, S::Output> {
fn from(service: S) -> Self {
ServiceObjLocal(Box::new(service))
}
}
/// A thread-safe (`Send`) trait object encapsulating a `Service` with
/// the given `Output` type.
///
/// Types which implement the `Service` trait and the `Send` trait can
/// be converted to objects of this type via the `From`/`Into` traits.
pub struct ServiceObj<'a, Output>(Box<dyn Service<Output = Output> + Send + 'a>);
impl<'a, S: Service + Send + 'a> From<S> for ServiceObj<'a, S::Output> {
fn from(service: S) -> Self {
ServiceObj(Box::new(service))
}
}
/// A trait implemented by both `ServiceObj` and `ServiceObjLocal` that
/// allows code to be generic over thread-safety.
///
/// Code that uses `ServiceObj` will require `Send` bounds but will be
/// multithreaded-capable, while code that uses `ServiceObjLocal` will
/// allow non-`Send` types but will be restricted to singlethreaded
/// executors.
pub trait ServiceObjTrait {
/// The output type of the underlying `Service`.
type Output;
/// Get a mutable reference to the underlying `Service` trait object.
fn service(&mut self) -> &mut dyn Service<Output = Self::Output>;
}
impl<'a, Output> ServiceObjTrait for ServiceObjLocal<'a, Output> {
type Output = Output;
fn service(&mut self) -> &mut dyn Service<Output = Self::Output> {
&mut *self.0
}
}
impl<'a, Output> ServiceObjTrait for ServiceObj<'a, Output> {
type Output = Output;
fn service(&mut self) -> &mut dyn Service<Output = Self::Output> {
&mut *self.0
}
}