use crate::models::*;
use crate::Result;
use axum::Router;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Mutex;
use tauri::{plugin::PluginApi, AppHandle, Manager, Runtime};
use tokio::net::TcpListener;
use tokio_util::sync::CancellationToken;
use tower_http::services::ServeDir;
pub fn init<R: Runtime, C: DeserializeOwned>(app: &AppHandle<R>, _api: PluginApi<R, C>) -> Result<Httpd<R>> {
Ok(Httpd { app_handle: app.clone(), services: Mutex::new(Vec::new()), controllers: Mutex::new(HashMap::new()) })
}
pub struct Httpd<R: Runtime> {
app_handle: AppHandle<R>,
services: Mutex<Vec<Service>>,
controllers: Mutex<HashMap<String, CancellationToken>>,
}
impl<R: Runtime> Httpd<R> {
pub async fn listen(&self, label: &str, port: u16, router: Router) -> Result<()> {
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let listener = TcpListener::bind(addr).await?;
let token = CancellationToken::new();
let cloned_token = token.clone();
self.controllers.lock().unwrap().insert(label.to_string(), token);
tauri::async_runtime::spawn(async move {
axum::serve(listener, router)
.with_graceful_shutdown(async move {
cloned_token.cancelled().await;
})
.await
.unwrap();
});
self.services.lock().unwrap().push(Service { label: label.to_string(), port });
Ok(())
}
pub async fn listen_static(&self, label: &str, port: u16, root: &str) -> Result<()> {
let parsed_root = self.app_handle.path().parse(&root)?;
let router = Router::new().fallback_service(ServeDir::new(parsed_root));
self.listen(label, port, router).await?;
Ok(())
}
pub fn get_services(&self) -> Result<Vec<Service>> {
Ok(self.services.lock().unwrap().clone())
}
pub fn stop_service(&self, label: &str) -> crate::Result<()> {
if let Some(token) = self.controllers.lock().unwrap().remove(label) {
token.cancel();
self.services.lock().unwrap().retain(|s| s.label != label);
Ok(())
} else {
Err(crate::Error::ServiceNotFound(label.to_string()))
}
}
}