use std::collections::HashSet;
use zng_app_context::app_local;
use zng_txt::Txt;
use zng_var::VARS;
use crate::update::UPDATES;
use super::*;
app_local! {
pub(crate) static EVENTS_SV: EventsService = const { EventsService::new() };
}
pub(crate) struct EventsService {
commands: CommandSet,
l10n: EventsL10n,
}
enum EventsL10n {
Pending(Vec<([&'static str; 3], Command, &'static str, CommandMetaVar<Txt>)>),
Init(Box<dyn Fn([&'static str; 3], Command, &'static str, CommandMetaVar<Txt>) + Send + Sync>),
}
impl EventsService {
const fn new() -> Self {
Self {
commands: HashSet::with_hasher(BuildFxHasher),
l10n: EventsL10n::Pending(vec![]),
}
}
}
#[derive(Clone, Default)]
pub struct BuildFxHasher;
impl std::hash::BuildHasher for BuildFxHasher {
type Hasher = rustc_hash::FxHasher;
fn build_hasher(&self) -> Self::Hasher {
rustc_hash::FxHasher::default()
}
}
pub type CommandSet = HashSet<Command, BuildFxHasher>;
pub struct EVENTS;
impl EVENTS {
pub fn commands(&self) -> CommandSet {
EVENTS_SV.read().commands.clone()
}
pub(super) fn register_command(&self, cmd: Command) {
tracing::trace!("register {cmd:?}");
UPDATES.once_update("register_command", move || {
let mut ev = EVENTS_SV.write();
if !ev.commands.insert(cmd) {
tracing::error!("command `{cmd:?}` is already registered");
}
});
}
pub(super) fn unregister_command(&self, cmd: Command) {
tracing::trace!("unregister {cmd:?}");
UPDATES.once_update("unregister_command", move || {
EVENTS_SV.write().commands.remove(&cmd);
});
}
pub fn notify(&self, debug_name: &'static str, n: impl FnOnce() + Send + 'static) {
VARS.modify(debug_name, n);
}
}
#[expect(non_camel_case_types)]
pub struct EVENTS_L10N;
impl EVENTS_L10N {
pub(crate) fn init_meta_l10n(&self, file: [&'static str; 3], cmd: Command, meta_name: &'static str, txt: CommandMetaVar<Txt>) {
{
let sv = EVENTS_SV.read();
if let EventsL10n::Init(f) = &sv.l10n {
f(file, cmd, meta_name, txt);
return;
}
}
let mut sv = EVENTS_SV.write();
match &mut sv.l10n {
EventsL10n::Pending(a) => a.push((file, cmd, meta_name, txt)),
EventsL10n::Init(f) => f(file, cmd, meta_name, txt),
}
}
pub fn init_l10n(&self, localize: impl Fn([&'static str; 3], Command, &'static str, CommandMetaVar<Txt>) + Send + Sync + 'static) {
let mut sv = EVENTS_SV.write();
match &mut sv.l10n {
EventsL10n::Pending(a) => {
for (f, k, a, t) in a.drain(..) {
localize(f, k, a, t);
}
}
EventsL10n::Init(_) => panic!("EVENTS_L10N already has a localizer"),
}
sv.l10n = EventsL10n::Init(Box::new(localize));
}
}