ccd-cli 1.0.0-beta.4

Bootstrap and validate Continuous Context Development repositories
use anyhow::Result;
use rusqlite::{Connection, OptionalExtension};

use crate::state::work_stream_decay::WorkStreamDecayState;

pub(crate) fn read(conn: &Connection) -> Result<Option<WorkStreamDecayState>> {
    conn.query_row(
        "SELECT session_id, consecutive_no_progress, last_outcome, updated_at_epoch_s,
                last_progress_at_epoch_s
         FROM work_stream_decay
         WHERE id = 1",
        [],
        |row| {
            Ok(WorkStreamDecayState {
                session_id: row.get(0)?,
                consecutive_no_progress: row.get(1)?,
                last_outcome: row.get(2)?,
                updated_at_epoch_s: row.get(3)?,
                last_progress_at_epoch_s: row.get(4)?,
            })
        },
    )
    .optional()
    .map_err(Into::into)
}

pub(crate) fn write(conn: &Connection, state: &WorkStreamDecayState) -> Result<()> {
    conn.execute(
        "INSERT OR REPLACE INTO work_stream_decay
            (id, session_id, consecutive_no_progress, last_outcome, updated_at_epoch_s,
             last_progress_at_epoch_s)
         VALUES (1, ?1, ?2, ?3, ?4, ?5)",
        rusqlite::params![
            &state.session_id,
            state.consecutive_no_progress,
            &state.last_outcome,
            state.updated_at_epoch_s,
            state.last_progress_at_epoch_s,
        ],
    )?;
    Ok(())
}

pub(crate) fn delete(conn: &Connection) -> Result<()> {
    conn.execute("DELETE FROM work_stream_decay WHERE id = 1", [])?;
    Ok(())
}

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

    fn test_conn() -> Connection {
        let conn = Connection::open_in_memory().unwrap();
        schema::initialize(&conn, None).unwrap();
        conn
    }

    #[test]
    fn write_and_read_roundtrip() {
        let conn = test_conn();
        let state = WorkStreamDecayState {
            session_id: "ses_decay".to_owned(),
            consecutive_no_progress: 2,
            last_outcome: "no_progress".to_owned(),
            updated_at_epoch_s: 42,
            last_progress_at_epoch_s: Some(12),
        };

        write(&conn, &state).unwrap();
        assert_eq!(read(&conn).unwrap(), Some(state));
    }

    #[test]
    fn delete_clears_row() {
        let conn = test_conn();
        write(
            &conn,
            &WorkStreamDecayState {
                session_id: "ses_decay".to_owned(),
                consecutive_no_progress: 1,
                last_outcome: "neutral".to_owned(),
                updated_at_epoch_s: 7,
                last_progress_at_epoch_s: None,
            },
        )
        .unwrap();

        delete(&conn).unwrap();
        assert!(read(&conn).unwrap().is_none());
    }
}