mod filter;
mod overlay;
mod search;
pub mod security_filter;
mod status;
pub use filter::{CycleFilter, FilterState};
pub use overlay::{OverlayKind as ViewModelOverlayKind, OverlayState};
pub use search::{SearchState, SearchStateCore};
pub use security_filter::{
LicenseCategory, QuickFilter, RiskLevel, SecurityFilterCriteria, SecurityFilterState,
};
pub use status::StatusMessage;
#[derive(Debug, Clone)]
pub struct QualityViewState {
pub view_mode: QualityViewMode,
pub selected_recommendation: usize,
pub total_recommendations: usize,
pub scroll_offset: usize,
}
impl Default for QualityViewState {
fn default() -> Self {
Self::new()
}
}
impl QualityViewState {
#[must_use]
pub const fn new() -> Self {
Self {
view_mode: QualityViewMode::Summary,
selected_recommendation: 0,
total_recommendations: 0,
scroll_offset: 0,
}
}
#[must_use]
pub const fn with_recommendations(total: usize) -> Self {
Self {
view_mode: QualityViewMode::Summary,
selected_recommendation: 0,
total_recommendations: total,
scroll_offset: 0,
}
}
pub const fn next_mode(&mut self) {
self.view_mode = match self.view_mode {
QualityViewMode::Summary => QualityViewMode::Recommendations,
QualityViewMode::Recommendations => QualityViewMode::Metrics,
QualityViewMode::Metrics => QualityViewMode::Summary,
};
}
pub const fn select_next(&mut self) {
if self.selected_recommendation < self.total_recommendations.saturating_sub(1) {
self.selected_recommendation += 1;
}
}
pub const fn select_prev(&mut self) {
if self.selected_recommendation > 0 {
self.selected_recommendation -= 1;
}
}
pub const fn go_first(&mut self) {
self.selected_recommendation = 0;
self.scroll_offset = 0;
}
pub const fn go_last(&mut self) {
self.selected_recommendation = self.total_recommendations.saturating_sub(1);
}
pub const fn set_total(&mut self, total: usize) {
self.total_recommendations = total;
if self.selected_recommendation >= total {
self.selected_recommendation = total.saturating_sub(1);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum QualityViewMode {
#[default]
Summary,
Recommendations,
Metrics,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quality_view_state_navigation() {
let mut state = QualityViewState::with_recommendations(10);
assert_eq!(state.selected_recommendation, 0);
state.select_next();
assert_eq!(state.selected_recommendation, 1);
state.select_prev();
assert_eq!(state.selected_recommendation, 0);
state.select_prev();
assert_eq!(state.selected_recommendation, 0);
state.go_last();
assert_eq!(state.selected_recommendation, 9);
state.select_next();
assert_eq!(state.selected_recommendation, 9);
state.go_first();
assert_eq!(state.selected_recommendation, 0);
}
#[test]
fn test_quality_view_state_mode_cycling() {
let mut state = QualityViewState::new();
assert_eq!(state.view_mode, QualityViewMode::Summary);
state.next_mode();
assert_eq!(state.view_mode, QualityViewMode::Recommendations);
state.next_mode();
assert_eq!(state.view_mode, QualityViewMode::Metrics);
state.next_mode();
assert_eq!(state.view_mode, QualityViewMode::Summary);
}
#[test]
fn test_quality_view_state_set_total() {
let mut state = QualityViewState::with_recommendations(10);
state.selected_recommendation = 8;
state.set_total(5);
assert_eq!(state.total_recommendations, 5);
assert_eq!(state.selected_recommendation, 4);
state.set_total(20);
assert_eq!(state.total_recommendations, 20);
assert_eq!(state.selected_recommendation, 4);
}
}