use anyhow::{Context, Result};
use indexmap::IndexMap;
use std::collections::HashMap;
use structopt::clap::arg_enum;
use term_table::{row::Row, table_cell::TableCell, Table};
type ListerResult = Result<fn(&str, Vec<&str>) -> Result<String>>;
type ShowOneResult = Result<fn(IndexMap<String, String>, Vec<String>) -> Result<String>>;
arg_enum! {
#[derive(Debug, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Format {
table,
value,
yaml,
json,
}
}
fn value_lister(_: &str, values: Vec<&str>) -> Result<String> {
Ok(values.iter().fold(String::new(), |acc, s| acc + s + "\n"))
}
fn yaml_lister(header: &str, values: Vec<&str>) -> Result<String> {
let mut output: Vec<HashMap<&str, &str>> = vec![];
for value in values.iter() {
let mut temp: HashMap<&str, &str> = HashMap::new();
let _ = temp.insert(header, value);
output.push(temp);
}
serde_yaml::to_string(&output).context("Failed to output list as yaml.")
}
fn json_lister(header: &str, values: Vec<&str>) -> Result<String> {
let mut output: Vec<HashMap<&str, &str>> = vec![];
for value in values.iter() {
let mut temp: HashMap<&str, &str> = HashMap::new();
let _ = temp.insert(header, value);
output.push(temp);
}
serde_json::to_string_pretty(&output).context("Failed to output list as json.")
}
fn table_lister(header: &str, values: Vec<&str>) -> Result<String> {
let mut table = Table::new();
table.style = term_table::TableStyle::simple();
let header = Row::new(vec![TableCell::new(header)]);
table.add_row(header);
values.iter().for_each(|value| {
let mut row = Row::new(vec![TableCell::new(value)]);
row.has_separator = false;
table.add_row(row);
});
if table.rows.len() > 1 {
table.rows[1].has_separator = true;
}
Ok(table.render())
}
pub fn get_lister(format: Format) -> ListerResult {
match format {
Format::value => Ok(value_lister),
Format::table => Ok(table_lister),
Format::yaml => Ok(yaml_lister),
Format::json => Ok(json_lister),
}
}
fn value_showone(data: IndexMap<String, String>, retract_fields: Vec<String>) -> Result<String> {
Ok(data
.iter()
.map(|(key, value)| {
if retract_fields.contains(key) {
"<redacted>"
} else {
value
}
})
.fold(String::new(), |acc, value| acc + value + "\n"))
}
fn table_showone(data: IndexMap<String, String>, retract_fields: Vec<String>) -> Result<String> {
let mut table = Table::new();
table.style = term_table::TableStyle::simple();
table.separate_rows = false;
data.iter().for_each(|(key, value)| {
let value = {
if retract_fields.contains(key) {
"<redacted>"
} else {
value.as_str()
}
};
table.add_row(Row::new(vec![TableCell::new(key), TableCell::new(value)]));
});
Ok(table.render())
}
fn yaml_showone(data: IndexMap<String, String>, retract_fields: Vec<String>) -> Result<String> {
let redacted = String::from("<redacted>");
let mut temp: HashMap<_, _> = data.iter().collect();
data.iter().for_each(|(key, value)| {
let _ = temp.insert(key, {
if retract_fields.contains(key) {
&redacted
} else {
value
}
});
});
serde_yaml::to_string(&temp).context("Failed to output one result as yaml.")
}
fn json_showone(data: IndexMap<String, String>, retract_fields: Vec<String>) -> Result<String> {
let redacted = String::from("<redacted>");
let mut temp: HashMap<_, _> = data.iter().collect();
data.iter().for_each(|(key, value)| {
let _ = temp.insert(key, {
if retract_fields.contains(key) {
&redacted
} else {
value
}
});
});
serde_json::to_string_pretty(&temp).context("Failed to output one result as json.")
}
pub fn get_showone(format: Format) -> ShowOneResult {
match format {
Format::value => Ok(value_showone),
Format::table => Ok(table_showone),
Format::yaml => Ok(yaml_showone),
Format::json => Ok(json_showone),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn value_lister() {
let values = vec!["foo", "bar"];
assert_eq!(
"foo\nbar\n",
get_lister(Format::value).unwrap()("biz", values).unwrap()
);
}
#[test]
fn value_lister_empty() {
let values = Vec::new();
assert_eq!(
"",
get_lister(Format::value).unwrap()("biz", values).unwrap()
);
}
#[test]
fn table_lister() {
let values = vec!["foo", "bar"];
assert_eq!(
"+-----+\n| biz |\n+-----+\n| foo |\n| bar |\n+-----+\n",
get_lister(Format::table).unwrap()("biz", values).unwrap()
);
}
#[test]
fn table_lister_empty() {
let values = vec![];
assert_eq!(
"+-----+\n| biz |\n+-----+\n",
get_lister(Format::table).unwrap()("biz", values).unwrap()
);
}
#[test]
fn yaml_lister() {
let values = vec!["foo", "bar"];
assert_eq!(
"---\n- biz: foo\n- biz: bar",
get_lister(Format::yaml).unwrap()("biz", values).unwrap()
);
}
#[test]
fn yaml_lister_empty() {
let values = Vec::new();
assert_eq!(
"---\n[]",
get_lister(Format::yaml).unwrap()("biz", values).unwrap()
);
}
#[test]
fn json_lister() {
let values = vec!["foo", "bar"];
assert_eq!(
"[\n {\n \"biz\": \"foo\"\n },\n {\n \"biz\": \"bar\"\n }\n]",
get_lister(Format::json).unwrap()("biz", values).unwrap()
);
}
#[test]
fn json_lister_empty() {
let values = Vec::new();
assert_eq!(
"[]",
get_lister(Format::json).unwrap()("biz", values).unwrap()
);
}
#[test]
fn value_showone() {
let mut data = IndexMap::new();
let _ = data.insert("foo_key".into(), "foo_value".into());
let _ = data.insert("bar_key".into(), "".into());
assert_eq!(
"foo_value\n\n",
get_showone(Format::value).unwrap()(data, vec![]).unwrap()
);
}
#[test]
fn value_showone_empty() {
let data = IndexMap::new();
assert_eq!(
"",
get_showone(Format::value).unwrap()(data, vec![]).unwrap()
);
}
#[test]
fn value_showone_redacted() {
let mut data = IndexMap::new();
let _ = data.insert("foo_key".into(), "foo_value".into());
let _ = data.insert("bar_key".into(), "".into());
assert_eq!(
"<redacted>\n\n",
get_showone(Format::value).unwrap()(data, vec!["foo_key".into()]).unwrap()
);
}
#[test]
fn table_showone() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"+-----+-----+\n| foo | bar |\n+-----+-----+\n",
get_showone(Format::table).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn table_showone_empty() {
let map = indexmap::IndexMap::new();
assert_eq!(
"",
get_showone(Format::table).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn table_showone_redacted() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"+-----+------------+\n| foo | <redacted> |\n+-----+------------+\n",
get_showone(Format::table).unwrap()(map, vec!["foo".into()]).unwrap()
);
}
#[test]
fn yaml_showone() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"---\nfoo: bar",
get_showone(Format::yaml).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn yaml_showone_empty() {
let map = indexmap::IndexMap::new();
assert_eq!(
"---\n{}",
get_showone(Format::yaml).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn yaml_showone_redacted() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"---\nfoo: \"<redacted>\"",
get_showone(Format::yaml).unwrap()(map, vec!["foo".into()]).unwrap()
);
}
#[test]
fn json_showone() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"{\n \"foo\": \"bar\"\n}",
get_showone(Format::json).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn json_showone_empty() {
let map = indexmap::IndexMap::new();
assert_eq!(
"{}",
get_showone(Format::json).unwrap()(map, vec![]).unwrap()
);
}
#[test]
fn json_showone_redacted() {
let mut map = indexmap::IndexMap::new();
let _ = map.insert("foo".into(), "bar".into());
assert_eq!(
"{\n \"foo\": \"<redacted>\"\n}",
get_showone(Format::json).unwrap()(map, vec!["foo".into()]).unwrap()
);
}
}