summaryrefslogtreecommitdiffstats
path: root/src/local/database.rs
blob: b94a6445f27f2857989de819cd9064089ef00c79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::path::Path;

use eyre::Result;

use rusqlite::NO_PARAMS;
use rusqlite::{params, Connection};

use crate::History;

pub trait Database {
    fn save(&self, h: History) -> Result<()>;
    fn list(&self) -> Result<()>;
}

// Intended for use on a developer machine and not a sync server.
// TODO: implement IntoIterator
pub struct SqliteDatabase {
    conn: Connection,
}

impl SqliteDatabase {
    pub fn new(path: impl AsRef<Path>) -> Result<SqliteDatabase> {
        let path = path.as_ref();
        debug!("opening sqlite database at {:?}", path);

        let create = !path.exists();
        if create {
            if let Some(dir) = path.parent() {
                std::fs::create_dir_all(dir)?;
            }
        }

        let conn = Connection::open(path)?;

        if create {
            Self::setup_db(&conn)?;
        }

        Ok(SqliteDatabase { conn })
    }

    fn setup_db(conn: &Connection) -> Result<()> {
        debug!("running sqlite database setup");

        conn.execute(
            "create table if not exists history (
                id integer primary key,
                timestamp integer not null,
                command text not null,
                cwd text not null
            )",
            NO_PARAMS,
        )?;

        Ok(())
    }
}

impl Database for SqliteDatabase {
    fn save(&self, h: History) -> Result<()> {
        debug!("saving history to sqlite");

        self.conn.execute(
            "insert into history (
                timestamp,
                command,
                cwd
            ) values (?1, ?2, ?3)",
            params![h.timestamp, h.command, h.cwd],
        )?;

        Ok(())
    }

    fn list(&self) -> Result<()> {
        debug!("listing history");

        let mut stmt = self
            .conn
            .prepare("SELECT timestamp, command, cwd FROM history")?;
        let history_iter = stmt.query_map(params![], |row| {
            Ok(History {
                timestamp: row.get(0)?,
                command: row.get(1)?,
                cwd: row.get(2)?,
            })
        })?;

        for h in history_iter {
            let h = h.unwrap();

            println!("{}:{}:{}", h.timestamp, h.cwd, h.command);
        }

        Ok(())
    }
}