Skip to content

Commit

Permalink
Add rew x --null example
Browse files Browse the repository at this point in the history
  • Loading branch information
jpikl committed Mar 25, 2024
1 parent 12f91f9 commit c5a8c79
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 26 deletions.
2 changes: 1 addition & 1 deletion TODO.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
0.4
- Run integration tests using multiple shells.
- `rew x --null` example with propagation.
- Better shell specific examples for `rew x`.
- Add `rew x --quote` with examples.
- Fix `--examples` order in generated docs.
Expand All @@ -14,4 +13,5 @@
- Add here more TODOs for command replacement of old rew filters.

1.0
- Add `case` conversion command based on https://docs.rs/heck/latest/heck/
- Setup man generator.
21 changes: 21 additions & 0 deletions docs/reference/rew-x.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,24 @@ rew x '{seq}:\n\t{}'
</ul>
</div>
</div>

All global options `-0, --null`, `--buf-size` and `--buf-mode` are propagated to rew subcommands. Do not forget configure NUL separator manually for any external commands.

```sh
rew x --null '{upper | sed --null-data "s/^.//g"}'
```

<div class="example-io">
<div class="example-io-stream">
<small><b>stdin:</b></small>
<ul>
<li><code>aa\0bb\0cc\0</code></li>
</ul>
</div>
<div class="example-io-stream">
<small><b>stdout:</b></small>
<ul>
<li><code>A\0B\0C\0</code></li>
</ul>
</div>
</div>
6 changes: 6 additions & 0 deletions src/commands/x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ pub const META: Meta = command_meta! {
input: &["first", "second", "third"],
output: &["1:", "\tfirst", "2:", "\tsecond", "3:", "\tthird"],
},
"All global options `-0, --null`, `--buf-size` and `--buf-mode` are propagated to rew subcommands. \
Do not forget configure NUL separator manually for any external commands.": {
args: &["--null", "{upper | sed --null-data 's/^.//g'}"],
input: &["aa", "bb", "cc"],
output: &["A", "B", "C"],
},
],
};

Expand Down
44 changes: 32 additions & 12 deletions src/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ pub struct Example {
pub output: &'static [&'static str],
}

impl Example {
pub fn has_null_arg(&self) -> bool {
self.args.iter().any(|arg| *arg == "-0" || *arg == "--null")
}
}

#[macro_export]
macro_rules! examples {
($($text:literal: { args: $args:expr, input: $input:expr, output: $output:expr, }),*,) => {
Expand Down Expand Up @@ -102,9 +108,10 @@ fn write(writer: &mut impl Write, command: &str, examples: &[Example]) -> Result
write_text(writer, example.text, term_width)?;
writeln!(writer)?;
write_command(writer, command, example.args)?;
write_io(writer, example.input, example.output)?;
write_io(writer, example)?;
}

writeln!(writer)?;
Ok(())
}

Expand Down Expand Up @@ -163,13 +170,14 @@ fn write_command(writer: &mut impl Write, subcmd: &str, args: &[&str]) -> io::Re
writeln!(writer, " {GREEN}╰{}╯{RESET}", "─".repeat(width))
}

fn write_io(writer: &mut impl Write, input: &[&str], output: &[&str]) -> io::Result<()> {
if input.is_empty() && output.is_empty() {
fn write_io(writer: &mut impl Write, example: &Example) -> io::Result<()> {
if example.input.is_empty() && example.output.is_empty() {
return Ok(());
}

let input = normalize_lines(input);
let output = normalize_lines(output);
let null_separator = example.has_null_arg();
let input = normalize_lines(example.input, null_separator);
let output = normalize_lines(example.output, null_separator);

let input_label = "stdin:";
let output_label = "stdout:";
Expand Down Expand Up @@ -211,11 +219,17 @@ fn write_io_line(writer: &mut impl Write, lines: &[String], index: usize) -> io:
write!(writer, " ")?;

if let Some(line) = lines.get(index) {
write!(writer, "{GREEN}\"{RESET}{line}{GREEN}\"{RESET}{RESET}",)?;
writer.write_all(" ".repeat(max_line_width(lines) - line.width()).as_bytes())
} else {
write!(writer, "{}", " ".repeat(max_line_width(lines) + 2))
let line = line.replace("\\0", &format!("{YELLOW}\\0{RESET}"));
write!(writer, "{GREEN}\"{RESET}{line}{GREEN}\"{RESET}{RESET}")?;
}

let padding = if let Some(line) = lines.get(index) {
max_line_width(lines) - line.width()
} else {
max_line_width(lines) + 2
};

write!(writer, "{}", " ".repeat(padding))
}

fn normalize_args(args: &[&str]) -> Vec<String> {
Expand All @@ -232,11 +246,17 @@ fn normalize_args(args: &[&str]) -> Vec<String> {
.collect::<Vec<_>>()
}

fn normalize_lines(lines: &[&str]) -> Vec<String> {
lines
fn normalize_lines(lines: &[&str], null_separator: bool) -> Vec<String> {
let lines = lines
.iter()
.map(|line| line.replace('\t', " "))
.collect::<Vec<_>>()
.collect::<Vec<_>>();

if null_separator {
vec![format!("{}\\0", lines.join("\\0"))]
} else {
lines
}
}

fn max_line_width(lines: &[String]) -> usize {
Expand Down
15 changes: 11 additions & 4 deletions tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ use std::env;
fn examples() {
for meta in rew::commands::METAS {
for example in meta.examples {
// Such examples require coreutils with NUL separator support
// which are not available on MacOS by default.
if cfg!(target_os = "macos") && example.has_null_arg() {
continue;
}

println!("[{}] {}", meta.name, first_line(example.text));

Command::cargo_bin(crate_name!())
.unwrap()
.env("SHELL", "sh") // Examples expect UNIX shell
.arg(meta.name)
.args(example.args)
.write_stdin(join_lines(example.input))
.write_stdin(join_lines(example.input, example.has_null_arg()))
.assert()
.success()
.stdout(join_lines(example.output))
.stdout(join_lines(example.output, example.has_null_arg()))
.stderr("");
}
}
Expand All @@ -26,6 +32,7 @@ fn first_line(text: &str) -> &str {
text.split('\n').next().unwrap_or_default()
}

fn join_lines(lines: &[&str]) -> String {
format!("{}\n", lines.join("\n"))
fn join_lines(lines: &[&str], null_separator: bool) -> String {
let separator = if null_separator { "\0" } else { "\n" };
format!("{}{separator}", lines.join(separator))
}
31 changes: 22 additions & 9 deletions xtask/src/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,11 @@ fn write_example(writer: &mut impl Write, command: &Adapter<'_>, example: &Examp
writeln!(writer, "<div class=\"example-io\">")?;

if !example.input.is_empty() {
write_example_io(writer, "stdin", example.input)?;
write_example_io(writer, "stdin", example.input, example.has_null_arg())?;
}

if !example.output.is_empty() {
write_example_io(writer, "stdout", example.output)?;
write_example_io(writer, "stdout", example.output, example.has_null_arg())?;
}

writeln!(writer, "</div>")?;
Expand All @@ -303,21 +303,34 @@ fn write_example(writer: &mut impl Write, command: &Adapter<'_>, example: &Examp
Ok(())
}

fn write_example_io(writer: &mut impl Write, title: &str, lines: &[&str]) -> Result<()> {
fn write_example_io(
writer: &mut impl Write,
title: &str,
lines: &[&str],
null_separator: bool,
) -> Result<()> {
writeln!(writer, "<div class=\"example-io-stream\">")?;
writeln!(writer, "<small><b>{title}:</b></small>")?;
writeln!(writer, "<ul>")?;

for line in lines {
let line = line
.replace(' ', "\u{a0}") // Unbreakable spaces to prevent trimming by mdbook html renderer.
.replace('\t', "\u{a0}\u{a0}\u{a0}\u{a0}");

// <code> because mdbook html renderer cannot render empty `` (which we need for empty IO lines).
// We use <code> because mdbook cannot render empty `` which we need for empty IO lines.
if null_separator {
let line = format!("{}\\0", lines.join("\\0"));
let line = normalize_line(&line);
writeln!(writer, "<li><code>{line}</code></li>")?;
} else {
for line in lines {
let line = normalize_line(line);
writeln!(writer, "<li><code>{line}</code></li>")?;
}
}

writeln!(writer, "</ul>")?;
writeln!(writer, "</div>")?;
Ok(())
}

fn normalize_line(line: &str) -> String {
line.replace(' ', "\u{a0}") // Unbreakable spaces to prevent trimming by mdbook html renderer.
.replace('\t', "\u{a0}\u{a0}\u{a0}\u{a0}")
}

0 comments on commit c5a8c79

Please sign in to comment.