From 1ece76d128d686c6a740d49a2f92f7a1dfc7a683 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 | 101 ++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 cli/testing/fake-formatter.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8d2b41c51..7283e9a8c 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 000000000..777e4a5b3 --- /dev/null +++ b/cli/testing/fake-formatter.rs @@ -0,0 +1,101 @@ +// 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, + + /// Convert all characters to uppercase when reading stdin. + #[arg(long, default_value_t = false)] + uppercase: 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", { + let line = if args.reverse { + line.unwrap().chars().rev().collect() + } else { + line.unwrap() + }; + if args.uppercase { + line.to_uppercase() + } else { + line + } + }) + }) + .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 + } +}