formattable 0.2.0

Ergonomically support formatted output
Documentation
use crate::Format;
use clap::Args;
use serde::{Deserialize, Serialize};
use std::ops::Deref;

/// An embeddable format argument struct for use in clap CLIs.
///
/// `FormatArg`implements `Deref<Target = Format>`, so you can access methods
/// from [`Format`] like [`Format::to_string`] ergonomically on the `FormatArg`
/// itself.
///
/// For an optional format argument, use [`FormatArgOpt`].
///
/// # Serialization
///
/// `FormatArg` implements `Serialize` and `Deserialize` and flattens its
/// internal `format` field such that `FormatArg` should always be transparent
/// to any callers or persistence.
///
/// # Examples
///
/// The following shows how to use this in a clap CLI struct as a required
/// argument:
///
/// ```
/// use clap::Parser;
/// use formattable::FormatArg;
/// use serde::Serialize;
///
/// /// Demonstrate how to use `formattable` in a `clap`-based CLI.
/// ///
/// /// This example just dumps the CLI arguments themselves as the selected format.
/// #[derive(Debug, Parser, Serialize)]
/// struct Cli {
///     #[clap(flatten)]
///     format: FormatArg,
/// }
///
/// // Replace this with `Cli::parse` to actually parse passed arguments.
/// let cli = Cli::parse_from(["", "-f", "json"]);
///
/// println!("{}", cli.format.to_string_pretty(&cli).unwrap());
/// ```
#[derive(Args, Clone, Copy, Debug, Deserialize, Serialize)]
pub struct FormatArg {
    /// Select a format for output.
    #[clap(short, long, value_enum)]
    #[serde(flatten)]
    format: Format,
}

impl From<Format> for FormatArg {
    fn from(value: Format) -> Self {
        Self {
            format: value,
        }
    }
}

impl From<FormatArg> for Format {
    fn from(value: FormatArg) -> Self {
        value.format
    }
}

impl Deref for FormatArg {
    type Target = Format;

    fn deref(&self) -> &Self::Target {
        &self.format
    }
}

/// An embeddable optional format argument struct for use in clap CLIs.
///
/// `FormatArgOpt` implements `Deref<Target = Option<Format>>`, so you can access the
/// format ergonomically via destructuring. See the examples for details.
///
/// For a non-optional form of the argument, see [`FormatArg`].
///
/// # Serialization
///
/// `FormatArgOpt` implements `Serialize` and `Deserialize` and flattens its
/// internal `format` field such that `FormatArgOpt` should always be transparent
/// to any callers or persistence.
///
/// # Examples
///
/// ```
/// use clap::Parser;
/// use formattable::FormatArgOpt;
/// use serde::Serialize;
///
/// /// Demonstrate how to use `formattable::FormatArgOpt` in a `clap`-based CLI.
/// ///
/// /// This example just dumps the CLI arguments themselves in the selected format.
/// #[derive(Debug, Parser, Serialize)]
/// struct Cli {
///     #[clap(flatten)]
///     format: FormatArgOpt,
/// }
///
/// // Prints nothing if no format is specified.
/// let cli = Cli::parse_from([""]);
/// if let Some(format) = *cli.format {
///     println!("{}", format.to_string_pretty(&cli).unwrap());
/// }
///
/// // Prints JSON if JSON is specified.
/// let cli = Cli::parse_from(["", "-f", "json"]);
/// if let Some(format) = *cli.format {
///     println!("{}", format.to_string_pretty(&cli).unwrap());
/// }
/// ```
#[derive(Args, Clone, Copy, Debug, Deserialize, Serialize)]
pub struct FormatArgOpt {
    /// Select a format for output.
    #[clap(short, long, value_enum)]
    #[serde(flatten)]
    format: Option<Format>,
}

impl From<Format> for FormatArgOpt {
    fn from(value: Format) -> Self {
        Self {
            format: Some(value),
        }
    }
}

impl Deref for FormatArgOpt {
    type Target = Option<Format>;

    fn deref(&self) -> &Self::Target {
        &self.format
    }
}