use polars::prelude::*;
use super::{FileChangeRecord, change_kind_str};
use crate::RpoError;
pub fn build(records: Vec<FileChangeRecord>) -> Result<DataFrame, RpoError> {
let n = records.len();
let mut sha = Vec::with_capacity(n);
let mut commit_time = Vec::with_capacity(n);
let mut can_an = Vec::with_capacity(n);
let mut can_ae = Vec::with_capacity(n);
let mut can_cn = Vec::with_capacity(n);
let mut can_ce = Vec::with_capacity(n);
let mut path = Vec::with_capacity(n);
let mut old_path: Vec<Option<String>> = Vec::with_capacity(n);
let mut kind = Vec::with_capacity(n);
let mut insertions = Vec::with_capacity(n);
let mut deletions = Vec::with_capacity(n);
let mut extension: Vec<Option<String>> = Vec::with_capacity(n);
let mut is_generated = Vec::with_capacity(n);
let mut is_vendored = Vec::with_capacity(n);
for r in records {
sha.push(r.sha);
commit_time.push(r.commit_time_ms);
can_an.push(r.canonical_author_name);
can_ae.push(r.canonical_author_email);
can_cn.push(r.canonical_committer_name);
can_ce.push(r.canonical_committer_email);
path.push(r.path);
old_path.push(r.old_path);
kind.push(change_kind_str(r.change_kind).to_string());
insertions.push(r.insertions);
deletions.push(r.deletions);
extension.push(r.extension);
is_generated.push(r.is_generated);
is_vendored.push(r.is_vendored);
}
let ct = Column::new("commit_time".into(), commit_time).cast(&DataType::Datetime(
TimeUnit::Milliseconds,
Some(TimeZone::UTC),
))?;
let kind_col = Column::new("change_kind".into(), kind)
.cast(&DataType::from_categories(Categories::global()))?;
let df = DataFrame::new_infer_height(vec![
Column::new("sha".into(), sha),
ct,
Column::new("canonical_author_name".into(), can_an),
Column::new("canonical_author_email".into(), can_ae),
Column::new("canonical_committer_name".into(), can_cn),
Column::new("canonical_committer_email".into(), can_ce),
Column::new("path".into(), path),
Column::new("old_path".into(), old_path),
kind_col,
Column::new("insertions".into(), insertions),
Column::new("deletions".into(), deletions),
Column::new("extension".into(), extension),
Column::new("is_generated".into(), is_generated),
Column::new("is_vendored".into(), is_vendored),
])?;
Ok(df)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::ChangeKind;
#[test]
fn empty_input_produces_zero_row_frame() {
let df = build(vec![]).unwrap();
assert_eq!(df.height(), 0);
assert_eq!(df.width(), 14);
}
#[test]
fn records_produce_rows() {
let rec = FileChangeRecord {
sha: "deadbeef".into(),
commit_time_ms: 0,
canonical_author_name: "A".into(),
canonical_author_email: "a@a".into(),
canonical_committer_name: "A".into(),
canonical_committer_email: "a@a".into(),
path: "src/lib.rs".into(),
old_path: None,
change_kind: ChangeKind::Added,
insertions: 10,
deletions: 0,
extension: Some("rs".into()),
is_generated: false,
is_vendored: false,
};
let df = build(vec![rec]).unwrap();
assert_eq!(df.height(), 1);
assert_eq!(df.column("change_kind").unwrap().len(), 1);
}
}