app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 52244c296262d1f585bb9344a874d013cc5acc73
parent 3b424bd5bc014634a0ca1e546ccd0105ccad9f30
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 18:28:27 +0000

app: add health check types

- define health status and result structs
- add health report container with defaults
- export health types from app crate
- add unit tests for status and defaults

Diffstat:
Mapp/src/app.rs | 2+-
Aapp/src/health.rs | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/lib.rs | 6++++++
3 files changed, 114 insertions(+), 1 deletion(-)

diff --git a/app/src/app.rs b/app/src/app.rs @@ -47,7 +47,7 @@ pub fn App() -> impl IntoView { AppInitStage::Geocoder => "orange", AppInitStage::Idle => "gray", }; - let reset_disabled = move || backends.with_untracked(|value| value.is_none()); + let reset_disabled = move || backends.with(|value| value.is_none()); let reset_label = move || { reset_status .get() diff --git a/app/src/health.rs b/app/src/health.rs @@ -0,0 +1,107 @@ +#![forbid(unsafe_code)] + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AppHealthCheckStatus { + Ok, + Error, + Skipped, +} + +impl AppHealthCheckStatus { + pub const fn as_str(self) -> &'static str { + match self { + AppHealthCheckStatus::Ok => "ok", + AppHealthCheckStatus::Error => "error", + AppHealthCheckStatus::Skipped => "skipped", + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AppHealthCheckResult { + pub status: AppHealthCheckStatus, + pub message: Option<String>, +} + +impl AppHealthCheckResult { + pub fn ok() -> Self { + Self { + status: AppHealthCheckStatus::Ok, + message: None, + } + } + + pub fn error(message: impl Into<String>) -> Self { + Self { + status: AppHealthCheckStatus::Error, + message: Some(message.into()), + } + } + + pub fn skipped() -> Self { + Self { + status: AppHealthCheckStatus::Skipped, + message: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AppHealthReport { + pub key_maps: AppHealthCheckResult, + pub bootstrap_config: AppHealthCheckResult, + pub bootstrap_app_data: AppHealthCheckResult, + pub datastore_roundtrip: AppHealthCheckResult, + pub keystore: AppHealthCheckResult, +} + +impl Default for AppHealthReport { + fn default() -> Self { + Self { + key_maps: AppHealthCheckResult::skipped(), + bootstrap_config: AppHealthCheckResult::skipped(), + bootstrap_app_data: AppHealthCheckResult::skipped(), + datastore_roundtrip: AppHealthCheckResult::skipped(), + keystore: AppHealthCheckResult::skipped(), + } + } +} + +impl AppHealthReport { + pub fn empty() -> Self { + Self::default() + } +} + +#[cfg(test)] +mod tests { + use super::{AppHealthCheckResult, AppHealthCheckStatus, AppHealthReport}; + + #[test] + fn health_status_as_str() { + assert_eq!(AppHealthCheckStatus::Ok.as_str(), "ok"); + assert_eq!(AppHealthCheckStatus::Error.as_str(), "error"); + assert_eq!(AppHealthCheckStatus::Skipped.as_str(), "skipped"); + } + + #[test] + fn health_result_constructors() { + let ok = AppHealthCheckResult::ok(); + assert_eq!(ok.status, AppHealthCheckStatus::Ok); + assert!(ok.message.is_none()); + + let err = AppHealthCheckResult::error("boom"); + assert_eq!(err.status, AppHealthCheckStatus::Error); + assert_eq!(err.message.as_deref(), Some("boom")); + } + + #[test] + fn health_report_defaults_skipped() { + let report = AppHealthReport::default(); + assert_eq!(report.key_maps.status, AppHealthCheckStatus::Skipped); + assert_eq!(report.bootstrap_config.status, AppHealthCheckStatus::Skipped); + assert_eq!(report.bootstrap_app_data.status, AppHealthCheckStatus::Skipped); + assert_eq!(report.datastore_roundtrip.status, AppHealthCheckStatus::Skipped); + assert_eq!(report.keystore.status, AppHealthCheckStatus::Skipped); + } +} diff --git a/app/src/lib.rs b/app/src/lib.rs @@ -5,6 +5,7 @@ mod bootstrap; mod context; mod config; mod data; +mod health; mod init; mod entry; @@ -18,6 +19,11 @@ pub use bootstrap::{ }; pub use context::{app_context, AppContext}; pub use data::{AppAppData, AppConfigData, AppConfigRole}; +pub use health::{ + AppHealthCheckResult, + AppHealthCheckStatus, + AppHealthReport, +}; pub use config::{ app_config_default, app_config_from_env,