use std::cmp::Reverse;
use std::io;
use anyhow::Result;
use num_traits::ToPrimitive;
use rustc_hash::FxHashMap;
use serde::Serialize;
use crate::file_descriptor::DeviceDescriptor;
use crate::report::report_writer::ReportWriter;
use crate::types::Duplicate;
pub struct CSVWriter {}
impl ReportWriter for CSVWriter {
fn write(&mut self, duplicates: &mut Vec<Duplicate>, write: &mut dyn io::Write) -> Result<()> {
let mut writer = csv::WriterBuilder::new()
.quote_style(csv::QuoteStyle::NonNumeric)
.from_writer(write);
duplicates.sort_by_key(|duplicate| {
Reverse(duplicate.file_size * (duplicate.locations.len() as u64 - 1))
});
let mut known_descriptors: FxHashMap<DeviceDescriptor, u32> = FxHashMap::default();
for (id, duplicate) in duplicates.iter().enumerate() {
let file_size = duplicate.file_size;
for (file_id, descriptor) in duplicate.locations.iter().enumerate() {
let device_descriptor = descriptor.file_descriptor.device_descriptor;
let device = match known_descriptors.get(&device_descriptor) {
Some(&id) => id,
None => {
let id = known_descriptors.len().to_u32().unwrap_or(0);
known_descriptors.insert(device_descriptor, id);
id
}
};
for path in &descriptor.paths {
let record = DuplicateRecord {
duplicate: id as u64,
identity: file_id as u64,
device,
file_name: path.to_string_lossy().into_owned(),
file_size,
};
writer.serialize(record)?
}
}
}
writer.flush()?;
Ok(())
}
}
#[derive(Serialize)]
struct DuplicateRecord {
duplicate: u64,
identity: u64,
device: u32,
file_name: String,
file_size: u64,
}