use std::path::{Component, Path, PathBuf};
use std::sync::OnceLock;
use lingxia_platform::traits::app_runtime::AppRuntime;
static APP_DATA_DIR: OnceLock<PathBuf> = OnceLock::new();
pub(crate) fn set_data_dir(path: PathBuf) {
let _ = APP_DATA_DIR.set(path);
}
pub fn product_name() -> Option<&'static str> {
lingxia_app_context::product_name()
}
pub fn product_version() -> Option<&'static str> {
lingxia_app_context::product_version()
}
pub fn lingxia_id() -> Option<&'static str> {
lingxia_app_context::lingxia_id()
}
pub fn notifications_enabled() -> bool {
lingxia_app_context::notifications_enabled()
}
fn data_dir() -> crate::Result<PathBuf> {
APP_DATA_DIR
.get()
.cloned()
.ok_or_else(|| crate::Error::internal("app data directory is not initialized"))
}
pub fn state_dir() -> crate::Result<PathBuf> {
Ok(lingxia_app_context::app_state_dir(&data_dir()?))
}
pub fn state_file(name: &str) -> crate::Result<PathBuf> {
state_file_in(data_dir()?, name)
}
pub fn state_dir_for(app: &crate::LxApp) -> PathBuf {
lingxia_app_context::app_state_dir(&app.user_data_dir)
}
pub fn state_file_for(app: &crate::LxApp, name: &str) -> crate::Result<PathBuf> {
state_file_in(&app.user_data_dir, name)
}
pub fn exit() -> crate::Result<()> {
let runtime = lxapp::get_platform()
.ok_or_else(|| crate::Error::internal("platform is not initialized"))?;
runtime.exit().map_err(Into::into)
}
fn state_file_in(root: impl AsRef<Path>, name: &str) -> crate::Result<PathBuf> {
validate_state_file_name(name)?;
Ok(lingxia_app_context::app_state_file(root.as_ref(), name))
}
fn validate_state_file_name(name: &str) -> crate::Result<()> {
let path = Path::new(name);
if name.trim().is_empty() {
return Err(crate::Error::invalid_request("state file name is empty"));
}
if path
.components()
.any(|component| !matches!(component, Component::Normal(_) | Component::CurDir))
{
return Err(crate::Error::invalid_request(
"state file name must be relative and stay inside the state directory",
));
}
Ok(())
}