Skip to main content

mxr_store/
rules.rs

1use chrono::{DateTime, Utc};
2use sqlx::Row;
3
4pub struct RuleRecordInput<'a> {
5    pub id: &'a str,
6    pub name: &'a str,
7    pub enabled: bool,
8    pub priority: i32,
9    pub conditions_json: &'a str,
10    pub actions_json: &'a str,
11    pub created_at: DateTime<Utc>,
12    pub updated_at: DateTime<Utc>,
13}
14
15pub struct RuleLogInput<'a> {
16    pub rule_id: &'a str,
17    pub rule_name: &'a str,
18    pub message_id: &'a str,
19    pub actions_applied_json: &'a str,
20    pub timestamp: DateTime<Utc>,
21    pub success: bool,
22    pub error: Option<&'a str>,
23}
24
25impl super::Store {
26    pub async fn upsert_rule(&self, rule: RuleRecordInput<'_>) -> Result<(), sqlx::Error> {
27        sqlx::query(
28            "INSERT INTO rules (id, name, enabled, priority, conditions, actions, created_at, updated_at)
29             VALUES (?, ?, ?, ?, ?, ?, ?, ?)
30             ON CONFLICT(id) DO UPDATE SET
31                name = excluded.name,
32                enabled = excluded.enabled,
33                priority = excluded.priority,
34                conditions = excluded.conditions,
35                actions = excluded.actions,
36                updated_at = excluded.updated_at",
37        )
38        .bind(rule.id)
39        .bind(rule.name)
40        .bind(rule.enabled as i64)
41        .bind(rule.priority as i64)
42        .bind(rule.conditions_json)
43        .bind(rule.actions_json)
44        .bind(rule.created_at.to_rfc3339())
45        .bind(rule.updated_at.to_rfc3339())
46        .execute(self.writer())
47        .await?;
48        Ok(())
49    }
50
51    pub async fn list_rules(&self) -> Result<Vec<sqlx::sqlite::SqliteRow>, sqlx::Error> {
52        sqlx::query("SELECT * FROM rules ORDER BY priority ASC, created_at ASC")
53            .fetch_all(self.reader())
54            .await
55    }
56
57    pub async fn get_rule_by_id_or_name(
58        &self,
59        key: &str,
60    ) -> Result<Option<sqlx::sqlite::SqliteRow>, sqlx::Error> {
61        sqlx::query("SELECT * FROM rules WHERE id = ? OR name = ? ORDER BY priority ASC LIMIT 1")
62            .bind(key)
63            .bind(key)
64            .fetch_optional(self.reader())
65            .await
66    }
67
68    pub async fn delete_rule(&self, id: &str) -> Result<(), sqlx::Error> {
69        sqlx::query("DELETE FROM rules WHERE id = ?")
70            .bind(id)
71            .execute(self.writer())
72            .await?;
73        Ok(())
74    }
75
76    pub async fn insert_rule_log(&self, log: RuleLogInput<'_>) -> Result<(), sqlx::Error> {
77        sqlx::query(
78            "INSERT INTO rule_execution_log
79             (rule_id, rule_name, message_id, actions_applied, timestamp, success, error)
80             VALUES (?, ?, ?, ?, ?, ?, ?)",
81        )
82        .bind(log.rule_id)
83        .bind(log.rule_name)
84        .bind(log.message_id)
85        .bind(log.actions_applied_json)
86        .bind(log.timestamp.to_rfc3339())
87        .bind(log.success as i64)
88        .bind(log.error)
89        .execute(self.writer())
90        .await?;
91        Ok(())
92    }
93
94    pub async fn list_rule_logs(
95        &self,
96        rule_id: Option<&str>,
97        limit: u32,
98    ) -> Result<Vec<sqlx::sqlite::SqliteRow>, sqlx::Error> {
99        let mut sql = String::from("SELECT * FROM rule_execution_log");
100        if rule_id.is_some() {
101            sql.push_str(" WHERE rule_id = ?");
102        }
103        sql.push_str(" ORDER BY timestamp DESC LIMIT ?");
104        let mut query = sqlx::query(&sql);
105        if let Some(rule_id) = rule_id {
106            query = query.bind(rule_id);
107        }
108        query.bind(limit).fetch_all(self.reader()).await
109    }
110}
111
112pub fn row_to_rule_json(row: &sqlx::sqlite::SqliteRow) -> serde_json::Value {
113    serde_json::json!({
114        "id": row.get::<String, _>("id"),
115        "name": row.get::<String, _>("name"),
116        "enabled": row.get::<i64, _>("enabled") != 0,
117        "priority": row.get::<i64, _>("priority") as i32,
118        "conditions": serde_json::from_str::<serde_json::Value>(&row.get::<String, _>("conditions")).unwrap_or(serde_json::Value::Null),
119        "actions": serde_json::from_str::<serde_json::Value>(&row.get::<String, _>("actions")).unwrap_or(serde_json::Value::Null),
120        "created_at": row.get::<String, _>("created_at"),
121        "updated_at": row.get::<String, _>("updated_at"),
122    })
123}
124
125pub fn row_to_rule_log_json(row: &sqlx::sqlite::SqliteRow) -> serde_json::Value {
126    serde_json::json!({
127        "rule_id": row.get::<String, _>("rule_id"),
128        "rule_name": row.get::<String, _>("rule_name"),
129        "message_id": row.get::<String, _>("message_id"),
130        "actions_applied": serde_json::from_str::<serde_json::Value>(&row.get::<String, _>("actions_applied")).unwrap_or(serde_json::Value::Array(Vec::new())),
131        "timestamp": row.get::<String, _>("timestamp"),
132        "success": row.get::<i64, _>("success") != 0,
133        "error": row.get::<Option<String>, _>("error"),
134    })
135}