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
 98
 99
100
101
102
103
104
105
#[macro_use]
extern crate derive_more;

use chrono::NaiveDateTime;
use sqlx::{error::Error as DBError, SqlitePool};
use std::path::Path;

mod db;
mod event;
pub mod migration;
pub use event::*;

#[derive(Debug, Clone)]
pub struct Historian {
    pool: SqlitePool,
}

impl Historian {
    async fn raw_record(&self, script_id: i64, ty: &str, cmd: &str) -> Result<(), DBError> {
        sqlx::query!(
            "INSERT INTO events (script_id, type, cmd) VALUES(?, ?, ?)",
            script_id,
            ty,
            cmd
        )
        .execute(&self.pool)
        .await?;
        Ok(())
    }
    pub async fn new(hyper_scripter_path: impl AsRef<Path>) -> Result<Self, DBError> {
        db::get_pool(hyper_scripter_path)
            .await
            .map(|pool| Historian { pool })
    }

    pub async fn record(&self, event: Event<'_>) -> Result<(), DBError> {
        log::debug!("記錄事件 {:?}", event);
        let cmd = std::env::args().collect::<Vec<_>>().join(" ");
        let ty = event.data.get_type().to_string();
        match &event.data {
            EventData::Miss => {
                self.raw_record(event.script_id, &ty, &cmd).await?;
            }
            EventData::Read => {
                self.raw_record(event.script_id, &ty, &cmd).await?;
            }
            EventData::Exec(content) => {
                let mut content = Some(*content);
                let last_event = sqlx::query!(
                    "SELECT content FROM events
                WHERE type = ? AND script_id = ? AND NOT content IS NULL
                ORDER BY time DESC LIMIT 1",
                    ty,
                    event.script_id
                )
                .fetch_optional(&self.pool)
                .await?;
                if let Some(last_event) = last_event {
                    if last_event.content.as_ref().map(|s| s.as_str()) == content {
                        log::debug!("上次執行內容相同,不重覆記錄");
                        content = None;
                    }
                }
                sqlx::query!(
                    "INSERT INTO events (script_id, type, cmd, content) VALUES(?, ?, ?, ?)",
                    event.script_id,
                    ty,
                    cmd,
                    content,
                )
                .execute(&self.pool)
                .await?;
            }
            EventData::ExecDone(code) => {
                let code = code.to_string();
                sqlx::query!(
                    "INSERT INTO events (script_id, type, cmd, content) VALUES(?, ?, ?, ?)",
                    event.script_id,
                    ty,
                    cmd,
                    code
                )
                .execute(&self.pool)
                .await?;
            }
        }
        Ok(())
    }

    pub async fn last_time_of(&self, ty: EventType) -> Result<Vec<(i64, NaiveDateTime)>, DBError> {
        let ty = ty.to_string();
        let records = sqlx::query_as(
            "
        SELECT e.script_id, MAX(e.time) as time FROM events e
        WHERE type = ?
        GROUP BY e.script_id ORDER BY script_id
        ",
        )
        .bind(ty)
        .fetch_all(&self.pool)
        .await?;
        Ok(records)
        // Ok(records.into_iter().map(|r| (r.script_id, r.time)).collect())
    }
}