fmt_adapter 0.2.0

Provides newtype adaptors to and from any formatting trait
Documentation
/// Traits to include when generating.
///
/// If any formatting traits get added in the future (even in nightlies), file an issue on the GitHub repository.
static GEN_TRAITS: &[&str] = &[
    "Debug",
    "Display",
    "LowerExp",
    "LowerHex",
    "Octal",
    "Pointer",
    "UpperExp",
    "UpperHex"
];

/// Warning to add to the beginning of all generated files.
static GENERATED_WARNING: &str = "\
// === === === === === === Warning === === === === === === //
// This file has been GENERATED by the BUILD SCRIPT. Any   //
// modifications will be DISCARDED when switching to a     //
// different version/release channel/target platform of    //
// the Rust toolchain or if the build script receives an   //
// update.                                                 //
// === === === === === === === === === === === === === === //
";

use std::{env, fs, path::PathBuf, io::Write};

fn main() {
    println!("cargo:rerun-if-changed=build.rs");

    let crate_dir = PathBuf::from(env::var_os("OUT_DIR")
        .expect("Failed to retreive output directory (is Cargo broken?)"));

    let mut file = fs::File::create(crate_dir.join("BUILDSCRIPT_GENERATED_only_adapters.rs")).unwrap();
    file.write_all(generate_only_adapters_file().as_bytes()).unwrap();
    let mut file = fs::File::create(crate_dir.join("BUILDSCRIPT_GENERATED_fmt_adapters.rs")).unwrap();
    file.write_all(generate_fmt_adapters_file().as_bytes()).unwrap();
}

fn generate_only_adapters_file() -> String {
    let mut snippets = Vec::<String>::with_capacity(GEN_TRAITS.len() + 1);
    snippets.push(GENERATED_WARNING.to_string());
    for tr in GEN_TRAITS {
        snippets.push(mk_only(tr));
    }
    snippets.join("\n")
}
fn generate_fmt_adapters_file() -> String {
    let mut snippets = Vec::<String>::with_capacity(GEN_TRAITS.len() + 1);
    snippets.push(GENERATED_WARNING.to_string());
    for trd in GEN_TRAITS {
        for trs in GEN_TRAITS {
            if trd == trs {continue;}
            snippets.push(mk_adapter(trd, trs));
        }
    }
    snippets.join("\n")
}

fn mk_only(tr: &str) -> String {
    format!("\
/// Only implements the `{tr}` formatting trait despite any other trait being implemented on `T`.
#[repr(transparent)]
pub struct Only{tr}<T: {tr}> (pub T);
impl<T: {tr}> From<T> for Only{tr}<T> {{
    #[inline(always)]
    fn from(op: T) -> Self {{
        Self(op)
    }}
}}
impl<T: {tr}> {tr} for Only{tr}<T> {{
    #[inline(always)]
    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {{
        self.0.fmt(f)
    }}
}}
impl<T: {tr}> Only{tr}<T> {{
    /// Consumes the adapter and returns the wrapped value.
    #[inline(always)]
    pub fn into_inner(self) -> T {{
        self.0
    }}
}}",
        tr = tr
    )
}
fn mk_adapter(dst_trait: &str, src_trait: &str) -> String {
    format!("\
/// Provides `{trd}` formatting by using the result of invoking `{trs}` formatting.
#[repr(transparent)]
pub struct {trd}From{trs}<T: {trs}> (pub T);
impl<T: {trs}> From<T> for {trd}From{trs}<T> {{
    #[inline(always)]
    fn from(op: T) -> Self {{
        Self(op)
    }}
}}
impl<T: {trs}> {trd} for {trd}From{trs}<T> {{
    #[inline(always)]
    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {{
        {trs}::fmt(&self.0, f)
    }}
}}
impl<T: {trs}> {trd}From{trs}<T> {{
    /// Consumes the adapter and returns the wrapped value.
    #[inline(always)]
    pub fn into_inner(self) -> T {{
        self.0
    }}
}}",
        trs = src_trait,
        trd = dst_trait
    )
}