//! Core composition root (replaces `bootstrap()` of the Electron `src/main/index.js`). //! Services are created once and shared through `Arc`; Tauri commands reach them via //! the managed [`CoreSlot`]. use crate::core::config::ConfigStore; use crate::core::error::{CoreError, CoreResult}; use crate::core::i18n::I18n; use crate::core::logging::Logger; use crate::core::paths::AppPaths; use crate::core::profile_transfer::ProfileTransfer; use crate::core::profiles::ProfileService; use crate::core::vault::VaultStore; use crate::security::caller::{WindowRegistry, MAIN_LABEL}; use once_cell::sync::OnceCell; use serde::Serialize; use serde_json::Value; use std::sync::Arc; use tauri::{AppHandle, Emitter, EventTarget}; pub const CORE_API_VERSION: &str = "1.1.0"; /// Public Module API (formerly Plugin API). 1.7.0 = unchanged contract of 0.1.0. pub const MODULE_API_VERSION: &str = "1.7.0"; pub const DATA_SCHEMA: u32 = 1; pub const COPYRIGHT: &str = "Copyright 2026 Josef Chudy"; #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct BuildInfo { pub version: String, pub build_id: String, pub built_at: String, } impl BuildInfo { pub fn current(version: &str) -> Self { Self { version: version.to_string(), build_id: env!("CHJ_BUILD_ID").to_string(), built_at: env!("CHJ_BUILT_AT").to_string() } } } pub struct Core { pub app: AppHandle, pub paths: AppPaths, pub build: BuildInfo, pub logger: Arc, pub i18n: Arc, pub config: Arc, pub vault: Arc, pub profiles: Arc, pub profile_transfer: ProfileTransfer, pub windows: Arc, pub services: crate::core::services::Services, } impl Core { pub fn language(&self) -> String { self.config.language() } pub fn t(&self, key: &str) -> String { self.i18n.t(&self.language(), key) } /// Push event to the main window only (never to module windows). pub fn emit_main(&self, event: &str, payload: S) { let _ = self.app.emit_to(EventTarget::webview_window(MAIN_LABEL), event, payload); } /// Push event to one module window. pub fn emit_window(&self, label: &str, event: &str, payload: S) { let _ = self.app.emit_to(EventTarget::webview_window(label), event, payload); } pub fn log_ipc_failure(&self, channel: &str, error: &CoreError) { self.logger.error( "IPC operation failed.", serde_json::json!({ "channel": channel, "message": error.message, "code": if error.code.is_empty() { Value::Null } else { Value::String(error.code.clone()) } }), ); } } /// Holder managed by Tauri before the Core exists (license dialog runs first). #[derive(Default)] pub struct CoreSlot(OnceCell>); impl CoreSlot { pub fn set(&self, core: Arc) { let _ = self.0.set(core); } pub fn get(&self) -> CoreResult> { self.0.get().cloned().ok_or_else(|| CoreError::new("CORE_NOT_READY", "The Core is not initialized yet.")) } pub fn try_get(&self) -> Option> { self.0.get().cloned() } }