use std::path::PathBuf;
use std::sync::OnceLock;
pub fn family_name() -> Option<&'static str> {
static NAME: OnceLock<Option<String>> = OnceLock::new();
NAME.get_or_init(query_family_name).as_deref()
}
pub fn is_packaged() -> bool {
family_name().is_some()
}
pub fn local_state() -> Option<PathBuf> {
let family = family_name()?;
let local = std::env::var_os("LOCALAPPDATA")?;
Some(
PathBuf::from(local)
.join("Packages")
.join(family)
.join("LocalState"),
)
}
#[cfg(windows)]
fn query_family_name() -> Option<String> {
use windows_sys::Win32::Foundation::{
APPMODEL_ERROR_NO_PACKAGE, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS,
};
use windows_sys::Win32::Storage::Packaging::Appx::GetCurrentPackageFamilyName;
let mut length = 0u32;
let result = unsafe { GetCurrentPackageFamilyName(&mut length, std::ptr::null_mut()) };
if result == APPMODEL_ERROR_NO_PACKAGE || result != ERROR_INSUFFICIENT_BUFFER || length == 0 {
return None;
}
let mut buffer = vec![0u16; length as usize];
let result = unsafe { GetCurrentPackageFamilyName(&mut length, buffer.as_mut_ptr()) };
if result != ERROR_SUCCESS {
return None;
}
Some(String::from_utf16_lossy(
&buffer[..(length as usize).saturating_sub(1)],
))
}
#[cfg(not(windows))]
fn query_family_name() -> Option<String> {
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tests_do_not_run_from_a_package() {
assert!(!is_packaged());
assert_eq!(local_state(), None);
}
}