Remove unused code and eliminate build warnings

Removed unused widget subscription system, cache utilities, error variants,
theme functions, and struct fields. Replaced subscription-based widgets
with direct metric filtering. Build now completes with zero warnings.
This commit is contained in:
Christoffer Martinsson 2025-10-18 23:50:15 +02:00
parent 7f85a6436e
commit 0141a6e111
14 changed files with 15 additions and 764 deletions

View File

@ -6,7 +6,6 @@ use tracing::info;
/// Manages metric caching with background tasks
pub struct MetricCacheManager {
cache: Arc<ConfigurableCache>,
config: CacheConfig,
}
impl MetricCacheManager {
@ -15,7 +14,6 @@ impl MetricCacheManager {
Self {
cache,
config,
}
}
@ -25,69 +23,14 @@ impl MetricCacheManager {
info!("Cache manager background tasks disabled for debugging");
}
/// Check if metric should be collected
pub async fn should_collect_metric(&self, metric_name: &str) -> bool {
self.cache.should_collect(metric_name).await
}
/// Store metric in cache
pub async fn cache_metric(&self, metric: Metric) {
self.cache.store_metric(metric).await;
}
/// Get cached metric if valid
pub async fn get_cached_metric(&self, metric_name: &str) -> Option<Metric> {
self.cache.get_cached_metric(metric_name).await
}
/// Get all valid cached metrics
pub async fn get_all_valid_metrics(&self) -> Vec<Metric> {
self.cache.get_all_valid_metrics().await
}
/// Get all cached metrics (including expired ones) for broadcasting
pub async fn get_all_cached_metrics(&self) -> Vec<Metric> {
self.cache.get_all_cached_metrics().await
}
/// Cache warm-up: collect and cache high-priority metrics
pub async fn warm_cache<F>(&self, collector_fn: F)
where
F: Fn(&str) -> Option<Metric>,
{
if !self.config.enabled {
return;
}
let high_priority_patterns = ["cpu_load_*", "memory_usage_*"];
let mut warmed_count = 0;
for pattern in &high_priority_patterns {
// This is a simplified warm-up - in practice, you'd iterate through
// known metric names or use a registry
if pattern.starts_with("cpu_load_") {
for suffix in &["1min", "5min", "15min"] {
let metric_name = format!("cpu_load_{}", suffix);
if let Some(metric) = collector_fn(&metric_name) {
self.cache_metric(metric).await;
warmed_count += 1;
}
}
}
}
if warmed_count > 0 {
info!("Cache warmed with {} metrics", warmed_count);
}
}
/// Get cache configuration
pub fn get_config(&self) -> &CacheConfig {
&self.config
}
/// Get cache tier interval for a metric
pub fn get_cache_interval(&self, metric_name: &str) -> u64 {
self.config.get_cache_interval(metric_name)
}
}

103
agent/src/cache/mod.rs vendored
View File

@ -24,26 +24,6 @@ impl ConfigurableCache {
}
}
/// Check if metric should be collected based on cache tier
pub async fn should_collect(&self, metric_name: &str) -> bool {
if !self.config.enabled {
return true;
}
let cache = self.cache.read().await;
if let Some(cached_metric) = cache.get(metric_name) {
let cache_interval = self.config.get_cache_interval(metric_name);
let elapsed = cached_metric.collected_at.elapsed().as_secs();
// Should collect if cache interval has passed
elapsed >= cache_interval
} else {
// Not cached yet, should collect
true
}
}
/// Store metric in cache
pub async fn store_metric(&self, metric: Metric) {
if !self.config.enabled {
@ -69,50 +49,6 @@ impl ConfigurableCache {
// Cached metric (debug logging disabled for performance)
}
/// Get cached metric if valid
pub async fn get_cached_metric(&self, metric_name: &str) -> Option<Metric> {
if !self.config.enabled {
return None;
}
let mut cache = self.cache.write().await;
if let Some(cached_metric) = cache.get_mut(metric_name) {
let cache_interval = self.config.get_cache_interval(metric_name);
let elapsed = cached_metric.collected_at.elapsed().as_secs();
if elapsed < cache_interval {
cached_metric.access_count += 1;
// Cache hit (debug logging disabled for performance)
return Some(cached_metric.metric.clone());
} else {
// Cache expired (debug logging disabled for performance)
}
}
None
}
/// Get all cached metrics that are still valid
pub async fn get_all_valid_metrics(&self) -> Vec<Metric> {
if !self.config.enabled {
return vec![];
}
let cache = self.cache.read().await;
let mut valid_metrics = Vec::new();
for (metric_name, cached_metric) in cache.iter() {
let cache_interval = self.config.get_cache_interval(metric_name);
let elapsed = cached_metric.collected_at.elapsed().as_secs();
if elapsed < cache_interval {
valid_metrics.push(cached_metric.metric.clone());
}
}
valid_metrics
}
/// Get all cached metrics (including expired ones) for broadcasting
pub async fn get_all_cached_metrics(&self) -> Vec<Metric> {
@ -162,43 +98,4 @@ impl ConfigurableCache {
}
}
/// Get cache statistics
pub async fn get_stats(&self) -> CacheStats {
let cache = self.cache.read().await;
let mut stats_by_tier = HashMap::new();
for (_metric_name, cached_metric) in cache.iter() {
let tier_name = cached_metric.tier
.as_ref()
.map(|t| t.description.clone())
.unwrap_or_else(|| "default".to_string());
let tier_stats = stats_by_tier.entry(tier_name).or_insert(TierStats {
count: 0,
total_access_count: 0,
});
tier_stats.count += 1;
tier_stats.total_access_count += cached_metric.access_count;
}
CacheStats {
total_entries: cache.len(),
stats_by_tier,
enabled: self.config.enabled,
}
}
}
#[derive(Debug)]
pub struct CacheStats {
pub total_entries: usize,
pub stats_by_tier: HashMap<String, TierStats>,
pub enabled: bool,
}
#[derive(Debug)]
pub struct TierStats {
pub count: usize,
pub total_access_count: u64,
}

View File

@ -1,74 +0,0 @@
use super::{Collector, CollectorError};
use crate::cache::MetricCacheManager;
use cm_dashboard_shared::Metric;
use async_trait::async_trait;
use std::sync::Arc;
use tracing::{debug, instrument};
/// Wrapper that adds caching to any collector
pub struct CachedCollector {
name: String,
inner: Box<dyn Collector>,
cache_manager: Arc<MetricCacheManager>,
}
impl CachedCollector {
pub fn new(
name: String,
inner: Box<dyn Collector>,
cache_manager: Arc<MetricCacheManager>
) -> Self {
Self {
name,
inner,
cache_manager,
}
}
}
#[async_trait]
impl Collector for CachedCollector {
fn name(&self) -> &str {
&self.name
}
#[instrument(skip(self), fields(collector = %self.name))]
async fn collect(&self) -> Result<Vec<Metric>, CollectorError> {
// First, get all metrics this collector would normally produce
let all_metrics = self.inner.collect().await?;
let mut result_metrics = Vec::new();
let mut metrics_to_collect = Vec::new();
// Check cache for each metric
for metric in all_metrics {
if let Some(cached_metric) = self.cache_manager.get_cached_metric(&metric.name).await {
// Use cached version
result_metrics.push(cached_metric);
debug!("Using cached metric: {}", metric.name);
} else {
// Need to collect this metric
metrics_to_collect.push(metric.name.clone());
result_metrics.push(metric);
}
}
// Cache the newly collected metrics
for metric in &result_metrics {
if metrics_to_collect.contains(&metric.name) {
self.cache_manager.cache_metric(metric.clone()).await;
debug!("Cached new metric: {} (tier: {}s)",
metric.name,
self.cache_manager.get_cache_interval(&metric.name));
}
}
if !metrics_to_collect.is_empty() {
debug!("Collected {} new metrics, used {} cached metrics",
metrics_to_collect.len(),
result_metrics.len() - metrics_to_collect.len());
}
Ok(result_metrics)
}
}

View File

@ -7,16 +7,4 @@ pub enum CollectorError {
#[error("Failed to parse value '{value}': {error}")]
Parse { value: String, error: String },
#[error("System command failed: {command}: {error}")]
CommandFailed { command: String, error: String },
#[error("Configuration error: {message}")]
Configuration { message: String },
#[error("Metric calculation error: {message}")]
Calculation { message: String },
#[error("Timeout error: operation took longer than {timeout_ms}ms")]
Timeout { timeout_ms: u64 },
}

View File

@ -2,7 +2,6 @@ use async_trait::async_trait;
use cm_dashboard_shared::Metric;
use std::time::Duration;
pub mod cached_collector;
pub mod cpu;
pub mod memory;
pub mod disk;

View File

@ -46,9 +46,6 @@ pub const DEFAULT_SMART_WEAR_CRITICAL: f32 = 90.0;
// Backup configuration
pub const DEFAULT_BACKUP_MAX_AGE_HOURS: u64 = 48;
// Cache configuration
pub const DEFAULT_CACHE_TTL_SECONDS: u64 = 30;
pub const DEFAULT_CACHE_MAX_ENTRIES: usize = 10000;
// Notification configuration (from legacy)
pub const DEFAULT_SMTP_HOST: &str = "localhost";

View File

@ -18,7 +18,6 @@ use crate::metrics::MetricStore;
use crate::ui::TuiApp;
pub struct Dashboard {
config: DashboardConfig,
zmq_consumer: ZmqConsumer,
zmq_command_sender: ZmqCommandSender,
metric_store: MetricStore,
@ -111,7 +110,6 @@ impl Dashboard {
info!("Dashboard initialization complete");
Ok(Self {
config,
zmq_consumer,
zmq_command_sender,
metric_store,

View File

@ -1,136 +1,14 @@
use cm_dashboard_shared::{Metric, Status};
use std::time::Instant;
pub mod store;
pub mod subscription;
pub use store::MetricStore;
/// Widget types that can subscribe to metrics
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum WidgetType {
Cpu,
Memory,
Storage,
Services,
Backup,
Hosts,
Alerts,
}
/// Historical metric data point
#[derive(Debug, Clone)]
pub struct MetricDataPoint {
pub metric: Metric,
pub received_at: Instant,
}
/// Metric filtering and selection utilities
pub mod filter {
use super::*;
/// Filter metrics by widget type subscription
pub fn filter_metrics_for_widget<'a>(
metrics: &'a [Metric],
subscriptions: &[String],
) -> Vec<&'a Metric> {
metrics
.iter()
.filter(|metric| subscriptions.contains(&metric.name))
.collect()
}
/// Get metrics by pattern matching
pub fn filter_metrics_by_pattern<'a>(
metrics: &'a [Metric],
pattern: &str,
) -> Vec<&'a Metric> {
if pattern.is_empty() {
return metrics.iter().collect();
}
metrics
.iter()
.filter(|metric| metric.name.contains(pattern))
.collect()
}
/// Aggregate status from multiple metrics
pub fn aggregate_widget_status(metrics: &[&Metric]) -> Status {
if metrics.is_empty() {
return Status::Unknown;
}
let statuses: Vec<Status> = metrics.iter().map(|m| m.status).collect();
Status::aggregate(&statuses)
}
}
/// Widget metric subscription definitions
pub mod subscriptions {
/// CPU widget metric subscriptions
pub const CPU_WIDGET_METRICS: &[&str] = &[
"cpu_load_1min",
"cpu_load_5min",
"cpu_load_15min",
"cpu_temperature_celsius",
"cpu_frequency_mhz",
];
/// Memory widget metric subscriptions
pub const MEMORY_WIDGET_METRICS: &[&str] = &[
"memory_usage_percent",
"memory_total_gb",
"memory_used_gb",
"memory_available_gb",
"memory_swap_total_gb",
"memory_swap_used_gb",
"disk_tmp_size_mb",
"disk_tmp_total_mb",
"disk_tmp_usage_percent",
];
/// Storage widget metric subscriptions
pub const STORAGE_WIDGET_METRICS: &[&str] = &[
"disk_nvme0_temperature_celsius",
"disk_nvme0_wear_percent",
"disk_nvme0_spare_percent",
"disk_nvme0_hours",
"disk_nvme0_capacity_gb",
"disk_nvme0_usage_gb",
"disk_nvme0_usage_percent",
];
/// Services widget metric subscriptions
/// Note: Individual service metrics are dynamically discovered
/// Pattern: "service_{name}_status" and "service_{name}_memory_mb"
pub const SERVICES_WIDGET_METRICS: &[&str] = &[
// Individual service metrics will be matched by pattern in the widget
// e.g., "service_sshd_status", "service_nginx_status", etc.
];
/// Backup widget metric subscriptions
pub const BACKUP_WIDGET_METRICS: &[&str] = &[
"backup_overall_status",
"backup_duration_seconds",
"backup_last_run_timestamp",
"backup_total_services",
"backup_total_repo_size_gb",
"backup_services_completed_count",
"backup_services_failed_count",
"backup_services_disabled_count",
];
/// Get all metric subscriptions for a widget type
pub fn get_widget_subscriptions(widget_type: super::WidgetType) -> &'static [&'static str] {
match widget_type {
super::WidgetType::Cpu => CPU_WIDGET_METRICS,
super::WidgetType::Memory => MEMORY_WIDGET_METRICS,
super::WidgetType::Storage => STORAGE_WIDGET_METRICS,
super::WidgetType::Services => SERVICES_WIDGET_METRICS,
super::WidgetType::Backup => BACKUP_WIDGET_METRICS,
super::WidgetType::Hosts => &[], // Hosts widget doesn't subscribe to specific metrics
super::WidgetType::Alerts => &[], // Alerts widget aggregates from all metrics
}
}
}

View File

@ -1,9 +1,9 @@
use cm_dashboard_shared::{Metric, Status};
use cm_dashboard_shared::Metric;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use tracing::{debug, info, warn};
use super::{MetricDataPoint, WidgetType, subscriptions};
use super::MetricDataPoint;
/// Central metric storage for the dashboard
pub struct MetricStore {
@ -54,7 +54,6 @@ impl MetricStore {
// Add to history
host_history.push(MetricDataPoint {
metric,
received_at: now,
});
}
@ -80,6 +79,7 @@ impl MetricStore {
}
/// Get all current metrics for a host
#[allow(dead_code)]
pub fn get_host_metrics(&self, hostname: &str) -> Option<&HashMap<String, Metric>> {
self.current_metrics.get(hostname)
}
@ -93,36 +93,7 @@ impl MetricStore {
}
}
/// Get metrics for a specific widget type
pub fn get_metrics_for_widget(&self, hostname: &str, widget_type: WidgetType) -> Vec<&Metric> {
let subscriptions = subscriptions::get_widget_subscriptions(widget_type);
if let Some(host_metrics) = self.get_host_metrics(hostname) {
subscriptions
.iter()
.filter_map(|&metric_name| host_metrics.get(metric_name))
.collect()
} else {
Vec::new()
}
}
/// Get aggregated status for a widget
pub fn get_widget_status(&self, hostname: &str, widget_type: WidgetType) -> Status {
let metrics = self.get_metrics_for_widget(hostname, widget_type);
if metrics.is_empty() {
Status::Unknown
} else {
let statuses: Vec<Status> = metrics.iter().map(|m| m.status).collect();
Status::aggregate(&statuses)
}
}
/// Get list of all hosts with metrics
pub fn get_hosts(&self) -> Vec<String> {
self.current_metrics.keys().cloned().collect()
}
/// Get connected hosts (hosts with recent updates)
pub fn get_connected_hosts(&self, timeout: Duration) -> Vec<String> {
@ -140,45 +111,7 @@ impl MetricStore {
.collect()
}
/// Get last update timestamp for a host
pub fn get_last_update(&self, hostname: &str) -> Option<Instant> {
self.last_update.get(hostname).copied()
}
/// Check if host is considered connected
pub fn is_host_connected(&self, hostname: &str, timeout: Duration) -> bool {
if let Some(&last_update) = self.last_update.get(hostname) {
Instant::now().duration_since(last_update) <= timeout
} else {
false
}
}
/// Get metric value as specific type (helper function)
pub fn get_metric_value_f32(&self, hostname: &str, metric_name: &str) -> Option<f32> {
self.get_metric(hostname, metric_name)?
.value
.as_f32()
}
/// Get metric value as string (helper function)
pub fn get_metric_value_string(&self, hostname: &str, metric_name: &str) -> Option<String> {
Some(self.get_metric(hostname, metric_name)?
.value
.as_string())
}
/// Get historical data for a metric
pub fn get_metric_history(&self, hostname: &str, metric_name: &str) -> Vec<&MetricDataPoint> {
if let Some(history) = self.historical_metrics.get(hostname) {
history
.iter()
.filter(|dp| dp.metric.name == metric_name)
.collect()
} else {
Vec::new()
}
}
/// Cleanup old data and enforce limits
fn cleanup_host_data(&mut self, hostname: &str) {
@ -199,32 +132,4 @@ impl MetricStore {
}
}
/// Get storage statistics
pub fn get_stats(&self) -> MetricStoreStats {
let total_current_metrics: usize = self.current_metrics
.values()
.map(|host_metrics| host_metrics.len())
.sum();
let total_historical_metrics: usize = self.historical_metrics
.values()
.map(|history| history.len())
.sum();
MetricStoreStats {
total_hosts: self.current_metrics.len(),
total_current_metrics,
total_historical_metrics,
connected_hosts: self.get_connected_hosts(Duration::from_secs(30)).len(),
}
}
}
/// Metric store statistics
#[derive(Debug, Clone)]
pub struct MetricStoreStats {
pub total_hosts: usize,
pub total_current_metrics: usize,
pub total_historical_metrics: usize,
pub connected_hosts: usize,
}

View File

@ -1,160 +0,0 @@
use std::collections::{HashMap, HashSet};
use tracing::{debug, info};
use super::{WidgetType, subscriptions};
/// Manages metric subscriptions for widgets
pub struct SubscriptionManager {
/// Widget subscriptions: widget_type -> metric_names
widget_subscriptions: HashMap<WidgetType, Vec<String>>,
/// All subscribed metric names (for efficient filtering)
all_subscribed_metrics: HashSet<String>,
/// Active hosts
active_hosts: HashSet<String>,
}
impl SubscriptionManager {
pub fn new() -> Self {
let mut manager = Self {
widget_subscriptions: HashMap::new(),
all_subscribed_metrics: HashSet::new(),
active_hosts: HashSet::new(),
};
// Initialize default subscriptions
manager.initialize_default_subscriptions();
manager
}
/// Initialize default widget subscriptions
fn initialize_default_subscriptions(&mut self) {
// Subscribe CPU widget to CPU metrics
self.subscribe_widget(
WidgetType::Cpu,
subscriptions::CPU_WIDGET_METRICS.iter().map(|&s| s.to_string()).collect()
);
// Subscribe Memory widget to memory metrics
self.subscribe_widget(
WidgetType::Memory,
subscriptions::MEMORY_WIDGET_METRICS.iter().map(|&s| s.to_string()).collect()
);
info!("Initialized default widget subscriptions for {} widgets",
self.widget_subscriptions.len());
}
/// Subscribe a widget to specific metrics
pub fn subscribe_widget(&mut self, widget_type: WidgetType, metric_names: Vec<String>) {
debug!("Subscribing {:?} widget to {} metrics", widget_type, metric_names.len());
// Update widget subscriptions
self.widget_subscriptions.insert(widget_type, metric_names.clone());
// Update global subscription set
for metric_name in metric_names {
self.all_subscribed_metrics.insert(metric_name);
}
debug!("Total subscribed metrics: {}", self.all_subscribed_metrics.len());
}
/// Get metrics subscribed by a specific widget
pub fn get_widget_subscriptions(&self, widget_type: WidgetType) -> Vec<String> {
self.widget_subscriptions
.get(&widget_type)
.cloned()
.unwrap_or_default()
}
/// Get all subscribed metric names
pub fn get_all_subscribed_metrics(&self) -> Vec<String> {
self.all_subscribed_metrics.iter().cloned().collect()
}
/// Check if a metric is subscribed by any widget
pub fn is_metric_subscribed(&self, metric_name: &str) -> bool {
self.all_subscribed_metrics.contains(metric_name)
}
/// Add a host to active hosts list
pub fn add_host(&mut self, hostname: String) {
if self.active_hosts.insert(hostname.clone()) {
info!("Added host to subscription manager: {}", hostname);
}
}
/// Remove a host from active hosts list
pub fn remove_host(&mut self, hostname: &str) {
if self.active_hosts.remove(hostname) {
info!("Removed host from subscription manager: {}", hostname);
}
}
/// Get list of active hosts
pub fn get_active_hosts(&self) -> Vec<String> {
self.active_hosts.iter().cloned().collect()
}
/// Get subscription statistics
pub fn get_stats(&self) -> SubscriptionStats {
SubscriptionStats {
total_widgets_subscribed: self.widget_subscriptions.len(),
total_metric_subscriptions: self.all_subscribed_metrics.len(),
active_hosts: self.active_hosts.len(),
}
}
/// Update widget subscription dynamically
pub fn update_widget_subscription(&mut self, widget_type: WidgetType, metric_names: Vec<String>) {
// Remove old subscriptions from global set
if let Some(old_subscriptions) = self.widget_subscriptions.get(&widget_type) {
for old_metric in old_subscriptions {
// Only remove if no other widget subscribes to it
let still_subscribed = self.widget_subscriptions
.iter()
.filter(|(&wt, _)| wt != widget_type)
.any(|(_, metrics)| metrics.contains(old_metric));
if !still_subscribed {
self.all_subscribed_metrics.remove(old_metric);
}
}
}
// Add new subscriptions
self.subscribe_widget(widget_type, metric_names);
debug!("Updated subscription for {:?} widget", widget_type);
}
/// Get widgets that subscribe to a specific metric
pub fn get_widgets_for_metric(&self, metric_name: &str) -> Vec<WidgetType> {
self.widget_subscriptions
.iter()
.filter_map(|(&widget_type, metrics)| {
if metrics.contains(&metric_name.to_string()) {
Some(widget_type)
} else {
None
}
})
.collect()
}
}
impl Default for SubscriptionManager {
fn default() -> Self {
Self::new()
}
}
/// Subscription manager statistics
#[derive(Debug, Clone)]
pub struct SubscriptionStats {
pub total_widgets_subscribed: usize,
pub total_metric_subscriptions: usize,
pub active_hosts: usize,
}

View File

@ -14,7 +14,7 @@ pub mod widgets;
pub mod theme;
use widgets::{CpuWidget, MemoryWidget, ServicesWidget, BackupWidget, Widget};
use crate::metrics::{MetricStore, WidgetType};
use crate::metrics::MetricStore;
use cm_dashboard_shared::{Metric, Status};
use theme::{Theme, Layout as ThemeLayout, Typography, Components, StatusIcons};
@ -82,8 +82,14 @@ impl TuiApp {
let all_metrics = metric_store.get_metrics_for_host(&hostname);
if !all_metrics.is_empty() {
// Get metrics first while hostname is borrowed
let cpu_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Cpu);
let memory_metrics = metric_store.get_metrics_for_widget(&hostname, WidgetType::Memory);
let cpu_metrics: Vec<&Metric> = all_metrics.iter()
.filter(|m| m.name.starts_with("cpu_") || m.name.contains("c_state_") || m.name.starts_with("process_top_"))
.copied()
.collect();
let memory_metrics: Vec<&Metric> = all_metrics.iter()
.filter(|m| m.name.starts_with("memory_") || m.name.starts_with("disk_tmp_"))
.copied()
.collect();
let service_metrics: Vec<&Metric> = all_metrics.iter()
.filter(|m| m.name.starts_with("service_"))
.copied()

View File

@ -3,6 +3,7 @@ use ratatui::widgets::{Block, Borders};
use cm_dashboard_shared::Status;
/// Complete terminal color palette matching your configuration
#[allow(dead_code)]
pub struct TerminalColors {
// Primary colors
pub foreground: Color,
@ -86,6 +87,7 @@ impl Default for TerminalColors {
/// Comprehensive theming engine for dashboard consistency
pub struct Theme;
#[allow(dead_code)]
impl Theme {
fn colors() -> &'static TerminalColors {
static COLORS: std::sync::OnceLock<TerminalColors> = std::sync::OnceLock::new();
@ -243,22 +245,6 @@ impl StatusIcons {
}
}
/// Get style for status-colored text (icon + text in status color)
pub fn get_status_style(status: Status) -> Style {
let color = match status {
Status::Ok => Theme::success(), // Green
Status::Warning => Theme::warning(), // Yellow
Status::Critical => Theme::error(), // Red
Status::Unknown => Theme::muted_text(), // Gray
};
Style::default().fg(color).bg(Theme::background())
}
/// Format text with status icon (entire line uses status color)
pub fn format_with_status(status: Status, text: &str) -> String {
let icon = Self::get_icon(status);
format!("{} {}", icon, text)
}
/// Create spans with status icon colored and text in foreground color
pub fn create_status_spans(status: Status, text: &str) -> Vec<ratatui::text::Span<'static>> {
@ -282,17 +268,11 @@ impl StatusIcons {
]
}
/// Get style for secondary text with status icon (icon in status color, text in secondary)
pub fn get_secondary_with_status_style(_status: Status) -> Style {
// For now, use secondary color but we could enhance this later
// The icon color will come from the status color in the formatted text
Style::default().fg(Theme::secondary_text()).bg(Theme::background())
}
}
impl Components {
/// Standard widget block with title using bright foreground for title
pub fn widget_block(title: &str) -> Block {
pub fn widget_block(title: &str) -> Block<'_> {
Block::default()
.title(title)
.borders(Borders::ALL)
@ -300,40 +280,6 @@ impl Components {
.title_style(Style::default().fg(Theme::border_title()).bg(Theme::background()))
}
/// Status bar style
pub fn status_bar() -> Style {
Style::default()
.fg(Theme::muted_text())
.bg(Theme::background())
}
/// CPU usage styling based on percentage
pub fn cpu_usage_style(percentage: u16) -> Style {
Style::default()
.fg(Theme::cpu_color(percentage))
.bg(Theme::background())
}
/// Memory usage styling based on percentage
pub fn memory_usage_style(percentage: u16) -> Style {
Style::default()
.fg(Theme::memory_color(percentage))
.bg(Theme::background())
}
/// Service status styling
pub fn service_status_style(status: Status) -> Style {
Style::default()
.fg(Theme::status_color(status))
.bg(Theme::background())
}
/// Backup item styling
pub fn backup_item_style(status: Status) -> Style {
Style::default()
.fg(Theme::status_color(status))
.bg(Theme::background())
}
}
impl Typography {
@ -367,10 +313,4 @@ impl Typography {
.add_modifier(Modifier::BOLD)
}
/// Status text with dynamic colors
pub fn status(status: Status) -> Style {
Style::default()
.fg(Theme::status_color(status))
.bg(Theme::background())
}
}

View File

@ -107,19 +107,6 @@ impl BackupWidget {
}
}
/// Format service status counts
fn format_service_counts(&self) -> String {
let completed = self.services_completed_count.unwrap_or(0);
let failed = self.services_failed_count.unwrap_or(0);
let disabled = self.services_disabled_count.unwrap_or(0);
let total = self.total_services.unwrap_or(0);
if failed > 0 {
format!("{}{}{}◯ ({})", completed, failed, disabled, total)
} else {
format!("{}{}◯ ({})", completed, disabled, total)
}
}
/// Format disk usage in format "usedGB/totalGB"
fn format_repo_size(&self) -> String {
@ -160,30 +147,7 @@ impl BackupWidget {
}
}
/// Get status indicator symbol
fn get_status_symbol(&self) -> &str {
match self.overall_status {
Status::Ok => "",
Status::Warning => "",
Status::Critical => "",
Status::Unknown => "?",
}
}
/// Format size in GB to appropriate unit (kB/MB/GB)
fn format_size_gb(size_gb: f32) -> String {
if size_gb >= 1.0 {
format!("{:.1}GB", size_gb)
} else if size_gb >= 0.001 {
let size_mb = size_gb * 1024.0;
format!("{:.1}MB", size_mb)
} else if size_gb >= 0.000001 {
let size_kb = size_gb * 1024.0 * 1024.0;
format!("{:.0}kB", size_kb)
} else {
format!("{:.0}B", size_gb * 1024.0 * 1024.0 * 1024.0)
}
}
/// Format product name display
fn format_product_name(&self) -> String {

View File

@ -118,36 +118,6 @@ impl ServicesWidget {
disk_str)
}
/// Format sub-service line (indented, no memory/disk columns) - returns text without icon for span formatting
fn format_sub_service_line(&self, name: &str, info: &ServiceInfo) -> String {
// Truncate long sub-service names to fit layout (accounting for indentation)
let short_name = if name.len() > 18 {
format!("{}...", &name[..15])
} else {
name.to_string()
};
// Sub-services show latency if available, otherwise status
let status_str = if let Some(latency) = info.latency_ms {
if latency < 0.0 {
"timeout".to_string()
} else {
format!("{:.0}ms", latency)
}
} else {
match info.widget_status {
Status::Ok => "active".to_string(),
Status::Warning => "inactive".to_string(),
Status::Critical => "failed".to_string(),
Status::Unknown => "unknown".to_string(),
}
};
// Indent sub-services with " ├─ " prefix (no memory/disk columns)
format!(" ├─ {:<20} {:<10}",
short_name,
status_str)
}
/// Create spans for sub-service with icon next to name
fn create_sub_service_spans(&self, name: &str, info: &ServiceInfo) -> Vec<ratatui::text::Span<'static>> {