lingxia 0.6.5

LingXia - Cross-platform LxApp (lightweight application) framework for Android, iOS, and HarmonyOS
//! Host app metadata, state paths, and app-level lifecycle helpers.
//!
//! These APIs describe the native host application, not an individual LxApp
//! page. They are intended for Rust native code built with LingXia, including
//! code generated by `lingxia::native`.
//!
//! Common uses:
//!
//! - read product metadata such as `product_version` and `lingxia_id`;
//! - resolve host-owned state paths such as `state_dir` and `state_file`;
//! - request host app termination with `exit`.

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);
}

/// Returns the host app's display name from the initialized app config.
pub fn product_name() -> Option<&'static str> {
    lingxia_app_context::product_name()
}

/// Returns the host app version from the initialized app config.
pub fn product_version() -> Option<&'static str> {
    lingxia_app_context::product_version()
}

/// Returns the configured LingXia host identifier, if any.
pub fn lingxia_id() -> Option<&'static str> {
    lingxia_app_context::lingxia_id()
}

/// Reports whether push-notification capability is enabled for the host app.
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"))
}

/// Returns the host-owned state directory.
pub fn state_dir() -> crate::Result<PathBuf> {
    Ok(lingxia_app_context::app_state_dir(&data_dir()?))
}

/// Returns the full path to a file inside the host-owned state directory.
pub fn state_file(name: &str) -> crate::Result<PathBuf> {
    state_file_in(data_dir()?, name)
}

/// Returns the state directory for a specific running [`crate::LxApp`].
pub fn state_dir_for(app: &crate::LxApp) -> PathBuf {
    lingxia_app_context::app_state_dir(&app.user_data_dir)
}

/// Returns a file path inside an [`crate::LxApp`]'s state directory.
pub fn state_file_for(app: &crate::LxApp, name: &str) -> crate::Result<PathBuf> {
    state_file_in(&app.user_data_dir, name)
}

/// Requests host app termination through the active platform runtime.
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(())
}