config-easy 0.2.1

A small interactive settings menu for command-line Rust applications
Documentation
use rusqlite::params;

use crate::identifier::validate_identifier;
use crate::{ConfigEasyError, ConfigMenu, SettingRow, SettingsQuery, SettingsStore};

/// A [`SettingsStore`] backed by a `rusqlite::Connection`.
pub struct RusqliteStore<'a> {
    conn: &'a rusqlite::Connection,
}

impl<'a> RusqliteStore<'a> {
    /// Creates a settings store backed by the provided SQLite connection.
    pub fn new(conn: &'a rusqlite::Connection) -> Self {
        Self { conn }
    }
}

impl SettingsStore for RusqliteStore<'_> {
    fn load_settings(
        &self,
        query: &SettingsQuery<'_>,
    ) -> Result<Vec<SettingRow>, Box<dyn std::error::Error + Send + Sync>> {
        validate_query(query)?;

        let sql = format!(
            "SELECT {}, {} FROM {} ORDER BY {}",
            query.key_column, query.value_column, query.table, query.order_by
        );

        let mut statement = self.conn.prepare(&sql)?;
        let rows = statement.query_map([], |row| {
            Ok(SettingRow {
                key: row.get(0)?,
                value: row.get(1)?,
            })
        })?;

        Ok(rows.collect::<Result<Vec<_>, _>>()?)
    }

    fn update_setting(
        &self,
        query: &SettingsQuery<'_>,
        key: &str,
        value: &str,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        validate_query(query)?;

        let sql = format!(
            "UPDATE {} SET {} = ?1 WHERE {} = ?2",
            query.table, query.value_column, query.key_column
        );
        self.conn.execute(&sql, params![value, key])?;

        Ok(())
    }
}

/// Creates a configurable settings menu for a `rusqlite` connection.
pub fn builder<'a>(conn: &'a rusqlite::Connection) -> ConfigMenu<'a, RusqliteStore<'a>> {
    crate::builder(RusqliteStore::new(conn))
}

/// Runs a settings menu with the default SQLite configuration.
pub fn run(conn: &rusqlite::Connection) -> Result<(), ConfigEasyError> {
    builder(conn).run()
}

fn validate_query(query: &SettingsQuery<'_>) -> Result<(), ConfigEasyError> {
    validate_identifier(query.table)?;
    validate_identifier(query.key_column)?;
    validate_identifier(query.value_column)?;
    validate_identifier(query.order_by)?;
    Ok(())
}

#[cfg(test)]
mod tests;