Skip to content

Commit

Permalink
add support for importing deeply nested child fields addressed by pat…
Browse files Browse the repository at this point in the history
…h names
  • Loading branch information
jesseditson committed Nov 1, 2024
1 parent 2c833e8 commit e7b2aa0
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 16 deletions.
36 changes: 36 additions & 0 deletions src/binary/command/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ impl Command {
) -> Result<(), ImportError> {
// Generate a list of rows from our input data
progress("parsing file...", 0, 0);
let inverted_field_map: HashMap<&String, &String> = field_map.iter()
.map(|(k, v)| (v, k)).collect();
let parsed = file_format.parse(reader)?;
if let ImportName::Field(f) = &import_name {
progress("creating object...", 0, 0);
Expand Down Expand Up @@ -388,6 +390,7 @@ impl Command {
file_name
}
};
let mut unused_cols = row.clone();
for (name, field_type) in &mapped_type.fields {
let from_name = if let Some(mapped) = field_map.get(name) {
mapped
Expand All @@ -407,10 +410,43 @@ impl Command {
field: name.to_string(),
}))
.map_err(|e| ImportError::WriteError(e.to_string()))?;
unused_cols.remove(from_name);
} else {
println!("field '{}' not found in row: {:?}", from_name, row);
}
}
// We support pathing in csv column names, so if the name begins
// with a child name, insert it.
for (name, value) in unused_cols {
let import_name = if let Some(import_name) = inverted_field_map.get(&name) {
import_name
} else {
&name
};
let mut col_path = ValuePath::from_string(import_name);
// The final component is the field
let col_field = col_path.pop();
if col_field.is_none() {
continue;
}
if let Ok(found_type) = col_path.get_definition(mapped_type) {
let col_field = col_field.unwrap().to_string().trim().to_string();
if let Some(field_type) = found_type.fields.get(&col_field) {
// Validate type
let value = FieldValue::from_string(&name, field_type, value.to_string())
.map_err(|e| ImportError::ParseError(e.to_string()))?;
archival
.send_event_no_rebuild(ArchivalEvent::EditField(EditFieldEvent {
object: object.to_string(),
filename: filename.to_string(),
path: current_path.clone().concat(col_path),
value: Some(value),
field: col_field,
}))
.map_err(|e| ImportError::WriteError(e.to_string()))?;
}
}
}
}
Ok(())
}
Expand Down
45 changes: 29 additions & 16 deletions src/value_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use liquid::ValueView;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::{collections::HashMap, fmt::Display};
use thiserror::Error;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -132,6 +132,9 @@ impl ValuePath {
pub fn is_empty(&self) -> bool {
self.path.is_empty()
}
pub fn len(&self) -> usize {
self.path.len()
}

pub fn append(mut self, component: ValuePathComponent) -> Self {
self.path.push(component);
Expand Down Expand Up @@ -388,9 +391,15 @@ impl ValuePath {
) -> Result<&'a ObjectDefinition, ValuePathError> {
let mut last_val = def;
for cmp in self.path.iter() {
if let ValuePathComponent::Key(k) = cmp {
if let Some(child_def) = last_val.children.get(k) {
last_val = child_def;
match cmp {
ValuePathComponent::Key(k) => {
if let Some(child_def) = last_val.children.get(k) {
last_val = child_def;
continue;
}
},
ValuePathComponent::Index(_) => {
// Skip indexes in definitions
continue;
}
}
Expand Down Expand Up @@ -448,7 +457,7 @@ impl ValuePath {
if let ValuePathComponent::Index(index) = cmp {
if let Some(child) = children.get_mut(index) {
if let Some(ValuePathComponent::Key(k)) = i_path.next() {
if i_path.len() > 0 {
if child.contains_key(&k) {
last_val = child.get_mut(&k);
continue;
}
Expand Down Expand Up @@ -499,17 +508,21 @@ impl ValuePath {
// the index and then finds a key on it.
if let Some(FieldValue::Objects(children)) = last_val {
if let ValuePathComponent::Index(index) = cmp {
if let Some(child) = children.get_mut(index) {
if let Some(ValuePathComponent::Key(k)) = i_path.next() {
if i_path.len() > 0 {
last_val = child.get_mut(&k);
continue;
} else {
match value {
Some(value) => child.insert(k, value),
None => child.remove(&k),
};
}
while children.len() <= index {
// No child yet - since we're setting, insert one
// here.
children.push(HashMap::new());
}
let child = children.get_mut(index).unwrap();
if let Some(ValuePathComponent::Key(k)) = i_path.next() {
if i_path.len() > 0 {
last_val = child.get_mut(&k);
continue;
} else {
match value {
Some(value) => child.insert(k, value),
None => child.remove(&k),
};
}
}
}
Expand Down

0 comments on commit e7b2aa0

Please sign in to comment.