wallust_themes 1.1.0

Embeded colorschemes in an array of u32s.
Documentation
//! NOTE: Adding the ~150 of lines below `main()` is worth 0.1mb of 'savings' (roughly), also seeing
//!       `"colors": { color0: "#eeeeee", .." and so on in the binary was kinda ugly.
//! Another reason for this change was to allow easily adding more themes, which builds automagically.
//! Initially, wallust embeded the colorschemes as literal Strings, which took a bit more size in the final binary.
//! This approach of using simple uints, saves some space.

use std::path::Path;
use serde::{Serialize,Deserialize};

// #[allow(unused)]
// macro_rules! p {
//     ($($tokens: tt)*) => {
//         println!("cargo:warning={}", format!($($tokens)*))
//     }
// }

/// Simple rename
type Colors = [u32; 19];

/// Builds and stores values (Colors type above), keys (Scheme names) and len (overall vec size).
fn main() {
    let out = std::env::var_os("OUT_DIR").unwrap();

    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=colorschemes/");

    // p!("Building and embedding colorschemes..");

    let val_dir = Path::new(&out).join("value.rs");
    let len_dir = Path::new(&out).join("len.rs");
    let key_dir = Path::new(&out).join("col.rs");

    let mut keys  = String::new();
    let mut vals = String::new();

    keys.push('[');
    vals.push('[');

    let wal = builtinschemes();
    let gogh = gogh(); //empty if feature disabled

    let themes = [ gogh, wal ].concat();
    let len = themes.len();

    for i in themes {
        let name = i.0;
        let c = i.1;

        keys.push_str(&format!("\"{name}\","));
        vals.push_str(&printcols(c));
    }


    //NO SEMICOLON AT THE END
    //<https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/builtin/static.INCOMPLETE_INCLUDE.html>
    keys.push(']');
    vals.push(']');

    std::fs::write(key_dir, &keys).unwrap();
    std::fs::write(val_dir, &vals).unwrap();
    std::fs::write(len_dir, len.to_string()).unwrap();

    // p!("{vals}");

    fn printcols(a: Colors) -> String {
        let mut s = String::new();
        s.push('[');
        for i in a {
            s.push_str(&format!("{i},"));
        }
        s.push_str("],");
        s
    }
}

/// This uses the themes in the colorscheme/ directory, like pywal
/// Stores (name, value)
fn builtinschemes() -> Vec<(String, Colors)> {
    let dir = "./colorschemes/";
    let d = std::fs::read_dir(dir).unwrap();
    let mut ret = vec![];

    for i in d {
        let i = i.unwrap().path();
        let name = i.file_name().unwrap().to_string_lossy();
        // remove .json from the name
        let name = &name[..name.len() - 5];

        let f = std::fs::read_to_string(&i).unwrap();
        let ser = serde_json::from_str::<WalTheme>(&f).unwrap();
        let c: Colors = ser.to_colors();
        ret.push((name.to_string(), c));
    }

    ret
}


/// Uses Gogh recompiled themes:
/// <https://github.com/Gogh-Co/Gogh>
fn gogh() -> Vec<(String, Colors)> {
    #[cfg(feature = "gogh")]
    {
        let mut ret = vec![];
        let themes = serde_json::from_slice::<Vec<Gogh>>(include_bytes!("themes.json")).unwrap();
        for i in themes {
            let name = i.name
                .replace(" ", "-")
                .replace("(", "") // this ruins completions
                .replace(")", "")
                ;
            ret.push((name, i.to_colors()))
        }
        ret
    }

    #[cfg(not(feature = "gogh"))]
    vec![]
}

impl WalTheme {
    /// This SHOULD follows the Spec that the readme indicates.
    fn to_colors(&self) -> Colors {
        let c = &self.colors;
        let s = &self.special;
        [
            c.color0 .decode_hex(),
            c.color1 .decode_hex(),
            c.color2 .decode_hex(),
            c.color3 .decode_hex(),
            c.color4 .decode_hex(),
            c.color5 .decode_hex(),
            c.color6 .decode_hex(),
            c.color7 .decode_hex(),
            c.color8 .decode_hex(),
            c.color9 .decode_hex(),
            c.color10.decode_hex(),
            c.color11.decode_hex(),
            c.color12.decode_hex(),
            c.color13.decode_hex(),
            c.color14.decode_hex(),
            c.color15.decode_hex(),
            s.background.decode_hex(),
            s.foreground.decode_hex(),
            s.cursor    .decode_hex(),
        ]
    }
}

#[cfg(feature = "gogh")]
impl Gogh {
    /// This SHOULD follows the Spec that the readme indicates.
    fn to_colors(&self) -> Colors {
        let c = self;
        [
            c.color_01 .decode_hex(),
            c.color_02 .decode_hex(),
            c.color_03 .decode_hex(),
            c.color_04 .decode_hex(),
            c.color_05 .decode_hex(),
            c.color_06 .decode_hex(),
            c.color_07 .decode_hex(),
            c.color_08 .decode_hex(),
            c.color_09 .decode_hex(),
            c.color_10.decode_hex(),
            c.color_11.decode_hex(),
            c.color_12.decode_hex(),
            c.color_13.decode_hex(),
            c.color_14.decode_hex(),
            c.color_15.decode_hex(),
            c.color_16 .decode_hex(),
            c.background.decode_hex(),
            c.foreground.decode_hex(),
            c.cursor    .decode_hex(),
        ]
    }
}


impl HexConversion for String {
    fn decode_hex(&self) -> u32 {
        let s = if &self[..1] == "#" { &self[1..] } else { self };
        u32::from_str_radix(s, 16).unwrap()
    }
}

pub trait HexConversion {
    fn decode_hex(&self) -> u32;
}

#[derive(Deserialize, Serialize)]
pub struct Gogh {
    name: String,
    author: String,
    variant: String,
    color_01: String,
    color_02: String,
    color_03: String,
    color_04: String,
    color_05: String,
    color_06: String,
    color_07: String,
    color_08: String,
    color_09: String,
    color_10: String,
    color_11: String,
    color_12: String,
    color_13: String,
    color_14: String,
    color_15: String,
    color_16: String,
    background: String,
    foreground: String,
    cursor: String,
    hash: String,
}

#[derive(Deserialize, Serialize)]
pub struct WalColors {
    pub color0 : String,
    pub color1 : String,
    pub color2 : String,
    pub color3 : String,
    pub color4 : String,
    pub color5 : String,
    pub color6 : String,
    pub color7 : String,
    pub color8 : String,
    pub color9 : String,
    pub color10: String,
    pub color11: String,
    pub color12: String,
    pub color13: String,
    pub color14: String,
    pub color15: String,
}

/// Pywal colorscheme
#[derive(Deserialize, Serialize)]
pub struct WalTheme {
    pub special: WalSpecial,
    pub colors: WalColors,
}

#[derive(Deserialize, Serialize)]
pub struct WalSpecial {
    pub background: String,
    pub foreground: String,
    pub cursor: String,
}