182 lines
7.3 KiB
Rust
182 lines
7.3 KiB
Rust
// file: kb-app-demo-desktop/src/app_state.rs
|
|
// version: 4
|
|
|
|
//! Shared Tauri application state and startup initialization.
|
|
|
|
/// Shared state managed by Tauri for the desktop demo application.
|
|
pub(crate) struct AppState {
|
|
config_path: std::string::String,
|
|
app_config: kb_config::AppConfig,
|
|
active_profile: kb_config::ProfileConfig,
|
|
logging_guard: std::sync::Mutex<kb_logging::LoggingGuard>,
|
|
http_pool: kb_onchain_transport::HttpEndpointPool,
|
|
ws_pool: std::sync::Mutex<std::option::Option<std::sync::Arc<kb_onchain_transport::WsEndpointPool>>>,
|
|
demo_ws_session: tokio::sync::Mutex<std::option::Option<std::sync::Arc<kb_onchain_transport::WsSession>>>,
|
|
demo_backfill_running: std::sync::atomic::AtomicBool,
|
|
demo_backfill_cancel_requested: std::sync::atomic::AtomicBool,
|
|
}
|
|
|
|
impl crate::AppState {
|
|
/// Initializes configuration, logging and shared runtime state.
|
|
pub(crate) fn initialize() -> kb_core::Result<crate::AppState> {
|
|
let config_path = resolve_config_path();
|
|
let app_config = match kb_config::read_config_json_file(&config_path) {
|
|
std::result::Result::Ok(config) => config,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let active_profile = match kb_config::active_profile(&app_config) {
|
|
std::result::Result::Ok(profile) => profile.clone(),
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let logging_config = convert_logging_config(&active_profile.logging);
|
|
let logging_guard = match kb_logging::init_logging(&logging_config) {
|
|
std::result::Result::Ok(guard) => guard,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let http_pool = match kb_onchain_transport::HttpEndpointPool::from_profile(&active_profile) {
|
|
std::result::Result::Ok(pool) => pool,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(crate::AppState {
|
|
config_path: config_path.display().to_string(),
|
|
app_config,
|
|
active_profile,
|
|
logging_guard: std::sync::Mutex::new(logging_guard),
|
|
http_pool,
|
|
ws_pool: std::sync::Mutex::new(std::option::Option::None),
|
|
demo_ws_session: tokio::sync::Mutex::new(std::option::Option::None),
|
|
demo_backfill_running: std::sync::atomic::AtomicBool::new(false),
|
|
demo_backfill_cancel_requested: std::sync::atomic::AtomicBool::new(false),
|
|
});
|
|
}
|
|
|
|
/// Returns the path used to load the configuration file.
|
|
pub(crate) fn config_path(&self) -> &str {
|
|
return self.config_path.as_str();
|
|
}
|
|
|
|
/// Returns the complete parsed application configuration.
|
|
pub(crate) fn app_config(&self) -> &kb_config::AppConfig {
|
|
return &self.app_config;
|
|
}
|
|
|
|
/// Returns the active profile selected from the configuration.
|
|
pub(crate) fn active_profile(&self) -> &kb_config::ProfileConfig {
|
|
return &self.active_profile;
|
|
}
|
|
|
|
/// Returns the configured HTTP endpoint pool.
|
|
pub(crate) fn http_pool(&self) -> &kb_onchain_transport::HttpEndpointPool {
|
|
return &self.http_pool;
|
|
}
|
|
|
|
/// Returns the lazily initialized WebSocket endpoint pool.
|
|
pub(crate) fn demo_ws_pool(
|
|
&self,
|
|
) -> kb_core::Result<std::sync::Arc<kb_onchain_transport::WsEndpointPool>> {
|
|
let lock_result = self.ws_pool.lock();
|
|
let mut guard = match lock_result {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
return std::result::Result::Err(kb_core::Error::invalid_state(
|
|
"demo WebSocket pool lock is poisoned",
|
|
));
|
|
},
|
|
};
|
|
if let std::option::Option::Some(pool) = guard.as_ref() {
|
|
return std::result::Result::Ok(std::sync::Arc::clone(pool));
|
|
}
|
|
let pool = match kb_onchain_transport::WsEndpointPool::from_profile(&self.active_profile) {
|
|
std::result::Result::Ok(value) => std::sync::Arc::new(value),
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
*guard = std::option::Option::Some(std::sync::Arc::clone(&pool));
|
|
return std::result::Result::Ok(pool);
|
|
}
|
|
|
|
/// Returns the persistent WebSocket demo session slot.
|
|
pub(crate) fn demo_ws_session(
|
|
&self,
|
|
) -> &tokio::sync::Mutex<
|
|
std::option::Option<std::sync::Arc<kb_onchain_transport::WsSession>>,
|
|
> {
|
|
return &self.demo_ws_session;
|
|
}
|
|
|
|
/// Returns the single-campaign execution flag used by the backfill window.
|
|
pub(crate) fn demo_backfill_running(&self) -> &std::sync::atomic::AtomicBool {
|
|
return &self.demo_backfill_running;
|
|
}
|
|
|
|
/// Returns the cooperative cancellation flag used by the backfill window.
|
|
pub(crate) fn demo_backfill_cancel_requested(&self) -> &std::sync::atomic::AtomicBool {
|
|
return &self.demo_backfill_cancel_requested;
|
|
}
|
|
|
|
/// Returns the number of logging routes held by the logging guard.
|
|
pub(crate) fn logging_route_count(&self) -> usize {
|
|
let lock_result = self.logging_guard.lock();
|
|
let guard = match lock_result {
|
|
std::result::Result::Ok(guard) => guard,
|
|
std::result::Result::Err(_) => return 0,
|
|
};
|
|
return guard.route_count();
|
|
}
|
|
}
|
|
|
|
fn convert_logging_config(config: &kb_config::LoggingConfig) -> kb_logging::LoggingConfig {
|
|
let mut targets = std::vec::Vec::<kb_logging::LogTargetConfig>::new();
|
|
for target in &config.targets {
|
|
targets.push(kb_logging::LogTargetConfig {
|
|
name: target.name.clone(),
|
|
enabled: target.enabled,
|
|
sink: target.sink.clone(),
|
|
level: target.level.clone(),
|
|
path: target.path.clone(),
|
|
rotation: target.rotation.clone(),
|
|
format: target.format.clone(),
|
|
ansi: target.ansi,
|
|
targets: target.targets.clone(),
|
|
});
|
|
}
|
|
let mut target_filters = std::vec::Vec::<kb_logging::LogTargetFilterConfig>::new();
|
|
for filter in &config.target_filters {
|
|
target_filters.push(kb_logging::LogTargetFilterConfig {
|
|
target: filter.target.clone(),
|
|
level: filter.level.clone(),
|
|
});
|
|
}
|
|
return kb_logging::LoggingConfig {
|
|
default_level: config.default_level.clone(),
|
|
targets,
|
|
target_filters,
|
|
};
|
|
}
|
|
|
|
fn resolve_config_path() -> std::path::PathBuf {
|
|
let configured = std::env::var("KB_CONFIG_PATH").ok();
|
|
return resolve_config_path_from_value(configured.as_deref());
|
|
}
|
|
|
|
fn resolve_config_path_from_value(value: std::option::Option<&str>) -> std::path::PathBuf {
|
|
if let std::option::Option::Some(configured) = value {
|
|
let trimmed = configured.trim();
|
|
if !trimmed.is_empty() {
|
|
return std::path::PathBuf::from(trimmed);
|
|
}
|
|
}
|
|
return crate::workspace_root_dir().join("config/example.config.json");
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn resolve_config_path_uses_workspace_example_by_default() {
|
|
let path = super::resolve_config_path_from_value(std::option::Option::None);
|
|
assert_eq!(
|
|
path.file_name().and_then(std::ffi::OsStr::to_str),
|
|
std::option::Option::Some("example.config.json")
|
|
);
|
|
}
|
|
}
|