#![feature(async_closure)]
#![feature(lazy_cell)]
use anyhow::Result;
use axum::{
extract::State,
routing::{get, post},
Extension, Json, Router,
};
use config::{ConfigManager, FsConfigManager};
use generated::{
FeatureUsecase, GetFeatureStatusRequest, GetFeatureStatusResponse, GetFeaturesResponse,
ToggleFeatureRequest, ToggleFeatureResponse, WallpaperUsecase,
};
use serde_json::Value;
use std::{
net::SocketAddr,
path::{Path, PathBuf},
sync::Arc,
};
use tokio::net::TcpListener;
use tracing::{error, info, level_filters::LevelFilter, Level};
use crate::{
feature::{FeatureManager, FeatureManagerImpl},
features::FeatureUsecaseImpl,
generated::Config,
};
#[cfg(target_os = "macos")]
mod laptoplid;
#[cfg(target_os = "macos")]
use crate::laptoplid::{turn_off_laptoplid_change, turn_on_laptoplid_change};
mod config;
mod feature;
mod features;
mod generated;
#[derive(Clone)]
struct AppState {
feature_usecase: Arc<dyn FeatureUsecase + Sync + Send>,
}
impl AppState {
fn new(feature_usecase: Arc<dyn FeatureUsecase + Sync + Send>) -> Self {
Self { feature_usecase }
}
}
#[tokio::main]
async fn main() -> Result<()> {
let config_dir = config::get_config_dir();
config::init_dir(&config_dir)?;
let log_file = config_dir.join("log");
let _guard = assistant_common::setup_logging(Some(log_file))?;
let cfg_mngr: Arc<FsConfigManager<serde_json::Value>> =
Arc::new(FsConfigManager::new(config_dir, "settings.json".to_owned()));
let ft_mngt = Arc::new(FeatureManagerImpl::new());
let wp_usecase = Arc::new(features::WallpaperUsecaseImpl::new());
ft_mngt.register(wp_usecase).await;
let sshmp_usecase = Arc::new(features::SshpmUsecaseImpl::new());
ft_mngt.register(sshmp_usecase).await;
#[cfg(target_os = "windows")]
{
let wslpf_usecase = Arc::new(features::WslpfUsecaseImpl::new());
ft_mngt.register(wslpf_usecase).await;
}
let ft_mngt1 = ft_mngt.clone();
cfg_mngr.on(Arc::new(move |oldc, newc| {
let ft_mngt2 = ft_mngt1.clone();
Box::pin(async move {
#[cfg(target_os = "macos")]
match newc
.get("detect_lidchange")
.unwrap_or(&Value::Bool(false))
.as_bool()
{
Some(should_listen) => {
if should_listen {
match turn_on_laptoplid_change(
ft_mngt2.clone(),
tokio::runtime::Handle::current(),
) {
Ok(_) => {}
Err(err) => {
error!("failed to turn on laptoplid change detection: {}", err);
}
}
} else {
match turn_off_laptoplid_change() {
Ok(_) => {}
Err(err) => {
error!("failed to turn off laptoplid change detection: {}", err);
}
}
}
}
None => {}
}
ft_mngt2.update_config(oldc, newc).await;
})
}));
cfg_mngr
.init(Some(serde_json::to_value(Config::default())?))
.await?;
cfg_mngr.set_fs_watch(true).await?;
let feature_usecase = Arc::new(FeatureUsecaseImpl::new(cfg_mngr));
let app_state = AppState::new(feature_usecase);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(&addr).await?;
info!("server is listening on port {}", addr);
let app = Router::new()
.route("/toggle_feature", post(toggle_feature_handler))
.route("/get_features", get(get_features_handler))
.route("/get_feature_status", post(get_feature_status_handler))
.with_state(app_state);
axum::serve(listener, app).await.unwrap();
Ok(())
}
async fn toggle_feature_handler(
State(state): State<AppState>,
Json(request): Json<ToggleFeatureRequest>,
) -> Json<ToggleFeatureResponse> {
let response = state.feature_usecase.toggle_feature(request).await.unwrap();
Json(response)
}
async fn get_features_handler(State(state): State<AppState>) -> Json<GetFeaturesResponse> {
let response = state.feature_usecase.get_features().await.unwrap();
Json(response)
}
async fn get_feature_status_handler(
State(state): State<AppState>,
Json(request): Json<GetFeatureStatusRequest>,
) -> Json<GetFeatureStatusResponse> {
let response = state
.feature_usecase
.get_feature_status(request)
.await
.unwrap();
Json(response)
}