linera_sdk/service/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Types and macros useful for writing an application service.
5
6mod conversions_to_wit;
7#[cfg(not(with_testing))]
8mod runtime;
9#[cfg(with_testing)]
10mod test_runtime;
11#[doc(hidden)]
12pub mod wit;
13
14#[cfg(not(with_testing))]
15pub use self::runtime::ServiceRuntime;
16#[cfg(with_testing)]
17pub use self::test_runtime::MockServiceRuntime;
18#[doc(hidden)]
19pub use self::wit::export_service;
20use crate::util::BlockingWait as _;
21
22/// Inside tests, use the [`MockServiceRuntime`] instead of the real [`ServiceRuntime`].
23#[cfg(with_testing)]
24pub type ServiceRuntime<Application> = MockServiceRuntime<Application>;
25
26/// Declares an implementation of the [`Service`][`crate::Service`] trait, exporting it from the
27/// Wasm module.
28///
29/// Generates the necessary boilerplate for implementing the service WIT interface, exporting the
30/// necessary resource types and functions so that the host can call the application service.
31#[macro_export]
32macro_rules! service {
33    ($service:ident) => {
34        #[doc(hidden)]
35        static mut SERVICE: Option<$service> = None;
36
37        /// Export the service interface.
38        $crate::export_service!($service with_types_in $crate::service::wit);
39
40        /// Mark the service type to be exported.
41        impl $crate::service::wit::exports::linera::app::service_entrypoints::Guest for $service {
42            fn handle_query(argument: Vec<u8>) -> Vec<u8> {
43                use $crate::util::BlockingWait as _;
44                $crate::ServiceLogger::install();
45                let request = $crate::serde_json::from_slice(&argument)
46                    .unwrap_or_else(|_| panic!("Query {argument:?} is invalid and could not be deserialized"));
47                let response = $crate::service::run_async_entrypoint(
48                    unsafe { &mut SERVICE },
49                    move |service| service.handle_query(request).blocking_wait(),
50                );
51                $crate::serde_json::to_vec(&response)
52                    .expect("Failed to serialize query response")
53            }
54        }
55
56        /// Stub of a `main` entrypoint so that the binary doesn't fail to compile on targets other
57        /// than WebAssembly.
58        #[cfg(not(target_arch = "wasm32"))]
59        fn main() {}
60    };
61}
62
63/// Runs an asynchronous entrypoint in a blocking manner, by repeatedly polling the entrypoint
64/// future.
65pub fn run_async_entrypoint<Service, Output>(
66    service: &mut Option<Service>,
67    entrypoint: impl FnOnce(&mut Service) -> Output,
68) -> Output
69where
70    Service: crate::Service,
71{
72    let service =
73        service.get_or_insert_with(|| Service::new(ServiceRuntime::new()).blocking_wait());
74
75    entrypoint(service)
76}