use crate::compat::{Mutex, OnceLock, String};
use crate::core::Color;
use crate::platform::Platform;
#[cfg_attr(alloc_frugal, allow(dead_code))]
fn cache() -> &'static Mutex<crate::compat::HashMap<&'static str, &'static str>> {
static CACHE: OnceLock<Mutex<crate::compat::HashMap<&'static str, &'static str>>> =
OnceLock::new();
CACHE.get_or_init(|| Mutex::new(crate::compat::HashMap::new()))
}
#[cfg_attr(alloc_frugal, allow(dead_code))]
pub(crate) fn intern(value: Option<String>) -> Option<&'static str> {
let value = value?;
if value.is_empty() {
return None;
}
let mut guard = crate::compat::lock(cache());
if let Some(existing) = guard.get(value.as_str()) {
return Some(*existing);
}
let leaked: &'static str = crate::compat::Box::leak(value.into_boxed_str());
guard.insert(leaked, leaked);
Some(leaked)
}
#[cfg(not(alloc_frugal))]
#[cfg_attr(not(target_vendor = "apple"), allow(dead_code))]
pub(crate) fn from_environment() -> Option<String> {
interned_from_environment().map(String::from)
}
#[cfg(not(alloc_frugal))]
#[cfg_attr(not(any(unix_desktop, target_vendor = "apple")), allow(dead_code))]
pub(crate) fn interned_from_environment() -> Option<&'static str> {
static CACHE: crate::platform::environment_cache::EnvironmentFactCache<Option<&'static str>> =
OnceLock::new();
crate::platform::environment_cache::cached_environment_fact(&CACHE, || {
let tag = ["LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"]
.iter()
.filter_map(|name| std::env::var(name).ok())
.find_map(|value| normalize(&value));
intern(tag)
})
}
#[cfg(alloc_frugal)]
#[cfg_attr(not(target_vendor = "apple"), allow(dead_code))]
pub(crate) fn from_environment() -> Option<String> {
None
}
#[cfg(alloc_frugal)]
#[cfg_attr(not(any(unix_desktop, target_vendor = "apple")), allow(dead_code))]
pub(crate) fn interned_from_environment() -> Option<&'static str> {
None
}
#[cfg(not(alloc_frugal))]
#[cfg_attr(not(any(unix_desktop, target_vendor = "apple")), allow(dead_code))]
pub(crate) fn normalize(value: &str) -> Option<String> {
let value = value.trim();
if value.is_empty() || value == "C" || value == "POSIX" {
return None;
}
let without_modifier = value.split('@').next().unwrap_or(value);
let without_encoding = without_modifier.split('.').next().unwrap_or(without_modifier);
let tag = without_encoding.replace('_', "-");
if tag.is_empty() {
None
} else {
Some(tag)
}
}
pub(crate) fn environment_revision(platform: &dyn Platform) -> u64 {
hash_facts(
platform.system_theme(),
platform.high_contrast(),
platform.reduce_motion(),
platform.system_locale(),
platform.accent_color(),
platform.reduce_transparency(),
)
}
fn hash_facts(
color_scheme: Option<bool>,
high_contrast: Option<bool>,
reduce_motion: Option<bool>,
locale: Option<&str>,
accent: Option<Color>,
reduce_transparency: Option<bool>,
) -> u64 {
if color_scheme.is_none()
&& high_contrast.is_none()
&& reduce_motion.is_none()
&& locale.is_none()
&& accent.is_none()
&& reduce_transparency.is_none()
{
return 0;
}
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
let mut hash = FNV_OFFSET;
let mut mix = |part: u64| {
hash ^= part;
hash = hash.wrapping_mul(FNV_PRIME);
};
for fact in [color_scheme, high_contrast, reduce_motion] {
match fact {
None => mix(0),
Some(false) => mix(1),
Some(true) => mix(2),
}
}
match locale {
None => mix(0),
Some(tag) => {
mix(1);
for byte in tag.bytes() {
mix(u64::from(byte));
}
}
}
match accent {
None => mix(0),
Some(color) => {
mix(1);
for channel in [color.r, color.g, color.b, color.a] {
mix(u64::from(channel));
}
}
}
match reduce_transparency {
None => mix(0),
Some(false) => mix(1),
Some(true) => mix(2),
}
hash
}
#[cfg(all(test, not(alloc_frugal)))]
mod tests {
use super::*;
#[test]
fn normalization_strips_encoding_and_modifier() {
assert_eq!(normalize("de_DE.UTF-8@euro").as_deref(), Some("de-DE"));
assert_eq!(normalize("pt_BR.utf8").as_deref(), Some("pt-BR"));
assert_eq!(normalize("ja").as_deref(), Some("ja"));
assert_eq!(normalize("C"), None);
assert_eq!(normalize("POSIX"), None);
assert_eq!(normalize(""), None);
}
#[test]
fn interning_is_stable_and_replaces_on_change() {
let first = intern(Some(String::from("en-US"))).expect("a tag");
let again = intern(Some(String::from("en-US"))).expect("a tag");
assert_eq!(first.as_ptr(), again.as_ptr(), "the same tag must be interned once");
let second = intern(Some(String::from("fr-FR"))).expect("a tag");
assert_eq!(second, "fr-FR");
let third = intern(Some(String::from("fr-FR"))).expect("a tag");
assert_eq!(second.as_ptr(), third.as_ptr());
let first_again = intern(Some(String::from("en-US"))).expect("a tag");
assert_eq!(
first.as_ptr(),
first_again.as_ptr(),
"a tag's pointer must not depend on order"
);
let _ = intern(Some(String::from("en-US")));
}
#[test]
fn empty_and_absent_values_answer_none() {
assert_eq!(intern(None), None);
assert_eq!(intern(Some(String::new())), None);
}
fn core(
theme: Option<bool>,
contrast: Option<bool>,
motion: Option<bool>,
locale: Option<&str>,
) -> u64 {
hash_facts(theme, contrast, motion, locale, None, None)
}
#[test]
fn generation_is_zero_only_when_nothing_was_read() {
assert_eq!(core(None, None, None, None), 0);
let baseline = core(Some(false), Some(false), Some(false), Some("en-US"));
assert_ne!(baseline, 0);
assert_ne!(baseline, core(Some(true), Some(false), Some(false), Some("en-US")));
assert_ne!(baseline, core(Some(false), Some(true), Some(false), Some("en-US")));
assert_ne!(baseline, core(Some(false), Some(false), Some(true), Some("en-US")));
assert_ne!(baseline, core(Some(false), Some(false), Some(false), Some("de-DE")));
assert_eq!(baseline, core(Some(false), Some(false), Some(false), Some("en-US")));
}
#[test]
fn accent_and_transparency_are_part_of_the_revision() {
let baseline = hash_facts(
Some(false),
Some(false),
Some(false),
Some("en-US"),
Some(Color { r: 0, g: 122, b: 255, a: 255 }),
Some(false),
);
assert_ne!(baseline, 0, "a fully observed environment is a real revision");
let other_accent = hash_facts(
Some(false),
Some(false),
Some(false),
Some("en-US"),
Some(Color { r: 255, g: 59, b: 48, a: 255 }),
Some(false),
);
assert_ne!(baseline, other_accent);
let other_transparency = hash_facts(
Some(false),
Some(false),
Some(false),
Some("en-US"),
Some(Color { r: 0, g: 122, b: 255, a: 255 }),
Some(true),
);
assert_ne!(baseline, other_transparency);
let unobserved =
hash_facts(Some(false), Some(false), Some(false), Some("en-US"), None, None);
assert_ne!(baseline, unobserved);
}
#[test]
fn an_accent_only_backend_reports_a_revision() {
struct AccentOnly(Option<Color>);
impl Platform for AccentOnly {
fn as_any(&self) -> &dyn core::any::Any {
self
}
fn backend_name(&self) -> &'static str {
"accent-only-test"
}
fn family(&self) -> crate::core::PlatformFamily {
crate::core::PlatformFamily::Desktop
}
fn accent_color(&self) -> Option<Color> {
self.0
}
fn init(&self) {}
fn run(&self) {}
fn quit(&self) -> bool {
true
}
fn create_window(
&self,
_title: &str,
_x: i32,
_y: i32,
_w: u32,
_h: u32,
) -> crate::core::ObjectId {
0
}
}
assert_eq!(environment_revision(&AccentOnly(None)), 0);
assert_ne!(environment_revision(&AccentOnly(Some(Color { r: 1, g: 2, b: 3, a: 255 }))), 0);
}
}