use crate::adapter::{Adapter, FileReport, IngestBatch, SourceFile};
use crate::price::BundledPrices;
use crate::sources::{coinbase::Coinbase, gemini::Gemini, river::River, swan::Swan};
use crate::AdapterError;
use btctax_core::{PriceProvider, Source};
use std::collections::HashMap;
use std::path::PathBuf;
fn adapters() -> Vec<Box<dyn Adapter>> {
vec![
Box::new(Swan),
Box::new(Coinbase),
Box::new(River),
Box::new(Gemini),
]
}
pub fn ingest_files_bundled(paths: &[PathBuf]) -> Result<IngestBatch, AdapterError> {
let prices = BundledPrices::load()?;
ingest_files(paths, &prices)
}
pub fn ingest_files(
paths: &[PathBuf],
prices: &dyn PriceProvider,
) -> Result<IngestBatch, AdapterError> {
let adapters = adapters();
let mut buckets: HashMap<Source, Vec<SourceFile>> = HashMap::new();
for p in paths {
let f = SourceFile::new(p.clone());
let mut matched = None;
for a in &adapters {
if a.detect(&f)? {
matched = Some(a.source());
break;
}
}
match matched {
Some(s) => buckets.entry(s).or_default().push(f),
None => {
return Err(AdapterError::UnknownSource {
path: p.display().to_string(),
})
}
}
}
let mut batch = IngestBatch::default();
for a in &adapters {
let Some(files) = buckets.remove(&a.source()) else {
continue;
};
for group in a.group(files) {
let rows = a.parse(&group)?;
let out = a.normalize(&group, rows, prices)?;
batch.reports.push(FileReport {
source: group.source,
label: group.label.clone(),
parsed_rows: out.parsed_rows,
btc_events: out.events.len(),
dropped_no_btc: out.dropped_no_btc,
unclassified: out.unclassified,
skipped_zero_sat: out.skipped_zero_sat,
});
batch.events.extend(out.events);
}
}
Ok(batch)
}