From 303ee4a1a3e99815d3629f6992508688d26e7f47 Mon Sep 17 00:00:00 2001 From: Danny Hooper Date: Wed, 15 May 2024 12:39:33 -0500 Subject: [PATCH] cli: add a fake code formatter tool for testing --- cli/Cargo.toml | 5 ++ cli/testing/fake-formatter.rs | 93 +++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 cli/testing/fake-formatter.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8d2b41c514..7283e9a8c5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -39,6 +39,11 @@ name = "fake-diff-editor" path = "testing/fake-diff-editor.rs" required-features = ["test-fakes"] +[[bin]] +name = "fake-formatter" +path = "testing/fake-formatter.rs" +required-features = ["test-fakes"] + [[test]] name = "runner" diff --git a/cli/testing/fake-formatter.rs b/cli/testing/fake-formatter.rs new file mode 100644 index 0000000000..aa06c89c16 --- /dev/null +++ b/cli/testing/fake-formatter.rs @@ -0,0 +1,93 @@ +// Copyright 2024 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fs::OpenOptions; +use std::io::Write; +use std::path::PathBuf; +use std::process::ExitCode; + +use clap::{arg, Parser}; +use itertools::Itertools; + +/// A fake code formatter, useful for testing +/// +/// `fake-formatter` is similar to `cat`. +/// `fake-formatter --reverse` is similar to `rev` (not `tac`). +/// `fake-formatter --stdout foo` is similar to `echo foo`. +/// `fake-formatter --stdout foo --stderr bar --fail` is similar to +/// `echo foo; echo bar >&2; false`. +/// `fake-formatter --tee foo` is similar to `tee foo`). +/// +/// This program acts as a portable alternative to that class of shell commands. +#[derive(Parser, Debug)] +struct Args { + /// Exit with non-successful status. + #[arg(long, default_value_t = false)] + fail: bool, + + /// Reverse the characters in each line when reading stdin + #[arg(long, default_value_t = false)] + reverse: bool, + + /// Write this string to stdout, and ignore stdin. + #[arg(long)] + stdout: Option, + + /// Write this string to stderr. + #[arg(long)] + stderr: Option, + + /// Duplicate stdout into this file. + #[arg(long)] + tee: Option, +} + +fn main() -> ExitCode { + let args: Args = Args::parse(); + // Code formatters tend to print errors before printing the result. + if let Some(data) = args.stderr { + eprint!("{}", data); + } + let stdout = if let Some(data) = args.stdout { + data // --reverse doesn't apply to --stdout. + } else { + std::io::stdin() + .lines() + .map(|line| { + format!( + "{}\n", + if args.reverse { + line.unwrap().chars().rev().collect() + } else { + line.unwrap() + } + ) + }) + .join("") + }; + print!("{}", stdout); + if let Some(path) = args.tee { + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .unwrap(); + write!(file, "{}", stdout).unwrap(); + } + if args.fail { + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + } +}