Complete TUI Phase 3 implementation with all 5 power feature screens: - Who screen: 5 modes (expert/workload/reviews/active/overlap) with mode tabs, input bar, result rendering, and hint bar - Search screen: full-text search with result list and scoring display - Timeline screen: chronological event feed with time-relative display - Trace screen: file provenance chains with expand/collapse, rename tracking, and linked issues/discussions - File History screen: per-file MR timeline with rename chain display and discussion snippets Also includes: - Command palette overlay (fuzzy search) - Bootstrap screen (initial sync flow) - Action layer split from monolithic action.rs to per-screen modules - Entity and render cache infrastructure - Shared who_types module in core crate - All screens wired into view/mod.rs dispatch - 597 tests passing, clippy clean (pedantic + nursery), fmt clean
161 lines
4.8 KiB
Rust
161 lines
4.8 KiB
Rust
#![allow(dead_code)] // Phase 2.5: consumed by Bootstrap screen
|
|
|
|
//! Bootstrap screen state.
|
|
//!
|
|
//! Handles first-launch and empty-database scenarios. The schema
|
|
//! preflight runs before the TUI event loop; the bootstrap screen
|
|
//! guides users to sync when no data is available.
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DataReadiness
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Result of checking whether the database has enough data to show the TUI.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct DataReadiness {
|
|
/// Database has at least one issue.
|
|
pub has_issues: bool,
|
|
/// Database has at least one merge request.
|
|
pub has_mrs: bool,
|
|
/// Database has at least one search document.
|
|
pub has_documents: bool,
|
|
/// Current schema version from the schema_version table.
|
|
pub schema_version: i32,
|
|
}
|
|
|
|
impl DataReadiness {
|
|
/// Whether the database has any entity data at all.
|
|
#[must_use]
|
|
pub fn has_any_data(&self) -> bool {
|
|
self.has_issues || self.has_mrs
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SchemaCheck
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Result of schema version validation.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum SchemaCheck {
|
|
/// Schema is at or above the minimum required version.
|
|
Compatible { version: i32 },
|
|
/// No database or no schema_version table found.
|
|
NoDB,
|
|
/// Schema exists but is too old for this TUI version.
|
|
Incompatible { found: i32, minimum: i32 },
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BootstrapState
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// State for the Bootstrap screen.
|
|
#[derive(Debug, Default)]
|
|
pub struct BootstrapState {
|
|
/// Whether a data readiness check has completed.
|
|
pub readiness: Option<DataReadiness>,
|
|
/// Whether the user has initiated a sync from the bootstrap screen.
|
|
pub sync_started: bool,
|
|
}
|
|
|
|
impl BootstrapState {
|
|
/// Apply a data readiness result.
|
|
pub fn apply_readiness(&mut self, readiness: DataReadiness) {
|
|
self.readiness = Some(readiness);
|
|
}
|
|
|
|
/// Whether we have data (and should auto-transition to Dashboard).
|
|
#[must_use]
|
|
pub fn should_transition_to_dashboard(&self) -> bool {
|
|
self.readiness
|
|
.as_ref()
|
|
.is_some_and(DataReadiness::has_any_data)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_data_readiness_has_any_data() {
|
|
let empty = DataReadiness {
|
|
has_issues: false,
|
|
has_mrs: false,
|
|
has_documents: false,
|
|
schema_version: 26,
|
|
};
|
|
assert!(!empty.has_any_data());
|
|
|
|
let with_issues = DataReadiness {
|
|
has_issues: true,
|
|
..empty.clone()
|
|
};
|
|
assert!(with_issues.has_any_data());
|
|
|
|
let with_mrs = DataReadiness {
|
|
has_mrs: true,
|
|
..empty
|
|
};
|
|
assert!(with_mrs.has_any_data());
|
|
}
|
|
|
|
#[test]
|
|
fn test_schema_check_variants() {
|
|
let compat = SchemaCheck::Compatible { version: 26 };
|
|
assert!(matches!(compat, SchemaCheck::Compatible { version: 26 }));
|
|
|
|
let no_db = SchemaCheck::NoDB;
|
|
assert!(matches!(no_db, SchemaCheck::NoDB));
|
|
|
|
let incompat = SchemaCheck::Incompatible {
|
|
found: 10,
|
|
minimum: 20,
|
|
};
|
|
assert!(matches!(
|
|
incompat,
|
|
SchemaCheck::Incompatible {
|
|
found: 10,
|
|
minimum: 20
|
|
}
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn test_bootstrap_state_default() {
|
|
let state = BootstrapState::default();
|
|
assert!(state.readiness.is_none());
|
|
assert!(!state.sync_started);
|
|
assert!(!state.should_transition_to_dashboard());
|
|
}
|
|
|
|
#[test]
|
|
fn test_bootstrap_state_apply_readiness_empty() {
|
|
let mut state = BootstrapState::default();
|
|
state.apply_readiness(DataReadiness {
|
|
has_issues: false,
|
|
has_mrs: false,
|
|
has_documents: false,
|
|
schema_version: 26,
|
|
});
|
|
assert!(!state.should_transition_to_dashboard());
|
|
}
|
|
|
|
#[test]
|
|
fn test_bootstrap_state_apply_readiness_with_data() {
|
|
let mut state = BootstrapState::default();
|
|
state.apply_readiness(DataReadiness {
|
|
has_issues: true,
|
|
has_mrs: false,
|
|
has_documents: false,
|
|
schema_version: 26,
|
|
});
|
|
assert!(state.should_transition_to_dashboard());
|
|
}
|
|
}
|