use std::sync::Arc;
use zeph_db::DbPool;
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct CompressionRule {
pub id: String,
pub tool_glob: Option<String>,
pub pattern: String,
pub replacement_template: String,
pub hit_count: i64,
pub source: String,
pub created_at: String,
}
#[derive(Clone)]
pub struct CompressionRuleStore {
pool: Arc<DbPool>,
}
impl CompressionRuleStore {
#[must_use]
pub fn new(pool: Arc<DbPool>) -> Self {
Self { pool }
}
pub async fn list_active(&self) -> Result<Vec<CompressionRule>, zeph_db::SqlxError> {
sqlx::query_as(zeph_db::sql!(
"SELECT id, tool_glob, pattern, replacement_template, hit_count, source, created_at \
FROM compression_rules ORDER BY hit_count ASC"
))
.fetch_all(self.pool.as_ref())
.await
}
pub async fn upsert(&self, rule: &CompressionRule) -> Result<(), zeph_db::SqlxError> {
sqlx::query(zeph_db::sql!(
"INSERT INTO compression_rules \
(id, tool_glob, pattern, replacement_template, hit_count, source, created_at) \
VALUES (?, ?, ?, ?, ?, ?, ?) \
ON CONFLICT(tool_glob, pattern) DO UPDATE SET \
replacement_template = excluded.replacement_template, \
source = excluded.source"
))
.bind(&rule.id)
.bind(&rule.tool_glob)
.bind(&rule.pattern)
.bind(&rule.replacement_template)
.bind(rule.hit_count)
.bind(&rule.source)
.bind(&rule.created_at)
.execute(self.pool.as_ref())
.await?;
Ok(())
}
pub async fn increment_hits(&self, batch: &[(String, u64)]) -> Result<(), zeph_db::SqlxError> {
for (id, delta) in batch {
sqlx::query(zeph_db::sql!(
"UPDATE compression_rules SET hit_count = hit_count + ? WHERE id = ?"
))
.bind((*delta).cast_signed())
.bind(id.as_str())
.execute(self.pool.as_ref())
.await?;
}
Ok(())
}
pub async fn delete(&self, id: &str) -> Result<(), zeph_db::SqlxError> {
sqlx::query(zeph_db::sql!("DELETE FROM compression_rules WHERE id = ?"))
.bind(id)
.execute(self.pool.as_ref())
.await?;
Ok(())
}
pub async fn prune_lowest_hits(&self, max_rules: u32) -> Result<u64, zeph_db::SqlxError> {
let count: i64 =
sqlx::query_scalar(zeph_db::sql!("SELECT COUNT(*) FROM compression_rules"))
.fetch_one(self.pool.as_ref())
.await?;
if count <= i64::from(max_rules) {
return Ok(0);
}
let to_delete = count - i64::from(max_rules);
let result = sqlx::query(zeph_db::sql!(
"DELETE FROM compression_rules WHERE id IN \
(SELECT id FROM compression_rules ORDER BY hit_count ASC LIMIT ?)"
))
.bind(to_delete)
.execute(self.pool.as_ref())
.await?;
Ok(result.rows_affected())
}
}