Skip to content

Commit

Permalink
✨ feat(display)!: add markdown table format
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the default format 'table' has been renamed to 'cli'.
  • Loading branch information
welpo committed Feb 4, 2024
1 parent 312e12c commit 2d76b24
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 54 deletions.
91 changes: 87 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
strum = "0.26"
strum_macros = "0.26"
tabled = "0.15.0"
thiserror = "1.0"
toml = "0.8"

Expand Down
2 changes: 1 addition & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub struct Opt {
pub display: bool,

/// Specify the output format for displaying the parsed commit message.
/// Options: "table", "json", "toml". Default: "table"
/// Options: "cli", "table", "json", "toml". Default: "cli"
#[arg(short = 'f', long, env = "GIT_SUMI_FORMAT")]
pub format: Option<ParsedCommitDisplayFormat>,

Expand Down
5 changes: 3 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ pub trait Configurable {
#[derive(Debug, Clone, Serialize, Deserialize, EnumIter, AsRefStr, Default)]
#[serde(rename_all = "lowercase")]
pub enum ParsedCommitDisplayFormat {
Json,
#[default]
Cli,
Json,
Table,
Toml,
}
Expand Down Expand Up @@ -296,7 +297,7 @@ impl Config {
let config_comments = HashMap::from([
("quiet", "Suppress progress messages."),
("display", "Shows the parsed commit message post-linting. See 'format' for options."),
("format", "Output format for the parsed commit message. Options: \"json\", \"table\", \"toml\"."),
("format", "Output format for the parsed commit message. Options: \"cli\", \"json\", \"table\", \"toml\"."),
("split_lines", "Process each non-empty line in the commit message as an individual commit."),
("gitmoji", "Rule: include one valid Gitmoji: https://gitmoji.dev/"),
("conventional", "Rule: follow Conventional Commits format: https://www.conventionalcommits.org/"),
Expand Down
94 changes: 59 additions & 35 deletions src/lint/display.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
use super::errors::SumiError;
use crate::config::ParsedCommitDisplayFormat;
use crate::parser::ParsedCommit;
use prettytable::{format, Cell, Row, Table};
use serde_json::Value;
use tabled::{
settings::{object::Rows, Disable, Style},
Table, Tabled,
};

pub fn display_parsed_commit(
commit: &ParsedCommit,
format: &ParsedCommitDisplayFormat,
) -> Result<(), SumiError> {
match format {
ParsedCommitDisplayFormat::Cli => {
display_parsed_commit_as_table(commit, ParsedCommitDisplayFormat::Cli)?
}
ParsedCommitDisplayFormat::Json => display_parsed_commit_as_json(commit)?,
ParsedCommitDisplayFormat::Table => display_parsed_commit_as_table(commit)?,
ParsedCommitDisplayFormat::Table => {
display_parsed_commit_as_table(commit, ParsedCommitDisplayFormat::Table)?
}
ParsedCommitDisplayFormat::Toml => display_parsed_commit_as_toml(commit)?,
}
Ok(())
Expand Down Expand Up @@ -38,46 +46,62 @@ fn display_parsed_commit_as_json(commit: &ParsedCommit) -> Result<(), SumiError>
Ok(())
}

fn display_parsed_commit_as_table(commit: &ParsedCommit) -> Result<(), SumiError> {
let mut table = Table::new();
add_row_for_vector(&mut table, "Gitmoji", &commit.gitmoji);
add_row_for_string(&mut table, "Commit type", &commit.commit_type);
add_row_for_string(&mut table, "Scope", &commit.scope);
add_row_for_string(&mut table, "Description", &Some(commit.description.clone()));
add_row_for_string(&mut table, "Body", &commit.body);
add_row_for_vector(&mut table, "Footers", &commit.footers);
add_row_for_bool(&mut table, "Is breaking", &commit.is_breaking);
add_row_for_string(
&mut table,
"Breaking description",
&commit.breaking_description,
);
add_row_for_vector(&mut table, "References", &commit.references);
table.set_format(*format::consts::FORMAT_BOX_CHARS);
table.printstd();
Ok(())
#[derive(Tabled)]
#[tabled(rename_all = "PascalCase")]
struct CommitRow {
key: &'static str,
value: String,
}

fn add_row_for_string(table: &mut Table, label: &str, value: &Option<String>) {
if let Some(v) = value {
table.add_row(Row::new(vec![Cell::new(label), Cell::new(v)]));
}
}
fn display_parsed_commit_as_table(
commit: &ParsedCommit,
format: ParsedCommitDisplayFormat,
) -> Result<(), SumiError> {
let mut rows = Vec::new();
let fields = [
("Gitmoji", commit.gitmoji.as_ref().map(|g| g.join(", "))),
("Commit type", commit.commit_type.clone()),
("Scope", commit.scope.clone()),
// "Description" is the only field that is guaranteed to be present.
("Description", Some(commit.description.clone())),
("Body", commit.body.clone()),
("Footers", commit.footers.as_ref().map(|f| f.join(", "))),
(
"Is breaking",
Some(format!("{}", commit.is_breaking.unwrap_or(false))),
),
("Breaking description", commit.breaking_description.clone()),
(
"References",
commit.references.as_ref().map(|r| r.join(", ")),
),
];

fn add_row_for_bool(table: &mut Table, label: &str, value: &Option<bool>) {
if let Some(v) = value {
let display_value = if *v { "true" } else { "false" };
table.add_row(Row::new(vec![Cell::new(label), Cell::new(display_value)]));
for (key, value) in fields.iter() {
if let Some(val) = value {
rows.push(CommitRow {
key,
value: val.clone(),
});
}
}
}

fn add_row_for_vector(table: &mut Table, label: &str, vec: &Option<Vec<String>>) {
if let Some(v) = vec {
if !v.is_empty() {
let joined = v.join(", ");
table.add_row(Row::new(vec![Cell::new(label), Cell::new(&joined)]));
let mut table = Table::new(rows);
match format {
ParsedCommitDisplayFormat::Cli => {
// Cute table for terminal; no header.
table.with(Style::modern());
table.with(Disable::row(Rows::first()));
}
ParsedCommitDisplayFormat::Table => {
// Markdown table with header.
table.with(Style::markdown());
}
_ => {}
}

println!("{}", table);
Ok(())
}

fn display_parsed_commit_as_toml(commit: &ParsedCommit) -> Result<(), SumiError> {
Expand Down
4 changes: 2 additions & 2 deletions sumi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ quiet = false
# Shows the parsed commit message post-linting. See 'format' for options.
display = true

# Output format for the parsed commit message. Options: "json", "table", "toml".
format = "table"
# Output format for the parsed commit message. Options: "cli", "json", "table", "toml".
format = "cli"

# Process each non-empty line in the commit message as an individual commit.
split_lines = false
Expand Down
4 changes: 2 additions & 2 deletions tests/lint/test_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ quiet = false
# Shows the parsed commit message post-linting. See 'format' for options.
display = false
# Output format for the parsed commit message. Options: "json", "table", "toml".
format = "table"
# Output format for the parsed commit message. Options: "cli", "json", "table", "toml".
format = "cli"
# Process each non-empty line in the commit message as an individual commit.
split_lines = false
Expand Down
32 changes: 28 additions & 4 deletions tests/lint/test_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@ fn assert_table_output(cmd: &mut Command) {
.stdout(contains("Footers │ BREAKING CHANGE:breaking description, Footer1:value1, Footer2:value2"))
.stdout(contains("Is breaking │ true"))
.stdout(contains("Breaking description │ breaking description"))
.stdout(contains("References │ #123, ce6df36"));
.stdout(contains("References │ #123, ce6df36"))
// No headers.
.stdout(contains("Key").not())
.stdout(contains("Value").not());
}

#[test]
fn success_display_format_table() {
fn success_display_format_cli() {
let mut cmd = run_isolated_git_sumi("");
cmd.arg("-dCG").arg("-f").arg("table").arg(COMMIT_MESSAGE);
cmd.arg("-dCG").arg("-f").arg("cli").arg(COMMIT_MESSAGE);
assert_table_output(&mut cmd);
}

#[test]
fn success_display_format_table_as_default() {
fn success_display_format_cli_as_default() {
let mut cmd = run_isolated_git_sumi("");
cmd.arg("-dCG").arg(COMMIT_MESSAGE);
assert_table_output(&mut cmd);
Expand Down Expand Up @@ -124,3 +127,24 @@ fn success_does_not_contain_header() {
.success()
.stdout(contains("header = ").not());
}

#[test]
fn success_display_format_markdown() {
let mut cmd = run_isolated_git_sumi("");
cmd.arg("-dCG").arg("-f").arg("table").arg(COMMIT_MESSAGE);
cmd.assert()
.success()
.stdout(contains("| Key"))
.stdout(contains("| Value"))
.stdout(contains("| Gitmoji | 🐛"))
.stdout(contains("|----------------------|----------------------------------------------------------------------|"))
.stdout(contains("| Scope | Scope"))
.stdout(contains("| Description | short description"))
.stdout(contains("| Body | Longer body description "))
.stdout(contains(
"| Footers | BREAKING CHANGE:breaking description, Footer1:value1, Footer2:value2 |",
))
.stdout(contains("| Is breaking | true "))
.stdout(contains("| Breaking description | breaking description "))
.stdout(contains("| References | #123, ce6df36 "));
}
Loading

0 comments on commit 2d76b24

Please sign in to comment.