sabiql 1.9.0

A fast, driver-less TUI for browsing and editing PostgreSQL databases
Documentation
use std::collections::VecDeque;
use std::sync::Arc;

use crate::domain::QueryResult;

#[derive(Debug, Clone)]
pub struct ResultHistory {
    entries: VecDeque<Arc<QueryResult>>,
    capacity: usize,
}

impl Default for ResultHistory {
    fn default() -> Self {
        Self::new(20)
    }
}

impl ResultHistory {
    pub fn new(capacity: usize) -> Self {
        Self {
            entries: VecDeque::with_capacity(capacity),
            capacity,
        }
    }

    pub fn push(&mut self, result: Arc<QueryResult>) {
        if self.entries.len() >= self.capacity {
            self.entries.pop_front();
        }
        self.entries.push_back(result);
    }

    pub fn get(&self, index: usize) -> Option<&QueryResult> {
        self.entries.get(index).map(|arc| &**arc)
    }

    pub fn len(&self) -> usize {
        self.entries.len()
    }

    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::QuerySource;

    fn make_result(query: &str) -> QueryResult {
        QueryResult::success(
            query.to_string(),
            vec!["col1".to_string()],
            vec![vec!["val1".to_string()]],
            10,
            QuerySource::Adhoc,
        )
    }

    #[test]
    fn push_and_get_returns_entries_in_order() {
        let mut history = ResultHistory::new(3);

        history.push(Arc::new(make_result("SELECT 1")));
        history.push(Arc::new(make_result("SELECT 2")));

        assert_eq!(history.get(0).unwrap().query, "SELECT 1");
        assert_eq!(history.get(1).unwrap().query, "SELECT 2");
        assert!(history.get(2).is_none());
    }

    #[test]
    fn len_returns_entry_count() {
        let mut history = ResultHistory::new(5);

        assert_eq!(history.len(), 0);

        history.push(Arc::new(make_result("SELECT 1")));
        assert_eq!(history.len(), 1);

        history.push(Arc::new(make_result("SELECT 2")));
        assert_eq!(history.len(), 2);
    }

    #[test]
    fn is_empty_returns_true_when_no_entries() {
        let history = ResultHistory::new(5);

        assert!(history.is_empty());
    }

    #[test]
    fn is_empty_returns_false_after_push() {
        let mut history = ResultHistory::new(5);

        history.push(Arc::new(make_result("SELECT 1")));

        assert!(!history.is_empty());
    }

    #[test]
    fn push_evicts_oldest_when_at_capacity() {
        let mut history = ResultHistory::new(2);

        history.push(Arc::new(make_result("SELECT 1")));
        history.push(Arc::new(make_result("SELECT 2")));
        history.push(Arc::new(make_result("SELECT 3")));

        // SELECT 1 should be evicted
        assert_eq!(history.get(0).unwrap().query, "SELECT 2");
        assert_eq!(history.get(1).unwrap().query, "SELECT 3");
        assert!(history.get(2).is_none());
    }
}