sql-cli 1.73.1

SQL query tool for CSV/JSON with both interactive TUI and non-interactive CLI modes - perfect for exploration and automation
Documentation
use crate::debug::debug_trace::{DebugSection, DebugSectionBuilder, DebugTrace, Priority};
use std::sync::Arc;
use std::sync::RwLock;

/// Tracks memory usage over time
#[derive(Clone)]
pub struct MemoryTracker {
    history: Arc<RwLock<Vec<MemorySnapshot>>>,
    max_history: usize,
}

#[derive(Clone, Debug)]
struct MemorySnapshot {
    _timestamp: std::time::Instant,
    memory_kb: usize,
}

impl MemoryTracker {
    #[must_use]
    pub fn new(max_history: usize) -> Self {
        Self {
            history: Arc::new(RwLock::new(Vec::new())),
            max_history,
        }
    }

    pub fn record_snapshot(&self) {
        if let Some(memory_kb) = crate::utils::memory_tracker::get_process_memory_kb() {
            let snapshot = MemorySnapshot {
                _timestamp: std::time::Instant::now(),
                memory_kb,
            };

            if let Ok(mut history) = self.history.write() {
                history.push(snapshot);
                // Keep only the last max_history entries
                if history.len() > self.max_history {
                    let drain_count = history.len() - self.max_history;
                    history.drain(0..drain_count);
                }
            }
        }
    }

    #[must_use]
    pub fn get_current_memory_mb(&self) -> Option<f64> {
        crate::utils::memory_tracker::get_process_memory_kb().map(|kb| kb as f64 / 1024.0)
    }

    #[must_use]
    pub fn get_history(&self) -> Vec<(usize, f64)> {
        if let Ok(history) = self.history.read() {
            history
                .iter()
                .enumerate()
                .map(|(idx, snapshot)| (idx, snapshot.memory_kb as f64 / 1024.0))
                .collect()
        } else {
            Vec::new()
        }
    }
}

impl Default for MemoryTracker {
    fn default() -> Self {
        Self::new(100)
    }
}

/// Debug trace implementation for memory tracking
pub struct MemoryDebugProvider {
    tracker: MemoryTracker,
}

impl MemoryDebugProvider {
    #[must_use]
    pub fn new(tracker: MemoryTracker) -> Self {
        Self { tracker }
    }
}

impl DebugTrace for MemoryDebugProvider {
    fn name(&self) -> &'static str {
        "Memory"
    }

    fn debug_sections(&self) -> Vec<DebugSection> {
        let mut builder = DebugSectionBuilder::new();

        builder.add_section("MEMORY USAGE", "", Priority::MEMORY);

        // Current memory usage
        if let Some(memory_mb) = self.tracker.get_current_memory_mb() {
            builder.add_field("Current Memory", format!("{memory_mb:.2} MB"));
        } else {
            builder.add_field("Current Memory", "Unable to read");
        }

        // Memory history
        let history = self.tracker.get_history();
        if history.is_empty() {
            builder.add_line("No memory history available");
        } else {
            builder.add_line("");
            builder.add_line("Memory History (last readings):");

            // Calculate statistics
            let values: Vec<f64> = history.iter().map(|(_, mb)| *mb).collect();
            let min = values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
            let max = values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
            let avg = values.iter().sum::<f64>() / values.len() as f64;

            builder.add_field("  Min", format!("{min:.2} MB"));
            builder.add_field("  Max", format!("{max:.2} MB"));
            builder.add_field("  Avg", format!("{avg:.2} MB"));

            // Show last few readings
            builder.add_line("");
            builder.add_line("  Recent readings:");
            for (_, mb) in history.iter().rev().take(5) {
                builder.add_line(format!("    {mb:.2} MB"));
            }

            // Memory growth
            if history.len() >= 2 {
                let first = history.first().map_or(0.0, |(_, mb)| *mb);
                let last = history.last().map_or(0.0, |(_, mb)| *mb);
                let growth = last - first;
                let growth_pct = if first > 0.0 {
                    (growth / first) * 100.0
                } else {
                    0.0
                };

                builder.add_line("");
                builder.add_field("  Growth", format!("{growth:+.2} MB ({growth_pct:+.1}%)"));
            }
        }

        // System memory info (if available)
        #[cfg(target_os = "linux")]
        {
            if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
                let mut total_mb = 0.0;
                let mut available_mb = 0.0;

                for line in meminfo.lines() {
                    if line.starts_with("MemTotal:") {
                        if let Some(kb) = line.split_whitespace().nth(1) {
                            if let Ok(kb_val) = kb.parse::<f64>() {
                                total_mb = kb_val / 1024.0;
                            }
                        }
                    } else if line.starts_with("MemAvailable:") {
                        if let Some(kb) = line.split_whitespace().nth(1) {
                            if let Ok(kb_val) = kb.parse::<f64>() {
                                available_mb = kb_val / 1024.0;
                            }
                        }
                    }
                }

                if total_mb > 0.0 {
                    builder.add_line("");
                    builder.add_line("System Memory:");
                    builder.add_field("  Total", format!("{total_mb:.2} MB"));
                    builder.add_field("  Available", format!("{available_mb:.2} MB"));
                    let used_pct = ((total_mb - available_mb) / total_mb) * 100.0;
                    builder.add_field("  Used", format!("{used_pct:.1}%"));
                }
            }
        }

        builder.build()
    }

    fn debug_summary(&self) -> Option<String> {
        self.tracker
            .get_current_memory_mb()
            .map(|mb| format!("{mb:.2} MB"))
    }
}