use crate::error::Error;
use csv::StringRecord;
use regex::Regex;
use serde_json::{Map, Value};
pub fn csv_to_json(headers: Vec<&str>, records: Vec<StringRecord>) -> Result<Vec<Value>, Error> {
let arr: Vec<Value> = records
.iter()
.map(|record| {
let mut fields = Map::new();
for (i, id) in headers.iter().enumerate() {
let record_field_value: &str = match record.get(i) {
Some(value) => value,
None => "",
};
let (json_key, json_val) = csv_value_to_json(id, record_field_value);
match fields.contains_key(&json_key) {
true => {
fields = merge_json(fields, json_key, json_val);
}
false => {
fields.insert(json_key, json_val);
}
};
}
Value::Object(fields)
})
.collect();
Ok(arr)
}
fn csv_value_to_json(header: &str, value: &str) -> (String, Value) {
lazy_static! {
static ref FIND_NESTING_DELIMITERS: Regex = Regex::new(r"\[\]|\.").unwrap();
}
let val: Value = match FIND_NESTING_DELIMITERS.find(header) {
Some(matching_delimiter) => {
let rest_header: &str = &header[matching_delimiter.end()..];
match matching_delimiter.as_str() {
"[]" => {
let nested_val = match rest_header {
"" => Value::Array(vec![Value::String(value.to_string())]),
_ => {
let (_, recursive_val) = csv_value_to_json(rest_header, value);
Value::Array(vec![recursive_val])
}
};
nested_val
}
"." => {
let nested_val = match rest_header {
"" => panic!("There should always be text following the period (.)"),
_ => {
let (recursive_key, recursive_val) = csv_value_to_json(rest_header, value);
let mut fields = Map::new();
fields.insert(recursive_key, recursive_val);
Value::Object(fields)
}
};
nested_val
}
_ => panic!("csv_value_to_json() matched a character that should not have been matched."),
}
}
None => Value::String(value.to_string()),
};
let root_header_name: Vec<&str> = FIND_NESTING_DELIMITERS.split(header).collect();
(root_header_name[0].to_string(), val)
}
fn merge_json(
mut fields: Map<String, Value>,
json_key: String,
json_val: Value,
) -> Map<String, Value> {
let existing_val = fields.get_mut(&json_key).unwrap();
match existing_val {
Value::Object(existing_map) => {
if let Value::Object(json_val_map) = json_val {
for (key, val) in json_val_map.into_iter() {
existing_map.insert(key, val);
}
}
}
Value::Array(existing_vec) => {
if let Value::Array(json_val_vec) = json_val {
for val in json_val_vec.into_iter() {
existing_vec.push(val);
}
}
}
_ => {
fields.insert(json_key, json_val);
}
};
fields
}