mirror of
https://github.com/nushell/nushell.git
synced 2025-06-20 09:58:15 +02:00
Closes #14552 # Description Implemented a new flag to the ```to md``` command to center specific columns in Markdown table output using a list of CellPaths. This enhances formatting control for users exporting tables to markdown. ## Example For the table: ```shell let t = version | select version build_time | transpose k v ``` ``` ╭───┬────────────┬────────────────────────────╮ │ # │ k │ v │ ├───┼────────────┼────────────────────────────┤ │ 0 │ version │ 0.104.1 │ │ 1 │ build_time │ 2025-05-21 11:15:45 +01:00 │ ╰───┴────────────┴────────────────────────────╯ ``` Running ```$t | to md``` or ```$t | to md --pretty``` gives us, respectively: ``` |k|v| |-|-| |version|0.104.1| |build_time|2025-05-21 11:15:45 +01:00| ``` |k|v| |-|-| |version|0.104.1| |build_time|2025-05-21 11:15:45 +01:00| and ``` | k | v | | ---------- | -------------------------- | | version | 0.104.1 | | build_time | 2025-05-21 11:15:45 +01:00 | ``` | k | v | | ---------- | -------------------------- | | version | 0.104.1 | | build_time | 2025-05-21 11:15:45 +01:00 | With the new ```center``` flag, when adding ```--center [v]``` to the previous commands, we obtain, respectively: ``` |k|v| |-|:-:| |version|0.104.1| |build_time|2025-05-21 11:15:45 +01:00| ``` |k|v| |-|:-:| |version|0.104.1| |build_time|2025-05-21 11:15:45 +01:00| and ``` | k | v | | ---------- |:--------------------------:| | version | 0.104.1 | | build_time | 2025-05-21 11:15:45 +01:00 | ``` | k | v | | ---------- |:--------------------------:| | version | 0.104.1 | | build_time | 2025-05-21 11:15:45 +01:00 | The new ```center``` option, as demonstrated in the example, not only formats the Markdown table to center columns but also, when paired with ```pretty```, it also centers the string values within those columns. The logic works by extracting the column from the CellPath and applying centering. So, ```--center [1.v]``` is also valid and centers the ```v``` column. You can also specify multiple columns, for instance, ```--center [v k]``` will center both columns in the example above. # User-Facing Changes The ```to md``` command will support column centering with the new ```center``` flag. # Tests + Formatting Added test cases to ensure correct behaviour. fmt + clippy OK. # After Submitting The command documentation needs to be updated with the new ```center``` flag and an example. Co-authored-by: Marco Cunha <marcomarquesdacunha@tecnico.ulisboa.pt> Co-authored-by: Marco Cunha <marcomarquesdacunha@tecnico.ulisboa.pt>
This commit is contained in:
parent
61d59f13fa
commit
96a886eb84
@ -1,7 +1,8 @@
|
|||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_cmd_base::formats::to::delimited::merge_descriptors;
|
use nu_cmd_base::formats::to::delimited::merge_descriptors;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Config;
|
use nu_protocol::{Config, ast::PathMember};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToMd;
|
pub struct ToMd;
|
||||||
@ -24,6 +25,12 @@ impl Command for ToMd {
|
|||||||
"treat each row as markdown syntax element",
|
"treat each row as markdown syntax element",
|
||||||
Some('e'),
|
Some('e'),
|
||||||
)
|
)
|
||||||
|
.named(
|
||||||
|
"center",
|
||||||
|
SyntaxShape::List(Box::new(SyntaxShape::CellPath)),
|
||||||
|
"Formats the Markdown table to center given columns",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
.category(Category::Formats)
|
.category(Category::Formats)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +71,13 @@ impl Command for ToMd {
|
|||||||
"|foo|bar|\n|-|-|\n|1|2|\n|3|4|\n|foo|\n|-|\n|5|",
|
"|foo|bar|\n|-|-|\n|1|2|\n|3|4|\n|foo|\n|-|\n|5|",
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Center a column of a markdown table",
|
||||||
|
example: "[ {foo: 1, bar: 2} {foo: 3, bar: 4}] | to md --pretty --center [bar]",
|
||||||
|
result: Some(Value::test_string(
|
||||||
|
"| foo | bar |\n| --- |:---:|\n| 1 | 2 |\n| 3 | 4 |",
|
||||||
|
)),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +91,9 @@ impl Command for ToMd {
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
let pretty = call.has_flag(engine_state, stack, "pretty")?;
|
let pretty = call.has_flag(engine_state, stack, "pretty")?;
|
||||||
let per_element = call.has_flag(engine_state, stack, "per-element")?;
|
let per_element = call.has_flag(engine_state, stack, "per-element")?;
|
||||||
|
let center: Option<Vec<CellPath>> = call.get_flag(engine_state, stack, "center")?;
|
||||||
let config = stack.get_config(engine_state);
|
let config = stack.get_config(engine_state);
|
||||||
to_md(input, pretty, per_element, &config, head)
|
to_md(input, pretty, per_element, ¢er, &config, head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +101,7 @@ fn to_md(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
per_element: bool,
|
per_element: bool,
|
||||||
|
center: &Option<Vec<CellPath>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
@ -102,9 +118,12 @@ fn to_md(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |val| match val {
|
.map(move |val| match val {
|
||||||
Value::List { .. } => {
|
Value::List { .. } => {
|
||||||
format!("{}\n", table(val.into_pipeline_data(), pretty, config))
|
format!(
|
||||||
|
"{}\n",
|
||||||
|
table(val.into_pipeline_data(), pretty, center, config)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
other => fragment(other, pretty, config),
|
other => fragment(other, pretty, center, config),
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("")
|
.join("")
|
||||||
@ -113,11 +132,13 @@ fn to_md(
|
|||||||
)
|
)
|
||||||
.into_pipeline_data_with_metadata(Some(metadata)));
|
.into_pipeline_data_with_metadata(Some(metadata)));
|
||||||
}
|
}
|
||||||
Ok(Value::string(table(grouped_input, pretty, config), head)
|
Ok(
|
||||||
.into_pipeline_data_with_metadata(Some(metadata)))
|
Value::string(table(grouped_input, pretty, center, config), head)
|
||||||
|
.into_pipeline_data_with_metadata(Some(metadata)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
fn fragment(input: Value, pretty: bool, center: &Option<Vec<CellPath>>, config: &Config) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|
||||||
if let Value::Record { val, .. } = &input {
|
if let Value::Record { val, .. } = &input {
|
||||||
@ -128,13 +149,13 @@ fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
|||||||
"h2" => "## ".to_string(),
|
"h2" => "## ".to_string(),
|
||||||
"h3" => "### ".to_string(),
|
"h3" => "### ".to_string(),
|
||||||
"blockquote" => "> ".to_string(),
|
"blockquote" => "> ".to_string(),
|
||||||
_ => return table(input.into_pipeline_data(), pretty, config),
|
_ => return table(input.into_pipeline_data(), pretty, center, config),
|
||||||
};
|
};
|
||||||
|
|
||||||
out.push_str(&markup);
|
out.push_str(&markup);
|
||||||
out.push_str(&data.to_expanded_string("|", config));
|
out.push_str(&data.to_expanded_string("|", config));
|
||||||
}
|
}
|
||||||
_ => out = table(input.into_pipeline_data(), pretty, config),
|
_ => out = table(input.into_pipeline_data(), pretty, center, config),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out = input.to_expanded_string("|", config)
|
out = input.to_expanded_string("|", config)
|
||||||
@ -161,7 +182,12 @@ fn collect_headers(headers: &[String]) -> (Vec<String>, Vec<usize>) {
|
|||||||
(escaped_headers, column_widths)
|
(escaped_headers, column_widths)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table(input: PipelineData, pretty: bool, config: &Config) -> String {
|
fn table(
|
||||||
|
input: PipelineData,
|
||||||
|
pretty: bool,
|
||||||
|
center: &Option<Vec<CellPath>>,
|
||||||
|
config: &Config,
|
||||||
|
) -> String {
|
||||||
let vec_of_values = input
|
let vec_of_values = input
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|val| match val {
|
.flat_map(|val| match val {
|
||||||
@ -225,7 +251,13 @@ fn table(input: PipelineData, pretty: bool, config: &Config) -> String {
|
|||||||
{
|
{
|
||||||
String::from("")
|
String::from("")
|
||||||
} else {
|
} else {
|
||||||
get_output_string(&escaped_headers, &escaped_rows, &column_widths, pretty)
|
get_output_string(
|
||||||
|
&escaped_headers,
|
||||||
|
&escaped_rows,
|
||||||
|
&column_widths,
|
||||||
|
pretty,
|
||||||
|
center,
|
||||||
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.to_string()
|
.to_string()
|
||||||
};
|
};
|
||||||
@ -271,20 +303,42 @@ fn get_output_string(
|
|||||||
rows: &[Vec<String>],
|
rows: &[Vec<String>],
|
||||||
column_widths: &[usize],
|
column_widths: &[usize],
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
|
center: &Option<Vec<CellPath>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut output_string = String::new();
|
let mut output_string = String::new();
|
||||||
|
|
||||||
|
let mut to_center: HashSet<String> = HashSet::new();
|
||||||
|
if let Some(center_vec) = center.as_ref() {
|
||||||
|
for cell_path in center_vec {
|
||||||
|
if let Some(PathMember::String { val, .. }) = cell_path
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.find(|member| matches!(member, PathMember::String { .. }))
|
||||||
|
{
|
||||||
|
to_center.insert(val.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !headers.is_empty() {
|
if !headers.is_empty() {
|
||||||
output_string.push('|');
|
output_string.push('|');
|
||||||
|
|
||||||
for i in 0..headers.len() {
|
for i in 0..headers.len() {
|
||||||
if pretty {
|
if pretty {
|
||||||
output_string.push(' ');
|
output_string.push(' ');
|
||||||
|
if center.is_some() && to_center.contains(&headers[i]) {
|
||||||
|
output_string.push_str(&get_centered_string(
|
||||||
|
headers[i].clone(),
|
||||||
|
column_widths[i],
|
||||||
|
' ',
|
||||||
|
));
|
||||||
|
} else {
|
||||||
output_string.push_str(&get_padded_string(
|
output_string.push_str(&get_padded_string(
|
||||||
headers[i].clone(),
|
headers[i].clone(),
|
||||||
column_widths[i],
|
column_widths[i],
|
||||||
' ',
|
' ',
|
||||||
));
|
));
|
||||||
|
}
|
||||||
output_string.push(' ');
|
output_string.push(' ');
|
||||||
} else {
|
} else {
|
||||||
output_string.push_str(&headers[i]);
|
output_string.push_str(&headers[i]);
|
||||||
@ -295,11 +349,21 @@ fn get_output_string(
|
|||||||
|
|
||||||
output_string.push_str("\n|");
|
output_string.push_str("\n|");
|
||||||
|
|
||||||
for &col_width in column_widths.iter().take(headers.len()) {
|
for i in 0..headers.len() {
|
||||||
|
let centered_column = center.is_some() && to_center.contains(&headers[i]);
|
||||||
|
let border_char = if centered_column { ':' } else { ' ' };
|
||||||
if pretty {
|
if pretty {
|
||||||
output_string.push(' ');
|
output_string.push(border_char);
|
||||||
output_string.push_str(&get_padded_string(String::from("-"), col_width, '-'));
|
output_string.push_str(&get_padded_string(
|
||||||
output_string.push(' ');
|
String::from("-"),
|
||||||
|
column_widths[i],
|
||||||
|
'-',
|
||||||
|
));
|
||||||
|
output_string.push(border_char);
|
||||||
|
} else if centered_column {
|
||||||
|
output_string.push(':');
|
||||||
|
output_string.push('-');
|
||||||
|
output_string.push(':');
|
||||||
} else {
|
} else {
|
||||||
output_string.push('-');
|
output_string.push('-');
|
||||||
}
|
}
|
||||||
@ -318,7 +382,19 @@ fn get_output_string(
|
|||||||
for i in 0..row.len() {
|
for i in 0..row.len() {
|
||||||
if pretty && column_widths.get(i).is_some() {
|
if pretty && column_widths.get(i).is_some() {
|
||||||
output_string.push(' ');
|
output_string.push(' ');
|
||||||
output_string.push_str(&get_padded_string(row[i].clone(), column_widths[i], ' '));
|
if center.is_some() && to_center.contains(&headers[i]) {
|
||||||
|
output_string.push_str(&get_centered_string(
|
||||||
|
row[i].clone(),
|
||||||
|
column_widths[i],
|
||||||
|
' ',
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
output_string.push_str(&get_padded_string(
|
||||||
|
row[i].clone(),
|
||||||
|
column_widths[i],
|
||||||
|
' ',
|
||||||
|
));
|
||||||
|
}
|
||||||
output_string.push(' ');
|
output_string.push(' ');
|
||||||
} else {
|
} else {
|
||||||
output_string.push_str(&row[i]);
|
output_string.push_str(&row[i]);
|
||||||
@ -335,6 +411,24 @@ fn get_output_string(
|
|||||||
output_string
|
output_string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_centered_string(text: String, desired_length: usize, padding_character: char) -> String {
|
||||||
|
let total_padding = if text.len() > desired_length {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
desired_length - text.len()
|
||||||
|
};
|
||||||
|
|
||||||
|
let repeat_left = total_padding / 2;
|
||||||
|
let repeat_right = total_padding - repeat_left;
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
padding_character.to_string().repeat(repeat_left),
|
||||||
|
text,
|
||||||
|
padding_character.to_string().repeat(repeat_right)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_padded_string(text: String, desired_length: usize, padding_character: char) -> String {
|
fn get_padded_string(text: String, desired_length: usize, padding_character: char) -> String {
|
||||||
let repeat_length = if text.len() > desired_length {
|
let repeat_length = if text.len() > desired_length {
|
||||||
0
|
0
|
||||||
@ -355,7 +449,7 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
use nu_protocol::{Config, IntoPipelineData, Value, record};
|
use nu_protocol::{Config, IntoPipelineData, Value, casing::Casing, record};
|
||||||
|
|
||||||
fn one(string: &str) -> String {
|
fn one(string: &str) -> String {
|
||||||
string
|
string
|
||||||
@ -381,7 +475,10 @@ mod tests {
|
|||||||
"H1" => Value::test_string("Ecuador"),
|
"H1" => Value::test_string("Ecuador"),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(fragment(value, false, &Config::default()), "# Ecuador\n");
|
assert_eq!(
|
||||||
|
fragment(value, false, &None, &Config::default()),
|
||||||
|
"# Ecuador\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -390,7 +487,10 @@ mod tests {
|
|||||||
"H2" => Value::test_string("Ecuador"),
|
"H2" => Value::test_string("Ecuador"),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(fragment(value, false, &Config::default()), "## Ecuador\n");
|
assert_eq!(
|
||||||
|
fragment(value, false, &None, &Config::default()),
|
||||||
|
"## Ecuador\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -399,7 +499,10 @@ mod tests {
|
|||||||
"H3" => Value::test_string("Ecuador"),
|
"H3" => Value::test_string("Ecuador"),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(fragment(value, false, &Config::default()), "### Ecuador\n");
|
assert_eq!(
|
||||||
|
fragment(value, false, &None, &Config::default()),
|
||||||
|
"### Ecuador\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -408,7 +511,10 @@ mod tests {
|
|||||||
"BLOCKQUOTE" => Value::test_string("Ecuador"),
|
"BLOCKQUOTE" => Value::test_string("Ecuador"),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(fragment(value, false, &Config::default()), "> Ecuador\n");
|
assert_eq!(
|
||||||
|
fragment(value, false, &None, &Config::default()),
|
||||||
|
"> Ecuador\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -429,6 +535,7 @@ mod tests {
|
|||||||
table(
|
table(
|
||||||
value.clone().into_pipeline_data(),
|
value.clone().into_pipeline_data(),
|
||||||
false,
|
false,
|
||||||
|
&None,
|
||||||
&Config::default()
|
&Config::default()
|
||||||
),
|
),
|
||||||
one(r#"
|
one(r#"
|
||||||
@ -441,7 +548,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table(value.into_pipeline_data(), true, &Config::default()),
|
table(value.into_pipeline_data(), true, &None, &Config::default()),
|
||||||
one(r#"
|
one(r#"
|
||||||
| country |
|
| country |
|
||||||
| ----------- |
|
| ----------- |
|
||||||
@ -469,6 +576,7 @@ mod tests {
|
|||||||
table(
|
table(
|
||||||
value.clone().into_pipeline_data(),
|
value.clone().into_pipeline_data(),
|
||||||
false,
|
false,
|
||||||
|
&None,
|
||||||
&Config::default()
|
&Config::default()
|
||||||
),
|
),
|
||||||
one(r#"
|
one(r#"
|
||||||
@ -501,6 +609,7 @@ mod tests {
|
|||||||
table(
|
table(
|
||||||
value.clone().into_pipeline_data(),
|
value.clone().into_pipeline_data(),
|
||||||
false,
|
false,
|
||||||
|
&None,
|
||||||
&Config::default()
|
&Config::default()
|
||||||
),
|
),
|
||||||
one(r#"
|
one(r#"
|
||||||
@ -513,6 +622,270 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_center_column() {
|
||||||
|
let value = Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("1"),
|
||||||
|
"bar" => Value::test_string("2"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("3"),
|
||||||
|
"bar" => Value::test_string("4"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("5"),
|
||||||
|
"bar" => Value::test_string("6"),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let center_columns = Value::test_list(vec![Value::test_cell_path(CellPath {
|
||||||
|
members: vec![PathMember::test_string(
|
||||||
|
"bar".into(),
|
||||||
|
false,
|
||||||
|
Casing::Sensitive,
|
||||||
|
)],
|
||||||
|
})]);
|
||||||
|
|
||||||
|
let cell_path: Vec<CellPath> = center_columns
|
||||||
|
.into_list()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.into_cell_path().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let center: Option<Vec<CellPath>> = Some(cell_path);
|
||||||
|
|
||||||
|
// With pretty
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
true,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
| foo | bar |
|
||||||
|
| --- |:---:|
|
||||||
|
| 1 | 2 |
|
||||||
|
| 3 | 4 |
|
||||||
|
| 5 | 6 |
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Without pretty
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
false,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
|foo|bar|
|
||||||
|
|-|:-:|
|
||||||
|
|1|2|
|
||||||
|
|3|4|
|
||||||
|
|5|6|
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_center_column() {
|
||||||
|
let value = Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("1"),
|
||||||
|
"bar" => Value::test_string("2"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("3"),
|
||||||
|
"bar" => Value::test_string("4"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"foo" => Value::test_string("5"),
|
||||||
|
"bar" => Value::test_string("6"),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let center: Option<Vec<CellPath>> = Some(vec![]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
true,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
| foo | bar |
|
||||||
|
| --- | --- |
|
||||||
|
| 1 | 2 |
|
||||||
|
| 3 | 4 |
|
||||||
|
| 5 | 6 |
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_center_multiple_columns() {
|
||||||
|
let value = Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"command" => Value::test_string("ls"),
|
||||||
|
"input" => Value::test_string("."),
|
||||||
|
"output" => Value::test_string("file.txt"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"command" => Value::test_string("echo"),
|
||||||
|
"input" => Value::test_string("'hi'"),
|
||||||
|
"output" => Value::test_string("hi"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"command" => Value::test_string("cp"),
|
||||||
|
"input" => Value::test_string("a.txt"),
|
||||||
|
"output" => Value::test_string("b.txt"),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let center_columns = Value::test_list(vec![
|
||||||
|
Value::test_cell_path(CellPath {
|
||||||
|
members: vec![PathMember::test_string(
|
||||||
|
"command".into(),
|
||||||
|
false,
|
||||||
|
Casing::Sensitive,
|
||||||
|
)],
|
||||||
|
}),
|
||||||
|
Value::test_cell_path(CellPath {
|
||||||
|
members: vec![PathMember::test_string(
|
||||||
|
"output".into(),
|
||||||
|
false,
|
||||||
|
Casing::Sensitive,
|
||||||
|
)],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let cell_path: Vec<CellPath> = center_columns
|
||||||
|
.into_list()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.into_cell_path().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let center: Option<Vec<CellPath>> = Some(cell_path);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
true,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
| command | input | output |
|
||||||
|
|:-------:| ----- |:--------:|
|
||||||
|
| ls | . | file.txt |
|
||||||
|
| echo | 'hi' | hi |
|
||||||
|
| cp | a.txt | b.txt |
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_center_non_existing_column() {
|
||||||
|
let value = Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"name" => Value::test_string("Alice"),
|
||||||
|
"age" => Value::test_string("30"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"name" => Value::test_string("Bob"),
|
||||||
|
"age" => Value::test_string("5"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"name" => Value::test_string("Charlie"),
|
||||||
|
"age" => Value::test_string("20"),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let center_columns = Value::test_list(vec![Value::test_cell_path(CellPath {
|
||||||
|
members: vec![PathMember::test_string(
|
||||||
|
"none".into(),
|
||||||
|
false,
|
||||||
|
Casing::Sensitive,
|
||||||
|
)],
|
||||||
|
})]);
|
||||||
|
|
||||||
|
let cell_path: Vec<CellPath> = center_columns
|
||||||
|
.into_list()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.into_cell_path().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let center: Option<Vec<CellPath>> = Some(cell_path);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
true,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
| name | age |
|
||||||
|
| ------- | --- |
|
||||||
|
| Alice | 30 |
|
||||||
|
| Bob | 5 |
|
||||||
|
| Charlie | 20 |
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_center_complex_cell_path() {
|
||||||
|
let value = Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"k" => Value::test_string("version"),
|
||||||
|
"v" => Value::test_string("0.104.1"),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"k" => Value::test_string("build_time"),
|
||||||
|
"v" => Value::test_string("2025-05-28 11:00:45 +01:00"),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let center_columns = Value::test_list(vec![Value::test_cell_path(CellPath {
|
||||||
|
members: vec![
|
||||||
|
PathMember::test_int(1, false),
|
||||||
|
PathMember::test_string("v".into(), false, Casing::Sensitive),
|
||||||
|
],
|
||||||
|
})]);
|
||||||
|
|
||||||
|
let cell_path: Vec<CellPath> = center_columns
|
||||||
|
.into_list()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.into_cell_path().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let center: Option<Vec<CellPath>> = Some(cell_path);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
table(
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
true,
|
||||||
|
¢er,
|
||||||
|
&Config::default()
|
||||||
|
),
|
||||||
|
one(r#"
|
||||||
|
| k | v |
|
||||||
|
| ---------- |:--------------------------:|
|
||||||
|
| version | 0.104.1 |
|
||||||
|
| build_time | 2025-05-28 11:00:45 +01:00 |
|
||||||
|
"#)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_content_type_metadata() {
|
fn test_content_type_metadata() {
|
||||||
let mut engine_state = Box::new(EngineState::new());
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user