hematite-db 0.1.0

A small embeddable SQL database.
Documentation
//! Relational cursors.

use super::record::StoredRow;

#[derive(Debug, Clone)]
pub struct TableCursor {
    rows: Vec<StoredRow>,
    position: Option<usize>,
}

impl TableCursor {
    pub fn new(mut rows: Vec<StoredRow>) -> Self {
        rows.sort_by_key(|row| row.row_id);
        Self {
            rows,
            position: None,
        }
    }

    pub fn first(&mut self) -> bool {
        if self.rows.is_empty() {
            self.position = None;
            return false;
        }
        self.position = Some(0);
        true
    }

    pub fn next(&mut self) -> bool {
        let Some(position) = self.position else {
            return false;
        };
        let next = position + 1;
        if next < self.rows.len() {
            self.position = Some(next);
            true
        } else {
            self.position = None;
            false
        }
    }

    pub fn seek_rowid(&mut self, rowid: u64) -> bool {
        let found = self
            .rows
            .binary_search_by_key(&rowid, |row| row.row_id)
            .ok();
        self.position = found;
        found.is_some()
    }

    pub fn current(&self) -> Option<&StoredRow> {
        self.position.and_then(|index| self.rows.get(index))
    }

    pub fn is_valid(&self) -> bool {
        self.current().is_some()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndexEntry {
    pub key: Vec<u8>,
    pub row_id: u64,
}

#[derive(Debug, Clone)]
pub struct IndexCursor {
    entries: Vec<IndexEntry>,
    position: Option<usize>,
}

impl IndexCursor {
    pub fn new(mut entries: Vec<IndexEntry>) -> Self {
        entries.sort_by(|l, r| l.key.cmp(&r.key).then(l.row_id.cmp(&r.row_id)));
        Self {
            entries,
            position: None,
        }
    }

    pub fn first(&mut self) -> bool {
        if self.entries.is_empty() {
            self.position = None;
            return false;
        }
        self.position = Some(0);
        true
    }

    pub fn next(&mut self) -> bool {
        let Some(position) = self.position else {
            return false;
        };
        let next = position + 1;
        if next < self.entries.len() {
            self.position = Some(next);
            true
        } else {
            self.position = None;
            false
        }
    }

    pub fn seek_key(&mut self, key: &[u8]) -> bool {
        let mut left = 0usize;
        let mut right = self.entries.len();

        while left < right {
            let mid = left + (right - left) / 2;
            if self.entries[mid].key.as_slice() < key {
                left = mid + 1;
            } else {
                right = mid;
            }
        }

        let found = self
            .entries
            .get(left)
            .filter(|entry| entry.key.as_slice() == key)
            .map(|_| left);
        self.position = found;
        found.is_some()
    }

    pub fn current(&self) -> Option<&IndexEntry> {
        self.position.and_then(|index| self.entries.get(index))
    }

    pub fn is_valid(&self) -> bool {
        self.current().is_some()
    }
}