use std::process;
use parking_lot::Mutex;
use tokio::{net::TcpListener, sync::RwLock, task::LocalSet};
use tonic::transport::Server;
use crate::{Plugin, proto::plugin_server::PluginServer, store::ListItemStore};
pub(crate) struct ServerState<P> {
pub(crate) plugin: RwLock<Option<P>>,
pub(crate) list_item_store: Mutex<ListItemStore>,
}
impl<T: Plugin> ServerState<T> {
pub(crate) fn new_empty() -> Self {
Self {
plugin: RwLock::new(None),
list_item_store: Mutex::new(ListItemStore::new()),
}
}
}
pub fn run_server<T: Plugin>(plugin_id: &'static str) -> ! {
crate::PLUGIN_ID
.set(plugin_id)
.expect("plugin id should only be set from main");
let result = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| anyhow::anyhow!(e))
.and_then(|rt| {
let set = LocalSet::new();
let _guard = set.enter();
rt.block_on(set.run_until(async {
let listener = TcpListener::bind("[::1]:0").await?;
let port = listener.local_addr()?.port();
println!("{port}");
Server::builder()
.add_service(PluginServer::new(ServerState::<T>::new_empty()))
.serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
.await?;
Ok(())
}))
});
match result {
Ok(()) => process::exit(0),
Err(e) => {
print_error(&e);
process::exit(1)
}
}
}
fn print_error(e: &anyhow::Error) {
let err_string = e
.chain()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("\n");
eprintln!("{err_string}");
}