gloss-word 0.3.5

A simple English dictionary lookup utility
Documentation
use gloss_word::LookupMode;
use rusqlite::Connection;
use std::path::Path;

pub struct Cache {
    connection: Connection,
}

impl Cache {
    pub fn open(path: &Path) -> Result<Self, rusqlite::Error> {
        let connection = Connection::open(path)?;
        connection.execute_batch(
            "CREATE TABLE IF NOT EXISTS dictionary (
                word        TEXT UNIQUE NOT NULL,
                content     TEXT NOT NULL
            );
            CREATE TABLE IF NOT EXISTS etymology (
                word        TEXT UNIQUE NOT NULL,
                content     TEXT NOT NULL
            );",
        )?;
        Ok(Self { connection })
    }

    pub fn get(&self, word: &str, mode: LookupMode) -> Result<String, rusqlite::Error> {
        let query = format!("SELECT content FROM {} WHERE word = ?1", table_name(mode));
        let mut statement = self.connection.prepare(&query)?;
        statement.query_row([word], |row| row.get(0))
    }

    pub fn put(&self, word: &str, mode: LookupMode, content: &str) -> Result<(), rusqlite::Error> {
        let query = format!(
            "INSERT INTO {} (word, content) VALUES (?1, ?2)
             ON CONFLICT(word) DO UPDATE SET content = excluded.content",
            table_name(mode)
        );
        self.connection.execute(&query, [word, content])?;
        Ok(())
    }
}

const fn table_name(mode: LookupMode) -> &'static str {
    match mode {
        LookupMode::Definition => "dictionary",
        LookupMode::Etymology => "etymology",
    }
}

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

    fn test_cache() -> Cache {
        Cache::open(Path::new(":memory:")).unwrap()
    }

    #[test]
    fn round_trip_handles_apostrophes() {
        let cache = test_cache();

        cache
            .put("o'clock", LookupMode::Definition, "a time")
            .unwrap();

        assert_eq!(
            cache.get("o'clock", LookupMode::Definition).unwrap(),
            "a time"
        );
    }

    #[test]
    fn update_replaces_existing_content_in_the_selected_table() {
        let cache = test_cache();

        cache.put("forest", LookupMode::Etymology, "old").unwrap();
        cache.put("forest", LookupMode::Etymology, "new").unwrap();

        assert_eq!(cache.get("forest", LookupMode::Etymology).unwrap(), "new");
        assert!(cache.get("forest", LookupMode::Definition).is_err());
    }
}