blob: 4718e675746c39bf9b2f3744f222fe0bcdf7a181 [file] [log] [blame]
// Copyright 2025 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_next::{Request, Responder, ServerSender};
use fidl_next_fuchsia_examples_calculator::{
Calculator, CalculatorAddResponse, CalculatorDivideResponse, CalculatorMultiplyResponse,
CalculatorPowResponse, CalculatorServerHandler, CalculatorSubtractResponse, calculator,
};
use fuchsia_component::server::ServiceFs;
use fuchsia_inspect::component;
use fuchsia_inspect::health::Reporter;
use futures::prelude::*;
#[derive(Clone)]
pub struct CalculatorServer;
impl CalculatorServerHandler<fidl_next::fuchsia::zx::Channel> for CalculatorServer {
async fn add(
&mut self,
sender: &ServerSender<Calculator>,
request: Request<calculator::Add>,
responder: Responder<calculator::Add>,
) {
responder
.respond(sender, CalculatorAddResponse { sum: *request.a + *request.b })
.unwrap()
.await
.unwrap();
}
async fn subtract(
&mut self,
sender: &ServerSender<Calculator>,
request: Request<calculator::Subtract>,
responder: Responder<calculator::Subtract>,
) {
responder
.respond(sender, CalculatorSubtractResponse { difference: *request.a - *request.b })
.unwrap()
.await
.unwrap();
}
async fn multiply(
&mut self,
sender: &ServerSender<Calculator>,
request: Request<calculator::Multiply>,
responder: Responder<calculator::Multiply>,
) {
responder
.respond(sender, CalculatorMultiplyResponse { product: *request.a * *request.b })
.unwrap()
.await
.unwrap();
}
async fn divide(
&mut self,
sender: &ServerSender<Calculator>,
request: Request<calculator::Divide>,
responder: Responder<calculator::Divide>,
) {
responder
.respond(
sender,
CalculatorDivideResponse { quotient: *request.dividend / *request.divisor },
)
.unwrap()
.await
.unwrap();
}
async fn pow(
&mut self,
sender: &ServerSender<Calculator>,
request: Request<calculator::Pow>,
responder: Responder<calculator::Pow>,
) {
responder
.respond(sender, CalculatorPowResponse { power: request.base.powf(*request.exponent) })
.unwrap()
.await
.unwrap();
}
}
/// 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_next_protocol::<Calculator, _>(CalculatorServer);
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, |()| async {}).await;
Ok(())
}
#[cfg(test)]
mod tests {
use crate::CalculatorServer;
use fidl_next::Server;
use fidl_next::fuchsia::{create_channel, spawn_client_sender_detached};
use fidl_next_fuchsia_examples_calculator::Calculator;
use futures::FutureExt;
use std::pin::pin;
#[fuchsia::test]
async fn test_add() {
let (client_end, server_end) = create_channel::<Calculator>();
let sender = spawn_client_sender_detached(client_end);
let server_task = pin!(async move { Server::new(server_end).run(CalculatorServer).await });
let future = sender.add(4.5, 3.2).unwrap();
futures::select! {
actual = future.fuse() => {
let actual = actual.expect("Add proxy didn't return value.");
assert_eq!(actual.sum, 7.7);
},
_ = server_task.fuse() => {
panic!("server should never complete.")
}
}
}
#[fuchsia::test]
async fn test_subtract() {
let (client_end, server_end) = create_channel::<Calculator>();
let sender = spawn_client_sender_detached(client_end);
let server_task = pin!(async move { Server::new(server_end).run(CalculatorServer).await });
let future = sender.subtract(7.7, 3.2).unwrap();
futures::select! {
actual = future.fuse() => {
let actual = actual.expect("Subtract proxy didn't return value.");
assert_eq!(actual.difference, 4.5);
},
_ = server_task.fuse() => {
panic!("server should never complete.")
}
}
}
#[fuchsia::test]
async fn test_multiply() {
let (client_end, server_end) = create_channel::<Calculator>();
let sender = spawn_client_sender_detached(client_end);
let server_task = pin!(async move { Server::new(server_end).run(CalculatorServer).await });
let future = sender.multiply(1.5, 2.0).unwrap();
futures::select! {
actual = future.fuse() => {
let actual = actual.expect("Multiply proxy didn't return value.");
assert_eq!(actual.product, 3.0);
},
_ = server_task.fuse() => {
panic!("server should never complete.")
}
}
}
#[fuchsia::test]
async fn test_divide() {
let (client_end, server_end) = create_channel::<Calculator>();
let sender = spawn_client_sender_detached(client_end);
let server_task = pin!(async move { Server::new(server_end).run(CalculatorServer).await });
let future = sender.divide(2.0, 4.0).unwrap();
futures::select! {
actual = future.fuse() => {
let actual = actual.expect("Divide proxy didn't return value.");
assert_eq!(actual.quotient, 0.5);
},
_ = server_task.fuse() => {
panic!("server should never complete.")
}
}
}
#[fuchsia::test]
async fn test_pow() {
let (client_end, server_end) = create_channel::<Calculator>();
let sender = spawn_client_sender_detached(client_end);
let server_task = pin!(async move { Server::new(server_end).run(CalculatorServer).await });
let future = sender.pow(3.0, 4.0).unwrap();
futures::select! {
actual = future.fuse() => {
let actual = actual.expect("Pow proxy didn't return value.");
assert_eq!(actual.power, 81.0);
},
_ = server_task.fuse() => {
panic!("server should never complete.")
}
}
}
}