209 lines
9.6 KiB
Rust
209 lines
9.6 KiB
Rust
// file: crates/ksp-app-config-desk/src/app_state.rs
|
|
// version: 8
|
|
|
|
//! Shared backend state owned by the Tauri application.
|
|
|
|
/// Shared Config Desk application state managed by Tauri.
|
|
pub(crate) struct AppState {
|
|
config_management: ksp_config_lib::ConfigManagement,
|
|
logging_runtime: std::sync::Mutex<LoggingRuntimeState>,
|
|
splash_settings: crate::SplashSettings,
|
|
splash_sequence_started: std::sync::atomic::AtomicBool,
|
|
}
|
|
|
|
impl AppState {
|
|
/// Initializes Config ownership, the Logging runtime and durable application state.
|
|
pub(crate) fn initialize(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<Self> {
|
|
let config_management = crate::config_management(arguments);
|
|
let config_management = match config_management {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let runtime_identity = crate::launch_identity();
|
|
let runtime_identity = match runtime_identity {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let logging_startup = crate::initialize_logging(&config_management, &runtime_identity);
|
|
let logging_startup = match logging_startup {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let splash_settings = crate::SplashSettings::load();
|
|
let splash_settings = match splash_settings {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
ksp_logging_lib::warn!(
|
|
target: crate::TRACING_TARGET,
|
|
domain = crate::TRACING_DOMAIN_WINDOWS,
|
|
error_domain = error.code().domain(),
|
|
error_code = error.code().code(),
|
|
"managed splash timings are invalid; using transient in-memory defaults"
|
|
);
|
|
crate::SplashSettings::fallback()
|
|
},
|
|
};
|
|
ksp_logging_lib::debug!(
|
|
target: crate::TRACING_TARGET,
|
|
domain = crate::TRACING_DOMAIN_WINDOWS,
|
|
minimum_ms = splash_settings.minimum_ms(),
|
|
minimum_source = splash_settings.minimum_source(),
|
|
fade_ms = splash_settings.fade_ms(),
|
|
fade_source = splash_settings.fade_source(),
|
|
expected_backend_lifecycle_ms = splash_settings.expected_backend_lifecycle_ms(),
|
|
"resolved Config Desk splash timings"
|
|
);
|
|
return std::result::Result::Ok(Self {
|
|
config_management,
|
|
logging_runtime: std::sync::Mutex::new(LoggingRuntimeState {
|
|
guard: logging_startup.guard,
|
|
active_profile_id: logging_startup.active_profile_id,
|
|
selection_source: logging_startup.selection_source,
|
|
generation: 1,
|
|
fallback_active: logging_startup.fallback_active,
|
|
startup_diagnostic: logging_startup.startup_diagnostic,
|
|
}),
|
|
splash_settings,
|
|
splash_sequence_started: std::sync::atomic::AtomicBool::new(false),
|
|
});
|
|
}
|
|
|
|
/// Builds a safe initial frontend snapshot without exposing resolved secret values or arbitrary error context.
|
|
pub(crate) fn snapshot(&self) -> ksp_core_lib::Result<crate::AppSnapshotDto> {
|
|
let document_count = self.config_management.engine().registry().descriptors().count();
|
|
let document_count = u32::try_from(document_count);
|
|
let document_count = match document_count {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_APP_STATE_INVALID, "Config registry contains too many descriptors for the desktop DTO")
|
|
.with_source(error),
|
|
);
|
|
},
|
|
};
|
|
let runtime = self.logging_runtime.lock();
|
|
let runtime = match runtime {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
return std::result::Result::Err(ksp_core_lib::Error::new(
|
|
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
|
"Config Desk Logging runtime state lock is poisoned",
|
|
));
|
|
},
|
|
};
|
|
let _keep_guard_alive = &runtime.guard;
|
|
return std::result::Result::Ok(crate::AppSnapshotDto {
|
|
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
|
config_document_count: document_count,
|
|
active_logging_profile: runtime.active_profile_id.clone(),
|
|
logging_generation: runtime.generation,
|
|
fallback_logging_active: runtime.fallback_active,
|
|
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
|
});
|
|
}
|
|
|
|
/// Returns the Config management facade owned by the application state.
|
|
#[must_use]
|
|
pub(crate) const fn config_management(&self) -> &ksp_config_lib::ConfigManagement {
|
|
return &self.config_management;
|
|
}
|
|
|
|
/// Replaces the active Logging runtime with freshly resolved settings and advances the runtime generation only after success.
|
|
pub(crate) fn reinitialize_logging_runtime(
|
|
&self,
|
|
profile_id: &str,
|
|
selection_source: &str,
|
|
settings: &ksp_logging_lib::LoggingSettings,
|
|
) -> ksp_core_lib::Result<u32> {
|
|
let runtime = self.logging_runtime.lock();
|
|
let mut runtime = match runtime {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
return std::result::Result::Err(ksp_core_lib::Error::new(
|
|
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
|
"Config Desk Logging runtime state lock is poisoned",
|
|
));
|
|
},
|
|
};
|
|
let reinitialized = ksp_logging_lib::reinitialize(&mut runtime.guard, settings);
|
|
if let std::result::Result::Err(error) = reinitialized {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
runtime.active_profile_id = std::option::Option::Some(profile_id.to_owned());
|
|
runtime.selection_source = selection_source.to_owned();
|
|
runtime.generation = runtime.generation.saturating_add(1);
|
|
runtime.fallback_active = false;
|
|
runtime.startup_diagnostic = std::option::Option::None;
|
|
return std::result::Result::Ok(runtime.generation);
|
|
}
|
|
|
|
/// Returns a safe observable snapshot of the currently active Logging runtime.
|
|
pub(crate) fn logging_runtime_status(&self) -> ksp_core_lib::Result<crate::LoggingRuntimeStatusDto> {
|
|
let runtime = self.logging_runtime.lock();
|
|
let runtime = match runtime {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
return std::result::Result::Err(ksp_core_lib::Error::new(
|
|
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
|
"Config Desk Logging runtime state lock is poisoned",
|
|
));
|
|
},
|
|
};
|
|
let identity = runtime.guard.runtime_identity();
|
|
let identity = match identity {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => {
|
|
return std::result::Result::Err(ksp_core_lib::Error::new(
|
|
crate::ERROR_CODE_APP_STATE_INVALID,
|
|
"Config Desk Logging runtime is missing its application launch identity",
|
|
));
|
|
},
|
|
};
|
|
let console_enabled = match runtime.guard.settings().console() {
|
|
std::option::Option::Some(console) => console.enabled(),
|
|
std::option::Option::None => false,
|
|
};
|
|
let dropped = runtime.guard.dropped_lines();
|
|
let mut files = std::vec::Vec::<crate::LoggingRuntimeFileDto>::new();
|
|
for metadata in runtime.guard.active_file_outputs() {
|
|
files.push(crate::project_logging_file(&metadata));
|
|
}
|
|
return std::result::Result::Ok(crate::LoggingRuntimeStatusDto {
|
|
active_profile: runtime.active_profile_id.clone(),
|
|
selection_source: runtime.selection_source.clone(),
|
|
generation: runtime.generation,
|
|
fallback_active: runtime.fallback_active,
|
|
application_id: identity.application_id().to_owned(),
|
|
launch_timestamp: identity.launch_timestamp().to_owned(),
|
|
console_enabled,
|
|
dropped_console_lines: crate::count_to_u64(dropped.console()),
|
|
dropped_file_lines: crate::count_to_u64(dropped.file()),
|
|
dropped_total_lines: crate::count_to_u64(dropped.total()),
|
|
files,
|
|
});
|
|
}
|
|
|
|
/// Returns the resolved splash timings captured during application bootstrap.
|
|
#[must_use]
|
|
pub(crate) const fn splash_settings(&self) -> crate::SplashSettings {
|
|
return self.splash_settings;
|
|
}
|
|
|
|
/// Marks the one-shot splash lifecycle as started and reports whether this caller won the transition.
|
|
pub(crate) fn begin_splash_sequence(&self) -> bool {
|
|
return self
|
|
.splash_sequence_started
|
|
.compare_exchange(false, true, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire)
|
|
.is_ok();
|
|
}
|
|
}
|
|
|
|
struct LoggingRuntimeState {
|
|
guard: ksp_logging_lib::LoggingGuard,
|
|
active_profile_id: std::option::Option<String>,
|
|
selection_source: String,
|
|
generation: u32,
|
|
fallback_active: bool,
|
|
startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
|
}
|