use std::cmp::Ordering;
use std::collections::HashMap;
#[derive(Debug, Clone, sqlx::FromRow)]
pub(crate) struct RuleMetadataRow {
pub(crate) rule_id: String,
pub(crate) name: String,
pub(crate) source_repo: Option<String>,
}
pub(crate) async fn fetch_rule_metadata_for_ids(
db: &difflore_core::SqlitePool,
rule_ids: &[String],
normalized_repos: Option<&[String]>,
) -> HashMap<String, RuleMetadataRow> {
let ids: Vec<&str> = rule_ids
.iter()
.map(String::as_str)
.map(str::trim)
.filter(|id| !id.is_empty())
.collect();
if ids.is_empty() {
return HashMap::new();
}
let id_placeholders = std::iter::repeat_n("?", ids.len())
.collect::<Vec<_>>()
.join(", ");
let repo_filter = normalized_repos
.filter(|repos| !repos.is_empty())
.map(|repos| {
let placeholders = std::iter::repeat_n("?", repos.len())
.collect::<Vec<_>>()
.join(", ");
format!("AND LOWER(COALESCE(source_repo, '')) IN ({placeholders})")
})
.unwrap_or_default();
let sql = format!(
"SELECT id AS rule_id,
COALESCE(NULLIF(name, ''), id) AS name,
source_repo AS source_repo
FROM skills
WHERE id IN ({id_placeholders})
AND COALESCE(status, 'active') = 'active'
{repo_filter}"
);
let mut query = sqlx::query_as::<_, RuleMetadataRow>(&sql);
for id in ids {
query = query.bind(id);
}
if let Some(repos) = normalized_repos {
for repo in repos {
query = query.bind(repo);
}
}
query
.fetch_all(db)
.await
.unwrap_or_default()
.into_iter()
.map(|row| (row.rule_id.clone(), row))
.collect()
}
pub(crate) struct ProvenRuleRank<'a> {
pub(crate) total: i64,
pub(crate) linked_to_prior_recall: i64,
pub(crate) accepted_fix_proofs: i64,
pub(crate) name: &'a str,
}
impl ProvenRuleRank<'_> {
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
other
.total
.cmp(&self.total)
.then(
other
.linked_to_prior_recall
.cmp(&self.linked_to_prior_recall),
)
.then(other.accepted_fix_proofs.cmp(&self.accepted_fix_proofs))
.then(self.name.cmp(other.name))
}
}