forked from mirrors/jj
123 lines
3.7 KiB
Rust
123 lines
3.7 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::Write;
|
|
use std::path::PathBuf;
|
|
use std::process::ExitCode;
|
|
|
|
use clap::arg;
|
|
use clap::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,
|
|
|
|
/// 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>,
|
|
}
|
|
|
|
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());
|
|
data
|
|
} else {
|
|
let mut stdout = std::io::stdin()
|
|
.lines()
|
|
.map(|line| {
|
|
format!("{}\n", {
|
|
let line = if args.reverse {
|
|
line.unwrap().chars().rev().collect()
|
|
} else {
|
|
line.unwrap()
|
|
};
|
|
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);
|
|
}
|
|
stdout
|
|
};
|
|
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
|
|
}
|
|
}
|