mirror of
https://github.com/jj-vcs/jj.git
synced 2025-12-23 06:01:01 +00:00
167 lines
5.1 KiB
Rust
167 lines
5.1 KiB
Rust
// 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::Read as _;
|
|
use std::io::Write as _;
|
|
use std::path::PathBuf;
|
|
use std::process::ExitCode;
|
|
|
|
use bstr::ByteSlice as _;
|
|
use clap::Parser;
|
|
use itertools::Itertools as _;
|
|
|
|
/// 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,
|
|
|
|
/// Abort instead of exiting
|
|
#[arg(long, default_value_t = false, conflicts_with = "fail")]
|
|
abort: 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,
|
|
|
|
/// Convert all characters to lowercase when reading stdin.
|
|
#[arg(long, default_value_t = false)]
|
|
lowercase: bool,
|
|
|
|
/// Adds a line to the end of the file
|
|
#[arg(long)]
|
|
append: Option<String>,
|
|
|
|
/// Write this string to stdout, and ignore stdin.
|
|
#[arg(long)]
|
|
stdout: Option<String>,
|
|
|
|
/// Write this string to stderr.
|
|
#[arg(long)]
|
|
stderr: Option<String>,
|
|
|
|
/// Duplicate stdout into this file.
|
|
#[arg(long)]
|
|
tee: Option<PathBuf>,
|
|
|
|
/// Read one byte at a time, and send one byte to the stdout before reading
|
|
/// the next byte.
|
|
#[arg(long, default_value_t = false)]
|
|
byte_mode: bool,
|
|
}
|
|
|
|
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 {
|
|
// Other content-altering flags don't apply to --stdout.
|
|
assert!(!args.reverse);
|
|
assert!(!args.uppercase);
|
|
assert!(!args.lowercase);
|
|
assert!(args.append.is_none());
|
|
print!("{data}");
|
|
data
|
|
} else if args.byte_mode {
|
|
assert!(!args.reverse);
|
|
assert!(args.append.is_none());
|
|
|
|
let mut stdout = vec![];
|
|
#[expect(clippy::unbuffered_bytes)]
|
|
for byte in std::io::stdin().bytes() {
|
|
let byte = byte.expect("Failed to read from stdin");
|
|
let output = if args.uppercase {
|
|
byte.to_ascii_uppercase()
|
|
} else if args.lowercase {
|
|
assert!(!args.uppercase);
|
|
byte.to_ascii_lowercase()
|
|
} else {
|
|
byte
|
|
};
|
|
stdout.push(output);
|
|
std::io::stdout()
|
|
.write_all(&[output])
|
|
.expect("Failed to write to stdout");
|
|
}
|
|
stdout
|
|
.to_str()
|
|
.expect("Output is not a valid UTF-8 string")
|
|
.to_owned()
|
|
} else {
|
|
let mut input = vec![];
|
|
std::io::stdin()
|
|
.read_to_end(&mut input)
|
|
.expect("Failed to read from stdin");
|
|
let mut stdout = input
|
|
.lines_with_terminator()
|
|
.map(|line| {
|
|
let line = line
|
|
.to_str()
|
|
.expect("The input is not valid UTF-8 string")
|
|
.to_owned();
|
|
let line = if args.reverse {
|
|
line.chars().rev().collect()
|
|
} else {
|
|
line
|
|
};
|
|
if args.uppercase {
|
|
assert!(!args.lowercase);
|
|
line.to_uppercase()
|
|
} else if args.lowercase {
|
|
assert!(!args.uppercase);
|
|
line.to_lowercase()
|
|
} else {
|
|
line
|
|
}
|
|
})
|
|
.join("");
|
|
if let Some(line) = args.append {
|
|
stdout.push_str(&line);
|
|
}
|
|
print!("{stdout}");
|
|
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.abort {
|
|
std::process::abort()
|
|
} else if args.fail {
|
|
ExitCode::FAILURE
|
|
} else {
|
|
ExitCode::SUCCESS
|
|
}
|
|
}
|