midpad 1.1.1

Command line utility to pad texts.
Documentation
use std::{path::PathBuf, str::FromStr};

use clap::{
    builder::{
        styling::{AnsiColor, Effects},
        OsStr, PossibleValue, Styles,
    },
    Parser, ValueEnum,
};

const STYLES: Styles = Styles::styled()
    .header(AnsiColor::BrightGreen.on_default().effects(Effects::BOLD))
    .usage(AnsiColor::Green.on_default().effects(Effects::BOLD))
    .literal(
        AnsiColor::Magenta
            .on_default()
            .effects(Effects::UNDERLINE)
            .bold(),
    )
    .placeholder(AnsiColor::Magenta.on_default());

#[derive(Parser)]
#[command(name = "midpad", version, about = "Command line utility to pad texts.", long_about = None)]
#[command(author)]
#[command(styles = STYLES)]
pub struct Cli {
    #[arg(short, long)]
    pub raw: Option<String>,
    #[arg(short, long, value_name = "FILE")]
    pub file: Option<PathBuf>,
    #[arg(short, long)]
    pub output: Option<PathBuf>,
    #[arg(short, long, value_enum)]
    pub mode: Option<PadMode>,
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum PadMode {
    Left,
    Right,
    #[default]
    Middle,
}

impl FromStr for PadMode {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "l" | "left" => Ok(PadMode::Left),
            "m" | "middle" => Ok(PadMode::Left),
            "r" | "right" => Ok(PadMode::Left),
            _ => Err(()),
        }
    }
}

impl From<PadMode> for OsStr {
    fn from(value: PadMode) -> Self {
        match value {
            PadMode::Middle => OsStr::from("m"),
            PadMode::Left => OsStr::from("l"),
            PadMode::Right => OsStr::from("r"),
        }
    }
}

impl ValueEnum for PadMode {
    fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
        if !ignore_case {
            match input {
                "l" | "left" => Ok(PadMode::Left),
                "m" | "middle" => Ok(PadMode::Left),
                "r" | "right" => Ok(PadMode::Left),
                _ => Err(String::from("Failed to parse input!")),
            }
        } else {
            let input = input.to_lowercase();

            match input.as_str() {
                "l" | "left" => Ok(PadMode::Left),
                "m" | "middle" => Ok(PadMode::Left),
                "r" | "right" => Ok(PadMode::Left),
                _ => Err(String::from("Failed to parse input!")),
            }
        }
    }

    fn value_variants<'a>() -> &'a [Self] {
        &[PadMode::Middle, PadMode::Left, PadMode::Right]
    }

    fn to_possible_value(&self) -> Option<PossibleValue> {
        match self {
            PadMode::Middle => Some(
                PossibleValue::new("middle")
                    .help("Middle-pad texts.")
                    .alias("m"),
            ),
            PadMode::Left => Some(
                PossibleValue::new("left")
                    .help("Left-pad texts.")
                    .alias("l"),
            ),
            PadMode::Right => Some(
                PossibleValue::new("right")
                    .help("Right-pad texts.")
                    .alias("r"),
            ),
        }
    }
}