diff --git a/apps/service/src/config.rs b/apps/service/src/config.rs index 0b8d52a..9242d3b 100644 --- a/apps/service/src/config.rs +++ b/apps/service/src/config.rs @@ -11,7 +11,7 @@ pub enum Error { } /// Location privacy setting -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] pub enum LocationPrivacy { /// Disable location tracking completely #[serde(rename = "disabled")] @@ -21,15 +21,10 @@ pub enum LocationPrivacy { CountryOnly, /// Full location details (city, country, region) #[serde(rename = "full")] + #[default] Full, } -impl Default for LocationPrivacy { - fn default() -> Self { - LocationPrivacy::Full - } -} - #[derive(Debug, Serialize, Deserialize)] pub struct Config { pub zeromq: ZeroMQ, diff --git a/apps/service/src/location.rs b/apps/service/src/location.rs index 529f04d..d231a20 100644 --- a/apps/service/src/location.rs +++ b/apps/service/src/location.rs @@ -130,11 +130,7 @@ async fn fetch_location_from_ip() -> Result { let country = if !response.country_code.is_empty() { Some(response.country_code.clone()) } else { None }; - let region = if let Some(ref cc) = country { - Some(Location::region_from_country(cc).to_string()) - } else { - None - }; + let region = country.as_ref().map(|cc| Location::region_from_country(cc).to_string()); Ok(Location::new(city, country, region)) } @@ -154,11 +150,11 @@ pub fn init_location_cache(update_interval_secs: u64, privacy_level: LocationPri /// Initialize location from static config (for backwards compatibility) pub fn init_location(location: Location) { init_location_cache(0, LocationPrivacy::Full); // 0 = never auto-update - if let Some(cache) = LOCATION_CACHE.get() { - if let Ok(mut cache) = cache.write() { - cache.location = location; - cache.last_update = Instant::now(); - } + if let Some(cache) = LOCATION_CACHE.get() + && let Ok(mut cache) = cache.write() + { + cache.location = location; + cache.last_update = Instant::now(); } } diff --git a/apps/service/src/orchestrator/mod.rs b/apps/service/src/orchestrator/mod.rs index 5ba7ad4..01951a2 100644 --- a/apps/service/src/orchestrator/mod.rs +++ b/apps/service/src/orchestrator/mod.rs @@ -146,10 +146,10 @@ impl Orchestrator { } // Share with P2P network if enabled - if self.p2p_network.is_enabled() { - if let Err(e) = self.p2p_network.share_result(&signed_result).await { - error!("Failed to share result with P2P network: {}", e); - } + if self.p2p_network.is_enabled() + && let Err(e) = self.p2p_network.share_result(&signed_result).await + { + error!("Failed to share result with P2P network: {}", e); } // Log the result diff --git a/apps/service/src/tui/events/edit.rs b/apps/service/src/tui/events/edit.rs index 8785b1e..3153f96 100644 --- a/apps/service/src/tui/events/edit.rs +++ b/apps/service/src/tui/events/edit.rs @@ -38,10 +38,10 @@ pub async fn handle_edit_popup( } KeyCode::End => { - if state.edit_field_index < 2 { - if let Some(text) = state.get_current_field_text() { - state.text_cursor = text.len(); - } + if state.edit_field_index < 2 + && let Some(text) = state.get_current_field_text() + { + state.text_cursor = text.len(); } } @@ -105,12 +105,12 @@ pub async fn handle_edit_popup( } } else { // Validate before saving - if state.validate_current_monitor() { - if let Some(m) = state.edit_monitor.take() { - db.save_monitor(&m).await?; - state.close_edit(); - state.refresh_monitors_and_results(db).await?; - } + if state.validate_current_monitor() + && let Some(m) = state.edit_monitor.take() + { + db.save_monitor(&m).await?; + state.close_edit(); + state.refresh_monitors_and_results(db).await?; } } } @@ -120,10 +120,10 @@ pub async fn handle_edit_popup( KeyCode::Right => { if state.edit_field_index < 2 { // Move cursor right in text field - if let Some(text) = state.get_current_field_text() { - if state.text_cursor < text.len() { - state.text_cursor += 1; - } + if let Some(text) = state.get_current_field_text() + && state.text_cursor < text.len() + { + state.text_cursor += 1; } } else { // Adjust values in other fields @@ -260,38 +260,38 @@ pub async fn handle_edit_popup( } } '+' => { - if let Some(m) = state.edit_monitor.as_mut() { - if state.edit_field_index == 3 { - m.interval_seconds = m.interval_seconds.saturating_add(5); - } + if let Some(m) = state.edit_monitor.as_mut() + && state.edit_field_index == 3 + { + m.interval_seconds = m.interval_seconds.saturating_add(5); } } '-' => { - if let Some(m) = state.edit_monitor.as_mut() { - if state.edit_field_index == 3 { - m.interval_seconds = m.interval_seconds.saturating_sub(5).max(1); - } + if let Some(m) = state.edit_monitor.as_mut() + && state.edit_field_index == 3 + { + m.interval_seconds = m.interval_seconds.saturating_sub(5).max(1); } } '[' => { - if let Some(m) = state.edit_monitor.as_mut() { - if state.edit_field_index == 4 { - m.timeout_seconds = m.timeout_seconds.saturating_sub(1).max(1); - } + if let Some(m) = state.edit_monitor.as_mut() + && state.edit_field_index == 4 + { + m.timeout_seconds = m.timeout_seconds.saturating_sub(1).max(1); } } ']' => { - if let Some(m) = state.edit_monitor.as_mut() { - if state.edit_field_index == 4 { - m.timeout_seconds = m.timeout_seconds.saturating_add(1); - } + if let Some(m) = state.edit_monitor.as_mut() + && state.edit_field_index == 4 + { + m.timeout_seconds = m.timeout_seconds.saturating_add(1); } } ' ' => { - if let Some(m) = state.edit_monitor.as_mut() { - if state.edit_field_index == 5 { - m.enabled = !m.enabled; - } + if let Some(m) = state.edit_monitor.as_mut() + && state.edit_field_index == 5 + { + m.enabled = !m.enabled; } } _ => {} diff --git a/apps/service/src/tui/events/keyboard.rs b/apps/service/src/tui/events/keyboard.rs index 8321c08..c4cd346 100644 --- a/apps/service/src/tui/events/keyboard.rs +++ b/apps/service/src/tui/events/keyboard.rs @@ -124,14 +124,14 @@ pub async fn handle_main_view( // Toggle enabled status KeyCode::Char(' ') | KeyCode::Char('t') if key.modifiers.is_empty() => { - if state.focus == Focus::Monitors { - if let Some(mo) = state.monitors.get(state.selected).cloned() { - let mut nm = mo; - nm.enabled = !nm.enabled; - db.save_monitor(&nm).await?; - state.refresh_monitors_and_results(db).await?; - state.last_refresh = std::time::Instant::now(); - } + if state.focus == Focus::Monitors + && let Some(mo) = state.monitors.get(state.selected).cloned() + { + let mut nm = mo; + nm.enabled = !nm.enabled; + db.save_monitor(&nm).await?; + state.refresh_monitors_and_results(db).await?; + state.last_refresh = std::time::Instant::now(); } } diff --git a/apps/service/src/tui/events/mouse.rs b/apps/service/src/tui/events/mouse.rs index 52231f4..0135ae9 100644 --- a/apps/service/src/tui/events/mouse.rs +++ b/apps/service/src/tui/events/mouse.rs @@ -12,90 +12,87 @@ pub async fn handle_mouse( mouse: MouseEvent, db: &DatabaseImpl, ) -> Result { - if let Some(areas) = &state.areas { - match mouse.kind { - MouseEventKind::Down(MouseButton::Left) => { - let x = mouse.column; - let y = mouse.row; + if let Some(areas) = &state.areas + && let MouseEventKind::Down(MouseButton::Left) = mouse.kind + { + let x = mouse.column; + let y = mouse.row; - // Footer action buttons - for (label, rect) in &areas.action_buttons { - if is_in_rect(x, y, rect) { - match label.as_str() { - "Add" => { - let mut m = Monitor::new("".into(), "".into(), "http".into()); - m.interval_seconds = 30; - m.timeout_seconds = 10; - state.edit_monitor = Some(m); - state.show_edit = true; - state.is_add_form = true; - state.edit_field_index = 0; - state.text_cursor = 0; - } - "Edit" => { - if let Some(mo) = state.monitors.get(state.selected).cloned() { - state.edit_monitor = Some(mo); - state.show_edit = true; - state.is_add_form = false; - state.edit_field_index = 0; - state.text_cursor = 0; - } - } - "Delete" => { - if state.monitors.get(state.selected).is_some() { - state.show_delete_confirm = true; - } - } - "Refresh" => { - state.monitors = db.get_enabled_monitors().await?; - if let Some(mo) = state.monitors.get(state.selected) { - state.results = db.get_recent_results(mo.uuid, 50).await?; - } else { - state.results.clear(); - } - } - "Help" => { - state.show_help = true; - } - "Quit" => { - return Ok(true); // Signal to quit - } - _ => {} + // Footer action buttons + for (label, rect) in &areas.action_buttons { + if is_in_rect(x, y, rect) { + match label.as_str() { + "Add" => { + let mut m = Monitor::new("".into(), "".into(), "http".into()); + m.interval_seconds = 30; + m.timeout_seconds = 10; + state.edit_monitor = Some(m); + state.show_edit = true; + state.is_add_form = true; + state.edit_field_index = 0; + state.text_cursor = 0; + } + "Edit" => { + if let Some(mo) = state.monitors.get(state.selected).cloned() { + state.edit_monitor = Some(mo); + state.show_edit = true; + state.is_add_form = false; + state.edit_field_index = 0; + state.text_cursor = 0; } } - } - - // Monitors area - let mrect = areas.monitors; - if is_in_rect(x, y, &mrect) { - state.focus = Focus::Monitors; - // Approximate row idx: account for border and title - let inner_y = y.saturating_sub(mrect.y + 1); - if inner_y < mrect.height.saturating_sub(2) { - let idx = inner_y as usize; - if idx < state.monitors.len() { - state.selected = idx; + "Delete" => { + if state.monitors.get(state.selected).is_some() { + state.show_delete_confirm = true; } + } + "Refresh" => { + state.monitors = db.get_enabled_monitors().await?; if let Some(mo) = state.monitors.get(state.selected) { state.results = db.get_recent_results(mo.uuid, 50).await?; + } else { + state.results.clear(); } } + "Help" => { + state.show_help = true; + } + "Quit" => { + return Ok(true); // Signal to quit + } + _ => {} } + } + } - // Results area - let rrect = areas.results; - if is_in_rect(x, y, &rrect) { - state.focus = Focus::Results; - let inner_y = y.saturating_sub(rrect.y + 2); // header row - if inner_y < rrect.height.saturating_sub(3) { - let idx = inner_y as usize; - if idx < state.results.len() { - state.selected_result = idx; - } - } + // Monitors area + let mrect = areas.monitors; + if is_in_rect(x, y, &mrect) { + state.focus = Focus::Monitors; + // Approximate row idx: account for border and title + let inner_y = y.saturating_sub(mrect.y + 1); + if inner_y < mrect.height.saturating_sub(2) { + let idx = inner_y as usize; + if idx < state.monitors.len() { + state.selected = idx; + } + if let Some(mo) = state.monitors.get(state.selected) { + state.results = db.get_recent_results(mo.uuid, 50).await?; + } + } + } + + // Results area + let rrect = areas.results; + if is_in_rect(x, y, &rrect) { + state.focus = Focus::Results; + let inner_y = y.saturating_sub(rrect.y + 2); // header row + if inner_y < rrect.height.saturating_sub(3) { + let idx = inner_y as usize; + if idx < state.results.len() { + state.selected_result = idx; } } - _ => {} } } diff --git a/apps/service/src/tui/ui/footer.rs b/apps/service/src/tui/ui/footer.rs index 11beb56..ed8c02e 100644 --- a/apps/service/src/tui/ui/footer.rs +++ b/apps/service/src/tui/ui/footer.rs @@ -27,7 +27,7 @@ pub fn render(f: &mut Frame, area: Rect, show_help: bool) -> Vec<(String, Rect)> let keys = ["A", "E", "D", "R", "H/?", "Q/Esc"]; for (i, (label, key)) in labels.iter().zip(keys.iter()).enumerate() { - let text = format!("{}: {}", key, label); + let text = format!("{key}: {label}"); let btn = Paragraph::new(Line::from(Span::styled( text, Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD), diff --git a/apps/service/src/tui/ui/monitors.rs b/apps/service/src/tui/ui/monitors.rs index 44cf524..0271306 100644 --- a/apps/service/src/tui/ui/monitors.rs +++ b/apps/service/src/tui/ui/monitors.rs @@ -21,7 +21,7 @@ pub fn render(f: &mut Frame, area: Rect, state: &AppState) { }; ListItem::new(Line::from(vec![ - Span::styled(format!("{}", m.name), style), + Span::styled(m.name.to_string(), style), Span::styled( if m.enabled { " ✓ " } else { " ✗ " }, Style::default().fg(if m.enabled { Color::Green } else { Color::Red }), diff --git a/apps/service/src/tui/ui/network.rs b/apps/service/src/tui/ui/network.rs index f8fd931..a2daeae 100644 --- a/apps/service/src/tui/ui/network.rs +++ b/apps/service/src/tui/ui/network.rs @@ -1,8 +1,8 @@ +use ratatui::Frame; use ratatui::layout::Rect; use ratatui::style::{Color, Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Paragraph}; -use ratatui::Frame; use crate::tui::state::AppState; @@ -36,29 +36,19 @@ pub fn render(f: &mut Frame, area: Rect, state: &AppState) { if state.p2p_enabled { "Connected" } else { "Offline" } ))); lines.push(Line::from("")); - lines.push(Line::from(Span::styled( - "Peers", - Style::default().fg(Color::Yellow), - ))); + lines.push(Line::from(Span::styled("Peers", Style::default().fg(Color::Yellow)))); lines.push(Line::from(" Connected: 342")); lines.push(Line::from(" Total: 1,250")); lines.push(Line::from(" Health: 98%")); lines.push(Line::from("")); - lines.push(Line::from(Span::styled( - "Metrics", - Style::default().fg(Color::Yellow), - ))); + lines.push(Line::from(Span::styled("Metrics", Style::default().fg(Color::Yellow)))); lines.push(Line::from(" Share: 45%")); lines.push(Line::from(" Score: 8,750")); lines.push(Line::from(" BW: 2.3/10 GB")); lines.push(Line::from(" Checks: 2,840 today")); - let widget = Paragraph::new(lines).block( - Block::default() - .borders(Borders::ALL) - .title(title) - .border_style(focus_style), - ); + let widget = Paragraph::new(lines) + .block(Block::default().borders(Borders::ALL).title(title).border_style(focus_style)); f.render_widget(widget, area); } diff --git a/apps/service/src/tui/ui/popups/delete.rs b/apps/service/src/tui/ui/popups/delete.rs index 056c271..4042158 100644 --- a/apps/service/src/tui/ui/popups/delete.rs +++ b/apps/service/src/tui/ui/popups/delete.rs @@ -35,7 +35,7 @@ pub fn render(f: &mut Frame, size: Rect, state: &AppState) { Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), )), Line::from(""), - Line::from(format!("Are you sure you want to delete '{}' ?", name)), + Line::from(format!("Are you sure you want to delete '{name}' ?")), Line::from(""), Line::from("Y: Yes N/Esc: No"), ]) diff --git a/apps/service/src/tui/ui/popups/edit.rs b/apps/service/src/tui/ui/popups/edit.rs index a96b290..38578eb 100644 --- a/apps/service/src/tui/ui/popups/edit.rs +++ b/apps/service/src/tui/ui/popups/edit.rs @@ -68,7 +68,7 @@ pub fn render(f: &mut Frame, size: Rect, state: &AppState) { lines.push(Line::from(vec![ Span::raw(prefix), - Span::styled(format!("{}: ", label), Style::default().fg(Color::Gray)), + Span::styled(format!("{label}: "), Style::default().fg(Color::Gray)), Span::styled(display_value, field_style), ])); } @@ -78,7 +78,7 @@ pub fn render(f: &mut Frame, size: Rect, state: &AppState) { // Show validation error if present if let Some(err) = &state.validation_error { lines.push(Line::from(Span::styled( - format!("⚠ {}", err), + format!("⚠ {err}"), Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), ))); lines.push(Line::from("")); diff --git a/apps/service/src/tui/ui/popups/result_detail.rs b/apps/service/src/tui/ui/popups/result_detail.rs index ed37642..96ecc51 100644 --- a/apps/service/src/tui/ui/popups/result_detail.rs +++ b/apps/service/src/tui/ui/popups/result_detail.rs @@ -21,7 +21,7 @@ fn format_location( parts.push(country.clone()); } if let Some(region) = region { - parts.push(format!("({})", region)); + parts.push(format!("({region})")); } parts.join(", ") } else if let Some(region) = region { @@ -70,7 +70,7 @@ pub fn render(f: &mut Frame, size: Rect, state: &AppState) { "Code: {}", r.status_code.map(|v| v.to_string()).unwrap_or_else(|| "-".into()) )), - Line::from(format!("Location: {}", location)), + Line::from(format!("Location: {location}")), Line::from(format!("Error: {}", r.error_message.clone().unwrap_or_default())), Line::from(format!("Peer: {}", r.peer_id)), Line::from(""), diff --git a/apps/service/src/tui/ui/results.rs b/apps/service/src/tui/ui/results.rs index b9789c3..6199237 100644 --- a/apps/service/src/tui/ui/results.rs +++ b/apps/service/src/tui/ui/results.rs @@ -19,7 +19,7 @@ fn format_time(time: SystemTime) -> String { let minutes = (total_secs % 3600) / 60; let seconds = total_secs % 60; - format!("{:02}:{:02}:{:02}", hours, minutes, seconds) + format!("{hours:02}:{minutes:02}:{seconds:02}") } /// Format location from city, country, and region. diff --git a/apps/service/src/tui/ui/stats.rs b/apps/service/src/tui/ui/stats.rs index 4dabe16..7e97a26 100644 --- a/apps/service/src/tui/ui/stats.rs +++ b/apps/service/src/tui/ui/stats.rs @@ -28,21 +28,21 @@ pub fn render(f: &mut Frame, area: Rect, state: &AppState) { let monitor = &state.monitors[state.selected]; let truncated_name: String = monitor.name.chars().take(20).collect(); lines.push(Line::from(Span::styled( - format!("Monitor: {}", truncated_name), + format!("Monitor: {truncated_name}"), Style::default().fg(Color::Yellow), ))); - lines.push(Line::from(format!(" Uptime: {:.1}%", uptime))); - lines.push(Line::from(format!(" Success: {} / {}", success_count, total_checks))); - lines.push(Line::from(format!(" Latency: {} ms", avg_latency))); + lines.push(Line::from(format!(" Uptime: {uptime:.1}%"))); + lines.push(Line::from(format!(" Success: {success_count} / {total_checks}"))); + lines.push(Line::from(format!(" Latency: {avg_latency} ms"))); } else { lines.push(Line::from("No monitor selected")); } lines.push(Line::from("")); lines.push(Line::from(Span::styled("Global Stats", Style::default().fg(Color::Yellow)))); - lines.push(Line::from(format!(" Total: {}", total_monitors))); - lines.push(Line::from(format!(" Online: {}", online_monitors))); - lines.push(Line::from(format!(" Avg: {:.1}%", global_uptime))); + lines.push(Line::from(format!(" Total: {total_monitors}"))); + lines.push(Line::from(format!(" Online: {online_monitors}"))); + lines.push(Line::from(format!(" Avg: {global_uptime:.1}%"))); let widget = Paragraph::new(lines) .block(Block::default().borders(Borders::ALL).title(title).border_style(focus_style)); diff --git a/apps/service/src/validation.rs b/apps/service/src/validation.rs index 6b11b4d..549fade 100644 --- a/apps/service/src/validation.rs +++ b/apps/service/src/validation.rs @@ -40,8 +40,7 @@ pub fn validate_http_endpoint(target: &str) -> ValidationResult { let scheme = url.scheme(); if scheme != "http" && scheme != "https" { return ValidationResult::err(format!( - "Invalid scheme '{}'. Must be http or https", - scheme + "Invalid scheme '{scheme}'. Must be http or https" )); } @@ -56,7 +55,7 @@ pub fn validate_http_endpoint(target: &str) -> ValidationResult { if !target.contains("://") { ValidationResult::err("URL must include scheme (http:// or https://)") } else { - ValidationResult::err(format!("Invalid URL: {}", e)) + ValidationResult::err(format!("Invalid URL: {e}")) } } } @@ -87,7 +86,7 @@ pub fn validate_https_endpoint(target: &str) -> ValidationResult { if !target.contains("://") { ValidationResult::err("URL must include scheme (https://)") } else { - ValidationResult::err(format!("Invalid URL: {}", e)) + ValidationResult::err(format!("Invalid URL: {e}")) } } } @@ -157,7 +156,7 @@ pub fn validate_monitor_target(target: &str, check_type: &str) -> ValidationResu "https" => validate_https_endpoint(target), "tcp" => validate_tcp_endpoint(target), "icmp" => validate_icmp_endpoint(target), - _ => ValidationResult::err(format!("Unknown check type: {}", check_type)), + _ => ValidationResult::err(format!("Unknown check type: {check_type}")), } } @@ -191,7 +190,7 @@ pub fn validate_interval(interval: u64) -> ValidationResult { /// Validate monitor timeout pub fn validate_timeout(timeout: u64, interval: u64) -> ValidationResult { - if timeout < 1 { + if timeout == 0 { return ValidationResult::err("Timeout must be at least 1 second"); }