use duckdb::params;
use crate::facts::FactsDb;
use crate::{Options, Result};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EntityEffortRow {
pub entity: String,
pub author: String,
pub author_revs: u32,
pub total_revs: u32,
}
const SQL: &str = "
WITH ea AS (
SELECT c.path AS entity, m.canonical_author AS author,
COUNT(*)::BIGINT AS author_revs
FROM changes c JOIN commits m USING (rev)
GROUP BY c.path, m.canonical_author
)
SELECT entity, author, author_revs,
SUM(author_revs) OVER (PARTITION BY entity) AS total_revs
FROM ea
ORDER BY entity ASC, author_revs DESC, author ASC
LIMIT ?
";
#[tracing::instrument(name = "entity-effort", skip_all, fields(min_revs = opts.min_revs))]
pub fn run_entity_effort(db: &FactsDb, opts: &Options) -> Result<Vec<EntityEffortRow>> {
let row_limit: i64 = opts.rows_limit.map_or(i64::MAX, i64::from);
crate::analyses::lineage::materialize_if_needed(db, opts)?;
let sql = crate::analyses::lineage::rewrite(SQL, opts);
crate::analyses::query::explain_if_requested(
db,
&sql,
params![row_limit],
"entity-effort",
opts,
)?;
crate::analyses::query::query_map_collect(db, &sql, params![row_limit], "entity-effort", |r| {
Ok(EntityEffortRow {
entity: r.get::<_, String>(0)?,
author: r.get::<_, String>(1)?,
author_revs: u32::try_from(r.get::<_, i64>(2)?).unwrap_or(u32::MAX),
total_revs: u32::try_from(r.get::<_, i64>(3)?).unwrap_or(u32::MAX),
})
})
}