122 lines · 3.7 KB · Rust
RawDownload
// 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 as _;use std::path::PathBuf;use std::process::ExitCode; 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,     /// 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    }}