| // 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.") |
| } |
| } |
| } |
| } |