use duckdb::params;
use crate::facts::FactsDb;
use crate::{Options, Result};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EntityOwnershipRow {
pub entity: String,
pub author: String,
pub added: u64,
pub deleted: u64,
}
const SQL: &str = "
SELECT c.path AS entity,
m.canonical_author AS author,
COALESCE(SUM(c.loc_added), 0)::BIGINT AS added,
COALESCE(SUM(c.loc_deleted), 0)::BIGINT AS deleted
FROM changes c JOIN commits m USING (rev)
GROUP BY c.path, m.canonical_author
ORDER BY entity ASC, author ASC
LIMIT ?
";
#[tracing::instrument(name = "entity-ownership", skip_all, fields(min_revs = opts.min_revs))]
pub fn run_entity_ownership(db: &FactsDb, opts: &Options) -> Result<Vec<EntityOwnershipRow>> {
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-ownership",
opts,
)?;
crate::analyses::query::query_map_collect(
db,
&sql,
params![row_limit],
"entity-ownership",
|r| {
Ok(EntityOwnershipRow {
entity: r.get::<_, String>(0)?,
author: r.get::<_, String>(1)?,
added: u64::try_from(r.get::<_, i64>(2)?).unwrap_or(u64::MAX),
deleted: u64::try_from(r.get::<_, i64>(3)?).unwrap_or(u64::MAX),
})
},
)
}