in src/data.rs [1135:1205]
fn display_items_table(
items: Vec<HashMap<String, AttributeValue>>,
ts: &app::TableSchema,
selected_attributes: &Option<String>,
keys_only: bool,
) {
// Print no item message and return if items length is 0.
if items.is_empty() {
println!("No item to show in the table '{}'", ts.name.to_string());
return;
};
// build header - first, primary key(s). Even index, key(s) are always projected.
// ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Projections
let mut header: Vec<&str> = vec![ts.pk.name.as_str()];
if let Some(sk) = &ts.sk {
header.push(sk.name.as_str())
};
// build header - next, attribute names or aggregated "attributes" header, unless --keys-only flag is set.
if !keys_only {
if let Some(attrs) = selected_attributes {
header.extend(attrs.split(',').collect::<Vec<&str>>());
} else {
header.push("attributes")
};
};
debug!("built header elements: {:?}", header);
let mut tw = TabWriter::new(io::stdout());
tw.write_all((header.join("\t") + "\n").as_bytes()).unwrap();
// `cells` is sth like: ["item1-pk\titem1-attr1\titem1-attr2", "item2-pk\titem2-attr1\titem2-attr2"]
let mut cells: Vec<String> = vec![]; // may be able to use with_capacity to initialize the vec.
for mut item in items {
let mut item_attributes = vec![];
// First, take primary key(s) of each item.
let x: Option<AttributeValue> = item.remove(&ts.pk.name);
if let Some(sk) = &ts.sk {
let y: Option<AttributeValue> = item.remove(&sk.name);
item_attributes.extend(vec![attrval_to_cell_print(x), attrval_to_cell_print(y)]);
} else {
item_attributes.extend(vec![attrval_to_cell_print(x)]);
};
if !item.is_empty() {
if let Some(_attributes) = selected_attributes {
let attrs: Vec<&str> = _attributes.split(',').map(|x| x.trim()).collect();
for attr in attrs {
let attrval: Option<AttributeValue> = item.get(attr).cloned();
item_attributes.push(attrval_to_cell_print(attrval));
}
} else if !keys_only {
// print rest aggreated "attributes" column in JSON format.
let full = serde_json::to_string(&convert_to_json(&item)).unwrap();
let threshold: usize = 50;
if full.chars().count() > threshold {
// NOTE: counting bytes slice doesn't work for multi-bytes strings
let st: &String = &full.chars().take(threshold).collect();
item_attributes.push(String::from(st) + "...");
} else {
item_attributes.push(full);
}
}
}
cells.push(item_attributes.join("\t"));
}
tw.write_all((cells.join("\n") + "\n").as_bytes()).unwrap();
tw.flush().unwrap();
}