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}