Skip to content

Commit

Permalink
Output tables in writer::Basic
Browse files Browse the repository at this point in the history
  • Loading branch information
ilslv committed Oct 1, 2021
1 parent d318077 commit a25cca2
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 41 deletions.
2 changes: 2 additions & 0 deletions codegen/tests/features/example.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ Feature: Example feature

Scenario: An example scenario
Given foo is 0
| sample | table |
| longer value | |
When foo is 0

Scenario: An example sync scenario
Expand Down
139 changes: 98 additions & 41 deletions src/writer/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! Default [`Writer`] implementation.
use std::{
cmp,
fmt::{Debug, Display},
ops::Deref,
};
Expand Down Expand Up @@ -43,7 +44,7 @@ pub struct Basic {
indent: usize,

/// Indicates whether last lines should be cleared.
needs_clear: bool,
needs_clear: Option<usize>,
}

#[async_trait(?Send)]
Expand Down Expand Up @@ -86,7 +87,7 @@ impl Default for Basic {
terminal: Term::stdout(),
styles: Styles::new(),
indent: 0,
needs_clear: false,
needs_clear: None,
}
}
}
Expand All @@ -107,10 +108,12 @@ impl Basic {
}

/// Clears last `n` lines if terminal is present.
fn clear_last_lines_if_term_present(&mut self, n: usize) {
if self.styles.is_present && self.needs_clear {
self.clear_last_lines(n).unwrap();
self.needs_clear = false;
fn clear_last_lines_if_term_present(&mut self) {
if let Some(lines) =
self.styles.is_present.then(|| self.needs_clear).flatten()
{
self.clear_last_lines(lines).unwrap();
self.needs_clear = None;
}
}

Expand All @@ -128,7 +131,7 @@ impl Basic {
/// [started]: event::Feature::Started
/// [`Feature`]: [`gherkin::Feature`]
fn feature_started(&mut self, feature: &gherkin::Feature) {
self.needs_clear = true;
self.needs_clear = Some(1);
self.write_line(
&self
.styles
Expand Down Expand Up @@ -168,7 +171,7 @@ impl Basic {
/// [started]: event::Rule::Started
/// [`Rule`]: [`gherkin::Rule`]
fn rule_started(&mut self, rule: &gherkin::Rule) {
self.needs_clear = true;
self.needs_clear = Some(1);
self.indent += 2;
self.write_line(&self.styles.ok(format!(
"{indent}{}: {}",
Expand Down Expand Up @@ -211,7 +214,7 @@ impl Basic {
/// [started]: event::Scenario::Started
/// [`Scenario`]: [`gherkin::Scenario`]
fn scenario_started(&mut self, scenario: &gherkin::Scenario) {
self.needs_clear = true;
self.needs_clear = Some(1);
self.indent += 2;
self.write_line(&self.styles.ok(format!(
"{}{}: {}",
Expand Down Expand Up @@ -267,16 +270,17 @@ impl Basic {
/// [started]: event::Step::Started
/// [`Step`]: [`gherkin::Step`]
fn step_started(&mut self, step: &gherkin::Step) {
self.needs_clear = true;
self.indent += 4;
if self.styles.is_present {
self.write_line(&format!(
"{}{} {}",
" ".repeat(self.indent),
let output = format!(
"{indent}{} {}{}",
step.keyword,
step.value,
))
.unwrap();
format_table(step.table.as_ref(), self.indent),
indent = " ".repeat(self.indent),
);
self.needs_clear = Some(output.lines().count());
self.write_line(&output).unwrap();
}
}

Expand All @@ -285,14 +289,17 @@ impl Basic {
/// [passed]: event::Step::Passed
/// [`Step`]: [`gherkin::Step`]
fn step_passed(&mut self, step: &gherkin::Step) {
self.clear_last_lines_if_term_present(1);
self.write_line(&self.styles.ok(format!(
// ✔
"{}\u{2714} {} {}",
" ".repeat(self.indent.saturating_sub(3)),
step.keyword,
step.value,
)))
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.ok({
format!(
// ✔
"{indent}\u{2714} {} {}{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
indent = " ".repeat(self.indent.saturating_sub(3)),
)
}))
.unwrap();
}

Expand All @@ -301,12 +308,13 @@ impl Basic {
/// [skipped]: event::Step::Skipped
/// [`Step`]: [`gherkin::Step`]
fn step_skipped(&mut self, feat: &gherkin::Feature, step: &gherkin::Step) {
self.clear_last_lines_if_term_present(1);
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.skipped(format!(
"{indent}? {} {}\n\
"{indent}? {} {}{}\n\
{indent} Step skipped: {}:{}:{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
feat.path
.as_ref()
.and_then(|p| p.to_str())
Expand All @@ -329,15 +337,16 @@ impl Basic {
world: Option<&W>,
info: &Info,
) {
self.clear_last_lines_if_term_present(1);
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.err(format!(
// ✘
"{indent}\u{2718} {} {}\n\
"{indent}\u{2718} {} {}{}\n\
{indent} Step failed: {}:{}:{}\n\
{indent} Captured output: {}\
{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
feat.path
.as_ref()
.and_then(|p| p.to_str())
Expand Down Expand Up @@ -399,17 +408,17 @@ impl Basic {
/// [`Background`]: [`gherkin::Background`]
/// [`Step`]: [`gherkin::Step`]
fn bg_step_started(&mut self, step: &gherkin::Step) {
self.needs_clear = true;
self.indent += 4;
if self.styles.is_present {
self.write_line(&format!(
"{}{}{} {}",
" ".repeat(self.indent.saturating_sub(2)),
"> ",
let output = format!(
"{indent}> {} {}{}",
step.keyword,
step.value,
))
.unwrap();
format_table(step.table.as_ref(), self.indent),
indent = " ".repeat(self.indent.saturating_sub(2)),
);
self.needs_clear = Some(output.lines().count());
self.write_line(&output).unwrap();
}
}

Expand All @@ -419,13 +428,14 @@ impl Basic {
/// [`Background`]: [`gherkin::Background`]
/// [`Step`]: [`gherkin::Step`]
fn bg_step_passed(&mut self, step: &gherkin::Step) {
self.clear_last_lines_if_term_present(1);
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.ok(format!(
// ✔
"{}\u{2714}> {} {}",
" ".repeat(self.indent.saturating_sub(3)),
"{indent}\u{2714}> {} {}{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
indent = " ".repeat(self.indent.saturating_sub(3)),
)))
.unwrap();
}
Expand All @@ -440,12 +450,13 @@ impl Basic {
feat: &gherkin::Feature,
step: &gherkin::Step,
) {
self.clear_last_lines_if_term_present(1);
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.skipped(format!(
"{indent}?> {} {}\n\
"{indent}?> {} {}{}\n\
{indent} Background step failed: {}:{}:{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
feat.path
.as_ref()
.and_then(|p| p.to_str())
Expand All @@ -469,15 +480,16 @@ impl Basic {
world: Option<&W>,
info: &Info,
) {
self.clear_last_lines_if_term_present(1);
self.clear_last_lines_if_term_present();
self.write_line(&self.styles.err(format!(
// ✘
"{indent}\u{2718}> {} {}\n\
"{indent}\u{2718}> {} {}{}\n\
{indent} Background step failed: {}:{}:{}\n\
{indent} Captured output: {}\
{}",
step.keyword,
step.value,
format_table(step.table.as_ref(), self.indent),
feat.path
.as_ref()
.and_then(|p| p.to_str())
Expand Down Expand Up @@ -519,3 +531,48 @@ fn format_world<W: Debug>(world: Option<&W>, indent: usize) -> String {
.then(|| format!("\n{}", world))
.unwrap_or_default()
}

/// Formats [`gherkin::Table`], then adds `indent`s to each
/// line to prettify the output.
fn format_table(table: Option<&gherkin::Table>, indent: usize) -> String {
let table = if let Some(table) = table {
table
} else {
return String::new();
};

let max_row_len = table
.rows
.iter()
.fold(None, |mut acc: Option<Vec<_>>, row| {
if let Some(acc) = acc.as_mut() {
for (cell, max_len) in row.iter().zip(acc) {
*max_len = cmp::max(*max_len, cell.len());
}
} else {
acc = Some(row.iter().map(String::len).collect::<Vec<_>>());
}

acc
})
.unwrap_or_default();

let mut table = table
.rows
.iter()
.map(|row| {
row.iter()
.zip(&max_row_len)
.map(|(cell, len)| format!("| {:1$} ", cell, len))
.collect::<String>()
})
.map(|row| format!("{}{}", " ".repeat(indent + 1), row))
.join("|\n");

if !table.is_empty() {
table.insert(0, '\n');
table.push('|');
}

table
}

0 comments on commit a25cca2

Please sign in to comment.