feat: multiple-CSV import
This commit is contained in:
@ -56,6 +56,28 @@ pub fn parse_csv(path: &Path) -> Result<Vec<Value>> {
|
||||
Ok(records)
|
||||
}
|
||||
|
||||
/// Parse multiple CSV files and merge into a single JSON array.
|
||||
/// Each record gets a "File" field set to the filename stem (e.g., "sales" from "sales.csv").
|
||||
pub fn merge_csvs(paths: &[impl AsRef<Path>]) -> Result<Vec<Value>> {
|
||||
let mut all_records = Vec::new();
|
||||
for path in paths {
|
||||
let path = path.as_ref();
|
||||
let stem = path
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
let records = parse_csv(path)?;
|
||||
for mut record in records {
|
||||
if let Value::Object(ref mut map) = record {
|
||||
map.insert("File".to_string(), Value::String(stem.clone()));
|
||||
}
|
||||
all_records.push(record);
|
||||
}
|
||||
}
|
||||
Ok(all_records)
|
||||
}
|
||||
|
||||
fn parse_csv_field(field: &str) -> Value {
|
||||
if field.is_empty() {
|
||||
return Value::Null;
|
||||
@ -158,6 +180,40 @@ mod tests {
|
||||
assert_eq!(records[0]["Active"], Value::String("true".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_csvs_adds_file_field_from_stem() {
|
||||
let dir = tempdir().unwrap();
|
||||
let sales = dir.path().join("sales.csv");
|
||||
let expenses = dir.path().join("expenses.csv");
|
||||
fs::write(&sales, "Region,Revenue\nEast,100\nWest,200").unwrap();
|
||||
fs::write(&expenses, "Region,Revenue\nEast,50\nWest,75").unwrap();
|
||||
|
||||
let records = merge_csvs(&[sales, expenses]).unwrap();
|
||||
assert_eq!(records.len(), 4);
|
||||
assert_eq!(records[0]["File"], Value::String("sales".to_string()));
|
||||
assert_eq!(records[1]["File"], Value::String("sales".to_string()));
|
||||
assert_eq!(records[2]["File"], Value::String("expenses".to_string()));
|
||||
assert_eq!(records[3]["File"], Value::String("expenses".to_string()));
|
||||
// Original fields preserved
|
||||
assert_eq!(records[0]["Region"], Value::String("East".to_string()));
|
||||
assert_eq!(
|
||||
records[2]["Revenue"],
|
||||
Value::Number(serde_json::Number::from(50))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_csvs_single_file_works() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path = dir.path().join("data.csv");
|
||||
fs::write(&path, "Name,Value\nA,1").unwrap();
|
||||
|
||||
let records = merge_csvs(&[path]).unwrap();
|
||||
assert_eq!(records.len(), 1);
|
||||
assert_eq!(records[0]["File"], Value::String("data".to_string()));
|
||||
assert_eq!(records[0]["Name"], Value::String("A".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_checking_csv_format() {
|
||||
// Simulates the format of /Users/edwlan/Downloads/Checking1.csv
|
||||
|
||||
Reference in New Issue
Block a user