blob: 8b457630919ea9aa5357b81f1b2dcbc20e4a1f7c [file] [log] [blame]
// Copyright 2022 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.
//! A server to handle calculator requests.
//!
//! This component (and the accompying parent realm) is a realistic example of
//! how to create & route client/server components in Fuchsia. It aims to be
//! fully fleshed out and showcase best practices such as:
//!
//! 1. Testing
//! 2. Exposing capabilities
//! 3. Well commented code
//! 4. FIDL interaction
//! 5. Error handling
use anyhow::Context;
use fidl_fuchsia_examples_calculator::{CalculatorRequest, CalculatorRequestStream};
use fuchsia_component::server::ServiceFs;
use fuchsia_inspect::component;
use fuchsia_inspect::health::Reporter;
use futures::prelude::*;
/// Wraps all hosted protocols into a single type that can be matched against
/// and dispatched.
enum IncomingRequest {
Calculator(CalculatorRequestStream),
}
/// Calculator server entry point.
#[fuchsia::main]
async fn main() -> Result<(), anyhow::Error> {
let mut service_fs = ServiceFs::new_local();
// Initialize inspect.
let _inspect_server_task = inspect_runtime::publish(
component::inspector(),
inspect_runtime::PublishOptions::default(),
);
component::health().set_starting_up();
service_fs.dir("svc").add_fidl_service(IncomingRequest::Calculator);
service_fs.take_and_serve_directory_handle().context("failed to serve outgoing namespace")?;
component::health().set_ok();
log::debug!("Initialized.");
service_fs
.for_each_concurrent(None, |request: IncomingRequest| async move {
match request {
IncomingRequest::Calculator(stream) => handle_calculator_request(stream)
.await
.expect("Could not handle calculator request."),
}
})
.await;
Ok(())
}
/// Handler for incoming service requests.
async fn handle_calculator_request(stream: CalculatorRequestStream) -> anyhow::Result<()> {
stream
.try_for_each(|request| async {
match request {
CalculatorRequest::Add { a, b, responder } => responder.send(a + b),
CalculatorRequest::Subtract { a, b, responder } => responder.send(a - b),
CalculatorRequest::Multiply { a, b, responder } => responder.send(a * b),
CalculatorRequest::Divide { dividend, divisor, responder } => {
responder.send(dividend / divisor)
}
CalculatorRequest::Pow { base, exponent, responder } => {
responder.send(base.powf(exponent))
}
}
})
.await
.expect("failed to serve calculator service.");
Ok(())
}
#[cfg(test)]
mod tests {
use super::handle_calculator_request;
use fidl::endpoints::create_proxy_and_stream;
use fidl_fuchsia_examples_calculator::CalculatorMarker;
use futures::FutureExt;
#[fuchsia::test]
async fn test_add() {
let (proxy, stream) = create_proxy_and_stream::<CalculatorMarker>();
let handle_calculator_request_task = handle_calculator_request(stream).fuse();
let proxy_task = proxy.add(4.5, 3.2).fuse();
futures::pin_mut!(handle_calculator_request_task, proxy_task);
futures::select! {
actual = proxy_task => {
let actual = actual.expect("Add proxy didn't return value.");
assert_eq!(actual, 7.7);
},
_ = handle_calculator_request_task => {
panic!("handle_calculator_request should never complete.")
}
}
}
#[fuchsia::test]
async fn test_subtract() {
let (proxy, stream) = create_proxy_and_stream::<CalculatorMarker>();
let handle_calculator_request_task = handle_calculator_request(stream).fuse();
let proxy_task = proxy.subtract(7.7, 3.2).fuse();
futures::pin_mut!(handle_calculator_request_task, proxy_task);
futures::select! {
actual = proxy_task => {
let actual = actual.expect("Subtract proxy didn't return value.");
assert_eq!(actual, 4.5);
},
_ = handle_calculator_request_task => {
panic!("handle_calculator_request should never complete.")
}
}
}
#[fuchsia::test]
async fn test_multiply() {
let (proxy, stream) = create_proxy_and_stream::<CalculatorMarker>();
let handle_calculator_request_task = handle_calculator_request(stream).fuse();
let proxy_task = proxy.multiply(1.5, 2.0).fuse();
futures::pin_mut!(handle_calculator_request_task, proxy_task);
futures::select! {
actual = proxy_task => {
let actual = actual.expect("Multiply proxy didn't return value.");
assert_eq!(actual, 3.0);
},
_ = handle_calculator_request_task => {
panic!("handle_calculator_request should never complete.")
}
}
}
#[fuchsia::test]
async fn test_divide() {
let (proxy, stream) = create_proxy_and_stream::<CalculatorMarker>();
let handle_calculator_request_task = handle_calculator_request(stream).fuse();
let proxy_task = proxy.divide(2.0, 4.0).fuse();
futures::pin_mut!(handle_calculator_request_task, proxy_task);
futures::select! {
actual = proxy_task => {
let actual = actual.expect("Divide proxy didn't return value.");
assert_eq!(actual, 0.5);
},
_ = handle_calculator_request_task => {
panic!("handle_calculator_request should never complete.")
}
}
}
#[fuchsia::test]
async fn test_pow() {
let (proxy, stream) = create_proxy_and_stream::<CalculatorMarker>();
let handle_calculator_request_task = handle_calculator_request(stream).fuse();
let proxy_task = proxy.pow(3.0, 4.0).fuse();
futures::pin_mut!(handle_calculator_request_task, proxy_task);
futures::select! {
actual = proxy_task => {
let actual = actual.expect("Pow proxy didn't return value.");
assert_eq!(actual, 81.0);
},
_ = handle_calculator_request_task => {
panic!("handle_calculator_request should never complete.")
}
}
}
}