v0.1.4-pre.016

This commit is contained in:
2026-08-16 19:00:49 +02:00
parent d92eb01bc2
commit 5aa538ed5d
29 changed files with 994 additions and 73 deletions

View File

@@ -1,11 +1,12 @@
// file: crates/ksp-app-config-desk/src/app_state.rs
// version: 5
// version: 6
//! Shared backend state owned by the Tauri application.
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>,
@@ -27,7 +28,12 @@ impl AppState {
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);
let runtime_identity = crate::logging_runtime::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),
@@ -61,6 +67,7 @@ impl AppState {
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,
@@ -111,7 +118,12 @@ impl AppState {
}
/// 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, settings: &ksp_logging_lib::LoggingSettings) -> ksp_core_lib::Result<u32> {
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,
@@ -127,12 +139,59 @@ impl AppState {
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::logging_runtime::project_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::logging_runtime::count_to_u64(dropped.console()),
dropped_file_lines: crate::logging_runtime::count_to_u64(dropped.file()),
dropped_total_lines: crate::logging_runtime::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 {

View File

@@ -1,11 +1,12 @@
// file: crates/ksp-app-config-desk/src/bootstrap.rs
// version: 1
// version: 2
//! Config and Logging bootstrap for the desktop application.
pub(crate) struct LoggingStartup {
pub(crate) guard: ksp_logging_lib::LoggingGuard,
pub(crate) active_profile_id: std::option::Option<String>,
pub(crate) selection_source: String,
pub(crate) fallback_active: bool,
pub(crate) startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
}
@@ -25,20 +26,23 @@ pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_li
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine));
}
pub(crate) fn initialize_logging(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result<LoggingStartup> {
pub(crate) fn initialize_logging(
management: &ksp_config_lib::ConfigManagement,
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
) -> ksp_core_lib::Result<LoggingStartup> {
let environment = ksp_config_lib::ConfigEnvironment::load();
let environment = match environment {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error),
std::result::Result::Err(error) => return initialize_fallback_logging(error, runtime_identity),
};
let resolved = management.engine().load_resolved_logging_config(std::option::Option::None, &environment);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error),
std::result::Result::Err(error) => return initialize_fallback_logging(error, runtime_identity),
};
let active_profile_id = resolved.profile_id().to_owned();
let settings = resolved.into_settings();
let guard = ksp_logging_lib::initialize(&settings);
let guard = ksp_logging_lib::initialize_with_identity(&settings, runtime_identity);
return match guard {
std::result::Result::Ok(guard) => {
ksp_logging_lib::info!(
@@ -50,18 +54,22 @@ pub(crate) fn initialize_logging(management: &ksp_config_lib::ConfigManagement)
std::result::Result::Ok(LoggingStartup {
guard,
active_profile_id: std::option::Option::Some(active_profile_id),
selection_source: "default_profile".to_owned(),
fallback_active: false,
startup_diagnostic: std::option::Option::None,
})
},
std::result::Result::Err(error) => initialize_fallback_logging(error),
std::result::Result::Err(error) => initialize_fallback_logging(error, runtime_identity),
};
}
fn initialize_fallback_logging(initial_error: ksp_core_lib::Error) -> ksp_core_lib::Result<LoggingStartup> {
fn initialize_fallback_logging(
initial_error: ksp_core_lib::Error,
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
) -> ksp_core_lib::Result<LoggingStartup> {
let diagnostic = crate::CommandErrorDto::from_error(&initial_error);
let settings = fallback_logging_settings();
let guard = ksp_logging_lib::initialize(&settings);
let guard = ksp_logging_lib::initialize_with_identity(&settings, runtime_identity);
let guard = match guard {
std::result::Result::Ok(value) => value,
std::result::Result::Err(fallback_error) => {
@@ -83,6 +91,7 @@ fn initialize_fallback_logging(initial_error: ksp_core_lib::Error) -> ksp_core_l
return std::result::Result::Ok(LoggingStartup {
guard,
active_profile_id: std::option::Option::None,
selection_source: "fallback".to_owned(),
fallback_active: true,
startup_diagnostic: std::option::Option::Some(diagnostic),
});

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/constants.rs
// version: 7
// version: 8
//! Application-owned tracing targets and domains.
@@ -27,3 +27,5 @@ pub(crate) const TRACING_DOMAIN_ENVIRONMENT: &str = "config.environment";
pub(crate) const TRACING_DOMAIN_SECRETS: &str = "config.secrets";
/// Structured domain for typed Logging editor inspection.
pub(crate) const TRACING_DOMAIN_LOGGING_EDITOR: &str = "config.logging_editor";
/// Structured domain for active Logging runtime metadata and explicit profile application.
pub(crate) const TRACING_DOMAIN_LOGGING_RUNTIME: &str = "config.logging_runtime";

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/lib.rs
// version: 12
// version: 13
//! Tauri desktop application for managing and validating KSP configuration.
@@ -16,6 +16,7 @@ mod environment;
mod errors;
mod frontend_logging;
mod logging_editor;
mod logging_runtime;
mod profiles;
mod secrets;
mod splash;
@@ -34,6 +35,7 @@ pub(crate) use self::constants::TRACING_DOMAIN_DOCUMENTS;
pub(crate) use self::constants::TRACING_DOMAIN_ENVIRONMENT;
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_EDITOR;
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_RUNTIME;
pub(crate) use self::constants::TRACING_DOMAIN_PROFILES;
pub(crate) use self::constants::TRACING_DOMAIN_SECRETS;
pub(crate) use self::constants::TRACING_DOMAIN_WINDOWS;
@@ -70,6 +72,7 @@ pub(crate) use self::frontend_logging::emit_frontend_log_event;
pub(crate) use self::logging_editor::LoggingDocumentCandidateDto;
pub(crate) use self::logging_editor::LoggingDocumentDto;
pub(crate) use self::logging_editor::LoggingDocumentSaveResultDto;
pub(crate) use self::logging_runtime::{LoggingRuntimeFileDto, LoggingRuntimeStatusDto};
pub(crate) use self::profiles::ConfigProfileDetailDto;
pub(crate) use self::profiles::ConfigProfileDocumentDto;
pub(crate) use self::secrets::SecretRevealRequestDto;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/logging_editor.rs
// version: 3
// version: 4
//! Typed Logging document projection and validated persistence for the Config Desk Logging editor.
@@ -207,7 +207,7 @@ fn apply_persisted_runtime(state: &crate::AppState) -> ksp_core_lib::Result<(Str
};
let profile_id = resolved.profile_id().to_owned();
let settings = resolved.into_settings();
let generation = state.reinitialize_logging_runtime(profile_id.as_str(), &settings);
let generation = state.reinitialize_logging_runtime(profile_id.as_str(), "default_profile", &settings);
return match generation {
std::result::Result::Ok(value) => std::result::Result::Ok((profile_id, value)),
std::result::Result::Err(error) => std::result::Result::Err(error),

View File

@@ -0,0 +1,130 @@
// file: crates/ksp-app-config-desk/src/logging_runtime.rs
// version: 1
//! Runtime Logging metadata, launch identity and explicit profile application for Config Desk.
use ts_rs::TS; // rust-rules: derive-import
/// Metadata for one currently active persistent Logging file output.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging_runtime/LoggingRuntimeFileDto.ts")]
pub(crate) struct LoggingRuntimeFileDto {
/// Stable configured output identifier.
pub(crate) output_id: String,
/// Resolved runtime directory.
pub(crate) directory: String,
/// Effective per-launch filename prefix passed to the rolling appender.
pub(crate) file_name_prefix: String,
/// Effective file rotation cadence.
pub(crate) rotation: String,
}
/// Safe observable state of the currently active KSP Logging runtime.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging_runtime/LoggingRuntimeStatusDto.ts")]
pub(crate) struct LoggingRuntimeStatusDto {
/// Active profile identifier, or `None` while fallback Logging is active.
pub(crate) active_profile: std::option::Option<String>,
/// Source that selected the active runtime profile.
pub(crate) selection_source: String,
/// Monotonic runtime generation.
pub(crate) generation: u32,
/// Whether transient fallback Logging is active.
pub(crate) fallback_active: bool,
/// Stable application identifier embedded in launch-specific file names.
pub(crate) application_id: String,
/// Stable launch timestamp token reused by all hot reloads in this process.
pub(crate) launch_timestamp: String,
/// Whether the currently active console sink is enabled.
pub(crate) console_enabled: bool,
/// Cumulative console lines dropped by non-blocking outputs.
pub(crate) dropped_console_lines: u64,
/// Cumulative file lines dropped by non-blocking outputs.
pub(crate) dropped_file_lines: u64,
/// Cumulative lines dropped across all outputs.
pub(crate) dropped_total_lines: u64,
/// Active persistent file outputs and their launch-specific prefixes.
pub(crate) files: std::vec::Vec<LoggingRuntimeFileDto>,
}
/// Creates the stable runtime identity for this Config Desk process launch.
pub(crate) fn launch_identity() -> ksp_core_lib::Result<ksp_logging_lib::LoggingRuntimeIdentity> {
let now = time::OffsetDateTime::now_utc();
let milliseconds = now.nanosecond() / 1_000_000;
let timestamp = format!(
"{:04}{:02}{:02}-{:02}{:02}{:02}.{:03}Z-p{}",
now.year(),
now.month() as u8,
now.day(),
now.hour(),
now.minute(),
now.second(),
milliseconds,
std::process::id(),
);
return ksp_logging_lib::LoggingRuntimeIdentity::new(crate::TRACING_TARGET, timestamp);
}
/// Returns safe metadata for the currently active Logging runtime.
pub(crate) fn status(state: &crate::AppState) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
return state.logging_runtime_status();
}
/// Applies one persisted Logging profile explicitly without changing `default_profile` or the source document.
pub(crate) fn apply_profile(state: &crate::AppState, profile_id: &str) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
let environment = ksp_config_lib::ConfigEnvironment::load();
let environment = match environment {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let resolved = state.config_management().engine().load_resolved_logging_config(std::option::Option::Some(profile_id), &environment);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let active_profile = resolved.profile_id().to_owned();
let settings = resolved.into_settings();
let generation = state.reinitialize_logging_runtime(active_profile.as_str(), "explicit", &settings);
if let std::result::Result::Err(error) = generation {
return std::result::Result::Err(error);
}
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_LOGGING_RUNTIME,
active_profile = active_profile.as_str(),
selection_source = "explicit",
"persisted Logging profile applied explicitly to runtime"
);
return state.logging_runtime_status();
}
pub(crate) fn project_file(metadata: &ksp_logging_lib::RuntimeFileMetadata) -> LoggingRuntimeFileDto {
return LoggingRuntimeFileDto {
output_id: metadata.output_id().to_owned(),
directory: metadata.directory().display().to_string(),
file_name_prefix: metadata.file_name_prefix().to_owned(),
rotation: rotation_label(metadata.rotation()).to_owned(),
};
}
pub(crate) const fn rotation_label(rotation: ksp_logging_lib::FileRotation) -> &'static str {
return match rotation {
ksp_logging_lib::FileRotation::Never => "never",
ksp_logging_lib::FileRotation::Hourly => "hourly",
ksp_logging_lib::FileRotation::Daily => "daily",
};
}
pub(crate) fn count_to_u64(value: usize) -> u64 {
let converted = u64::try_from(value);
return match converted {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => u64::MAX,
};
}
#[cfg(test)]
#[path = "../unit_tests/logging_runtime.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/tauri.rs
// version: 13
// version: 14
//! Tauri runtime assembly for the KSP configuration desktop application.
@@ -49,6 +49,8 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
remove_environment_value,
reveal_environment_value,
get_logging_document,
get_logging_runtime_status,
apply_logging_profile,
save_logging_document,
emit_frontend_log,
splash_frontend_ready
@@ -181,6 +183,27 @@ fn get_logging_document(state: tauri::State<'_, crate::AppState>) -> std::result
};
}
#[tauri::command]
fn get_logging_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
let result = crate::logging_runtime::status(&state);
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
};
}
#[tauri::command]
fn apply_logging_profile(
profile_id: String,
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
let result = crate::logging_runtime::apply_profile(&state, profile_id.as_str());
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
};
}
#[tauri::command]
fn save_logging_document(
candidate: crate::LoggingDocumentCandidateDto,