cfgd_core/state/
compliance.rs1use rusqlite::params;
2
3use super::StateStore;
4use super::types::ComplianceHistoryRow;
5use crate::errors::{Result, StateError};
6
7impl StateStore {
8 pub fn store_compliance_snapshot(
11 &self,
12 snapshot: &crate::compliance::ComplianceSnapshot,
13 hash: &str,
14 ) -> Result<()> {
15 let json = serde_json::to_string(snapshot)
16 .map_err(|e| StateError::Database(format!("failed to serialize snapshot: {}", e)))?;
17 self.conn.execute(
18 "INSERT INTO compliance_snapshots (timestamp, content_hash, snapshot_json, summary_compliant, summary_warning, summary_violation)
19 VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
20 params![
21 snapshot.timestamp,
22 hash,
23 json,
24 snapshot.summary.compliant as i64,
25 snapshot.summary.warning as i64,
26 snapshot.summary.violation as i64,
27 ],
28 )?;
29 Ok(())
30 }
31
32 pub fn latest_compliance_hash(&self) -> Result<Option<String>> {
34 let result = self.conn.query_row(
35 "SELECT content_hash FROM compliance_snapshots ORDER BY id DESC LIMIT 1",
36 [],
37 |row| row.get(0),
38 );
39
40 match result {
41 Ok(hash) => Ok(Some(hash)),
42 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
43 Err(e) => Err(StateError::Database(e.to_string()).into()),
44 }
45 }
46
47 pub fn compliance_history(
50 &self,
51 since: Option<&str>,
52 limit: u32,
53 ) -> Result<Vec<ComplianceHistoryRow>> {
54 if let Some(since_ts) = since {
55 let mut stmt = self.conn.prepare(
56 "SELECT id, timestamp, summary_compliant, summary_warning, summary_violation
57 FROM compliance_snapshots WHERE timestamp > ?1 ORDER BY id DESC LIMIT ?2",
58 )?;
59
60 let rows = stmt
61 .query_map(params![since_ts, limit], |row| {
62 Ok(ComplianceHistoryRow {
63 id: row.get(0)?,
64 timestamp: row.get(1)?,
65 compliant: row.get(2)?,
66 warning: row.get(3)?,
67 violation: row.get(4)?,
68 })
69 })?
70 .collect::<std::result::Result<Vec<_>, _>>()?;
71 Ok(rows)
72 } else {
73 let mut stmt = self.conn.prepare(
74 "SELECT id, timestamp, summary_compliant, summary_warning, summary_violation
75 FROM compliance_snapshots ORDER BY id DESC LIMIT ?1",
76 )?;
77
78 let rows = stmt
79 .query_map(params![limit], |row| {
80 Ok(ComplianceHistoryRow {
81 id: row.get(0)?,
82 timestamp: row.get(1)?,
83 compliant: row.get(2)?,
84 warning: row.get(3)?,
85 violation: row.get(4)?,
86 })
87 })?
88 .collect::<std::result::Result<Vec<_>, _>>()?;
89 Ok(rows)
90 }
91 }
92
93 pub fn get_compliance_snapshot(
95 &self,
96 id: i64,
97 ) -> Result<Option<crate::compliance::ComplianceSnapshot>> {
98 let result = self.conn.query_row(
99 "SELECT snapshot_json FROM compliance_snapshots WHERE id = ?1",
100 params![id],
101 |row| row.get::<_, String>(0),
102 );
103
104 match result {
105 Ok(json) => {
106 let snapshot: crate::compliance::ComplianceSnapshot = serde_json::from_str(&json)
107 .map_err(|e| {
108 StateError::Database(format!("failed to deserialize snapshot: {}", e))
109 })?;
110 Ok(Some(snapshot))
111 }
112 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
113 Err(e) => Err(StateError::Database(e.to_string()).into()),
114 }
115 }
116
117 pub fn prune_compliance_snapshots(&self, before_timestamp: &str) -> Result<usize> {
120 let deleted = self.conn.execute(
121 "DELETE FROM compliance_snapshots WHERE timestamp < ?1",
122 params![before_timestamp],
123 )?;
124 Ok(deleted)
125 }
126}