sqlite-collections 0.1.0

Rust collection types backed by sqlite database files.
Documentation
use super::Error;
use crate::serializer::Serializer;
use crate::{identifier::Identifier, AsConnection};
use rusqlite::{params, OptionalExtension};

use std::{borrow::Borrow, marker::PhantomData};

#[derive(Debug)]
pub struct Iter<'db, 'tbl, S, C>
where
    S: Serializer,
    C: AsConnection,
{
    connection: C,
    database: Identifier<'db>,
    table: Identifier<'tbl>,
    front: Option<S::Buffer>,
    back: Option<S::Buffer>,
    size: usize,
    serializer: PhantomData<S>,
}

impl<'db, 'tbl, S, C> Iter<'db, 'tbl, S, C>
where
    S: Serializer,
    C: AsConnection,
{
    pub fn new(
        connection: C,
        database: Identifier<'db>,
        table: Identifier<'tbl>,
    ) -> rusqlite::Result<Self> {
        let mut new = Self {
            connection,
            database,
            table,
            size: 0,
            front: None,
            back: None,
            serializer: PhantomData,
        };
        new.update_size()?;
        Ok(new)
    }

    fn update_size(&mut self) -> rusqlite::Result<usize> {
        self.size = self.select("COUNT(*)", "", |row| row.get(0))?;
        Ok(self.size)
    }

    fn select<U, F>(&self, selection: &str, order: &str, f: F) -> rusqlite::Result<U>
    where
        F: FnOnce(&rusqlite::Row<'_>) -> rusqlite::Result<U>,
    {
        let database = &self.database;
        let table = &self.table;

        match (self.front.as_ref(), self.back.as_ref()) {
            (None, None) => self
                .connection
                .as_connection()
                .prepare_cached(&format!(
                    "SELECT {selection} FROM {database}.{table} {order}"
                ))?
                .query_row([], f),
            (Some(front), None) => self
                .connection
                .as_connection()
                .prepare_cached(&format!(
                    "SELECT {selection} FROM {database}.{table} WHERE key > ? {order}"
                ))?
                .query_row(params![front], f),
            (None, Some(back)) => self
                .connection
                .as_connection()
                .prepare_cached(&format!(
                    "SELECT {selection} FROM {database}.{table} WHERE key < ? {order}"
                ))?
                .query_row(params![back], f),
            (Some(front), Some(back)) => self
                .connection
                .as_connection()
                .prepare_cached(&format!(
                    "SELECT {selection} FROM {database}.{table} WHERE key > ? AND key < ? {order}"
                ))?
                .query_row(params![front, back], f),
        }
    }
}

impl<'db, 'tbl, S, C> Iterator for Iter<'db, 'tbl, S, C>
where
    S: Serializer,
    C: AsConnection,
{
    type Item = Result<S::Target, Error<S>>;

    fn next(&mut self) -> Option<Self::Item> {
        let next: rusqlite::Result<Option<S::Buffer>> = self
            .select("key", "ORDER BY key ASC", |row| row.get(0))
            .optional();
        match next {
            Ok(Some(serialized)) => {
                let ret = match S::deserialize(&serialized) {
                    Ok(value) => Some(Ok(value)),
                    Err(e) => Some(Err(Error::Deserialize(e))),
                };
                self.front = Some(serialized);
                if let Err(e) = self.update_size() {
                    return Some(Err(e.into()));
                }
                ret
            }
            Ok(None) => {
                self.size = 0;
                None
            }
            Err(e) => Some(Err(e.into())),
        }
    }

    /// return how many elements are in left in the iterator as of this call.
    /// This can change during iteration if you are iterating in autocommit
    /// mode, or hold the iterator across transactions. This is why this
    /// iterator doesn't implement ExactSizeIterator
    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.size, Some(self.size))
    }
}

impl<'db, 'tbl, S, C> DoubleEndedIterator for Iter<'db, 'tbl, S, C>
where
    S: Serializer,
    C: AsConnection,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        let next: rusqlite::Result<Option<S::Buffer>> = self
            .select("key", "ORDER BY key DESC", |row| row.get(0))
            .optional();
        match next {
            Ok(Some(serialized)) => {
                let ret = match S::deserialize(&serialized) {
                    Ok(value) => Some(Ok(value)),
                    Err(e) => Some(Err(Error::Deserialize(e))),
                };
                self.back = Some(serialized);
                if let Err(e) = self.update_size() {
                    return Some(Err(e.into()));
                }
                ret
            }
            Ok(None) => {
                self.size = 0;
                None
            }
            Err(e) => Some(Err(e.into())),
        }
    }
}