luncheon 0.2.11

A library to make working with SQLITE in a more typed and traited way
Documentation
use std::{fmt::Display, time::Duration};

use rusqlite::{params, types::FromSql};

//#[cfg(all(feature = "serde", feature = "json"))]

use crate::{
    Database, Res, Table,
    insert::Insert,
    select::{Select, SelectBy},
    upsert::Upsert,
};

#[cfg(all(feature = "json", feature = "serde"))]
use serde_json::Value as JsonValue;

#[cfg(all(feature = "json", feature = "serde"))]
use serde_json::to_string as to_json_string;

pub struct SettingsTable;

impl SettingsTable {
    /// TODO: Not sure if this is a good idea
    pub fn insert_tuple<T: Insert<SettingsTable>>(database: &Database, item: T) -> Res<()> {
        database.insert(item)
    }
    pub fn upsert_tuple<T: Upsert<SettingsTable>>(database: &Database, item: T) -> Res<()> {
        database.upsert(item)
    }
    pub fn get<T: FromSql>(database: &Database, key: &str) -> Res<Option<T>> {
        let mut stmt = database.prepare_reader("SELECT key, value FROM settings WHERE key=?1")?;
        let rows: Vec<T> = stmt
            .query_map(params![key], |row| {
                let val: T = row.get(2)?;
                Ok(val)
            })?
            .flatten()
            .collect();
        if rows.len() == 1 {
            let r = rows.into_iter().nth(0);
            Ok(r)
        } else {
            Ok(None)
        }
    }
}

impl Table for SettingsTable {
    const TBL_NAME: &str = "settings";

    const CREATE_QUERY: &str = "CREATE TABLE settings (key, value ANY)";
}

impl<S: Display> Insert<SettingsTable> for (S, String) {
    fn insert(database: &crate::Database, item: (S, String)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn stmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2)")
    }
}

impl<S: Display> Insert<SettingsTable> for (S, u64) {
    fn insert(database: &crate::Database, item: (S, u64)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn stmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2)")
    }
}

impl<S: Display> Insert<SettingsTable> for (S, Duration) {
    fn insert(database: &crate::Database, item: (S, Duration)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1.as_secs()])?;
        Ok(())
    }

    fn stmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2)")
    }
}

impl<S: Display> Insert<SettingsTable> for (S, rusqlite::types::Value) {
    fn insert(database: &crate::Database, item: (S, rusqlite::types::Value)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn stmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2)")
    }
}

#[cfg(all(feature = "serde", feature = "json"))]
impl<S: Display> Insert<SettingsTable> for (S, JsonValue) {
    fn insert(database: &crate::Database, item: (S, JsonValue)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        let json = to_json_string(&item.1).map_err(|e| Errors::Custom(e.to_string()))?;
        stmt.execute(params![item.0.to_string(), json])?;
        Ok(())
    }

    fn stmt(database: &crate::Database) -> crate::Res<rusqlite::Statement> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2)")
    }
}

impl Select<(String, rusqlite::types::Value)> for SettingsTable {
    const SELECT: &str = "";

    fn func(row: &rusqlite::Row) -> Result<(String, rusqlite::types::Value), rusqlite::Error> {
        let key = row.get(0)?;
        let value = row.get(1)?;
        Ok((key, value))
    }
}

impl SelectBy<rusqlite::types::Value, String> for SettingsTable {
    const SELECT_BY: &str = "SELECT value FROM settings WHERE key=?1";

    fn func(row: &rusqlite::Row) -> Result<rusqlite::types::Value, rusqlite::Error> {
        let value = row.get(0)?;
        Ok(value)
    }
}

impl<S: Display> Upsert<SettingsTable> for (S, String) {
    fn upsert(database: &crate::Database, item: (S, String)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn upstmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value WHERE key = excluded.key")
    }
}

impl<S: Display> Upsert<SettingsTable> for (S, u64) {
    fn upsert(database: &crate::Database, item: (S, u64)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn upstmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value WHERE key = excluded.key")
    }
}

impl<S: Display> Upsert<SettingsTable> for (S, Duration) {
    fn upsert(database: &crate::Database, item: (S, Duration)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1.as_secs()])?;
        Ok(())
    }

    fn upstmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value WHERE key = excluded.key")
    }
}

impl<S: Display> Upsert<SettingsTable> for (S, rusqlite::types::Value) {
    fn upsert(database: &crate::Database, item: (S, rusqlite::types::Value)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        stmt.execute(params![item.0.to_string(), item.1])?;
        Ok(())
    }

    fn upstmt(database: &crate::Database) -> crate::Res<rusqlite::Statement<'_>> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value WHERE key = excluded.key")
    }
}

#[cfg(all(feature = "serde", feature = "json"))]
impl<S: Display> Upsert<SettingsTable> for (S, JsonValue) {
    fn upsert(database: &crate::Database, item: (S, JsonValue)) -> crate::Res<()> {
        let mut stmt = Self::stmt(database)?;
        let json = to_json_string(&item.1).map_err(|e| Errors::Custom(e.to_string()))?;
        stmt.execute(params![item.0.to_string(), json])?;
        Ok(())
    }

    fn upstmt(database: &crate::Database) -> crate::Res<rusqlite::Statement> {
        database.prepare_writer("INSERT INTO settings (key, value) VALUE (?1, ?2) ON CONFLICT (key) DO UPDATE SET value = excluded.value WHERE key = excluded.key")
    }
}