use std::sync::{Arc, OnceLock};
use jsonrpsee::{RpcModule, core::RegisterMethodError};
use thruput_json_rpc_api::native::NativeApi;
use tracing::Instrument;
pub struct NativeModule<S> {
module: RpcModule<S>,
names: Arc<OnceLock<Vec<String>>>,
}
impl<S> std::fmt::Debug for NativeModule<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NativeModule")
.field("methods", &self.module.method_names().collect::<Vec<_>>())
.finish_non_exhaustive()
}
}
macro_rules! parse {
(empty, $request:ty, $params:expr) => {
crate::params::parse_empty($params)
};
(named, $request:ty, $params:expr) => {
crate::params::parse_named::<$request>($params)
};
}
macro_rules! decorate {
(plain, $result:expr, $names:expr) => {{
let _ = $names;
$result
}};
(version, $result:expr, $names:expr) => {{
let mut result = $result;
result.supported_methods = $names
.get()
.expect("bug: native module served before finalization")
.clone();
result
}};
}
impl<S: NativeApi> NativeModule<S> {
pub fn new(service: S) -> Result<Self, RegisterMethodError> {
let mut module = RpcModule::new(service);
let names = Arc::new(OnceLock::<Vec<String>>::new());
macro_rules! register {
($( $(#[$doc:meta])* $name:ident($wire:literal, $parse:ident, $decorate:ident): $request:ty => $response:ty; )*) => {
$({
let names = names.clone();
module.register_async_method($wire, move |params, service, _| {
let names = names.clone();
async move {
let request = parse!($parse, $request, ¶ms)?;
let result = service.$name(request).await?;
Ok::<$response, jsonrpsee::types::ErrorObjectOwned>(decorate!($decorate, result, names))
}.instrument(tracing::info_span!("native_rpc", method = $wire))
})?;
})*
};
}
thruput_json_rpc_api::native_methods!(register);
Ok(Self { module, names })
}
pub fn remove_method(&mut self, name: &'static str) -> bool {
self.module.remove_method(name).is_some()
}
pub fn finalize(self) -> FinalizedNativeModule<S> {
let mut names: Vec<_> = self.module.method_names().map(str::to_owned).collect();
names.sort_unstable();
self.names
.set(names.clone())
.expect("bug: native module finalized twice");
FinalizedNativeModule {
module: self.module,
names,
}
}
}
pub struct FinalizedNativeModule<S> {
module: RpcModule<S>,
names: Vec<String>,
}
impl<S> std::fmt::Debug for FinalizedNativeModule<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FinalizedNativeModule")
.field("supported_methods", &self.names)
.finish_non_exhaustive()
}
}
impl<S> FinalizedNativeModule<S> {
pub fn supported_methods(&self) -> &[String] {
&self.names
}
pub fn into_rpc(self) -> RpcModule<S> {
self.module
}
}
pub mod read;
pub mod transaction;
pub mod service;
pub mod funding;