wallswitch 0.60.6

randomly selects wallpapers for multiple monitors
Documentation
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};

const RESET: &str = "\x1b[0m";
const BOLD: &str = "\x1b[1m";
const DIM: &str = "\x1b[2m";
const BLACK: &str = "\x1b[30m";
const RED: &str = "\x1b[31m";
const GREEN: &str = "\x1b[32m";
const YELLOW: &str = "\x1b[33m";
const BLUE: &str = "\x1b[34m";
const MAGENTA: &str = "\x1b[35m";
const CYAN: &str = "\x1b[36m";
const WHITE: &str = "\x1b[37m";

pub const CURSOR_TO_START: &str = "\r";
pub const ERASE_LINE_TO_END: &str = "\x1b[K";

pub trait Colors {
    fn bold(&self) -> String;
    fn dimmed(&self) -> String;
    fn black(&self) -> String;
    fn red(&self) -> String;
    fn green(&self) -> String;
    fn yellow(&self) -> String;
    fn blue(&self) -> String;
    fn magenta(&self) -> String;
    fn cyan(&self) -> String;
    fn white(&self) -> String;
    fn to_line_start(&self) -> String;
}

impl<T> Colors for T
where
    T: Display,
{
    fn bold(&self) -> String {
        format!("{BOLD}{self}{RESET}")
    }
    fn dimmed(&self) -> String {
        format!("{DIM}{self}{RESET}")
    }
    fn black(&self) -> String {
        format!("{BLACK}{self}{RESET}")
    }
    fn red(&self) -> String {
        format!("{RED}{self}{RESET}")
    }
    fn green(&self) -> String {
        format!("{GREEN}{self}{RESET}")
    }
    fn yellow(&self) -> String {
        format!("{YELLOW}{self}{RESET}")
    }
    fn blue(&self) -> String {
        format!("{BLUE}{self}{RESET}")
    }
    fn magenta(&self) -> String {
        format!("{MAGENTA}{self}{RESET}")
    }
    fn cyan(&self) -> String {
        format!("{CYAN}{self}{RESET}")
    }
    fn white(&self) -> String {
        format!("{WHITE}{self}{RESET}")
    }
    fn to_line_start(&self) -> String {
        format!("{CURSOR_TO_START}{self}{ERASE_LINE_TO_END}")
    }
}

// ==============================================================================
// GRAPHICS AND IMAGE RENDERING COLOR MODELS
// ==============================================================================

/// Zero-cost, stack-allocated raw float-based RGB color vector.
/// Designed for high-performance pixel-level shader computations.
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
pub struct ColorRGB {
    pub red: f32,
    pub green: f32,
    pub blue: f32,
}

impl ColorRGB {
    /// Creates a new ColorRGB vector.
    #[inline(always)]
    pub fn new(red: f32, green: f32, blue: f32) -> Self {
        Self { red, green, blue }
    }

    /// Performs linearly interpolated (LERP) blending with another ColorRGB.
    #[inline(always)]
    pub fn lerp(&self, other: &Self, t: f32) -> Self {
        let t_clamp = t.clamp(0.0, 1.0);
        Self {
            red: self.red * t_clamp + other.red * (1.0 - t_clamp),
            green: self.green * t_clamp + other.green * (1.0 - t_clamp),
            blue: self.blue * t_clamp + other.blue * (1.0 - t_clamp),
        }
    }

    /// Shifts RGB channels cyclically to produce a highly compatible secondary color.
    #[inline(always)]
    pub fn rotated(&self) -> Self {
        Self {
            red: self.green,
            green: self.blue,
            blue: self.red,
        }
    }

    /// Converts the struct's color channels into a raw float array [red, green, blue].
    #[inline(always)]
    pub const fn to_array(&self) -> [f32; 3] {
        [self.red, self.green, self.blue]
    }

    /// Returns the complementary (inverse) color of the current vector.
    #[inline(always)]
    pub fn complementary(&self) -> Self {
        Self {
            red: 1.0 - self.red,
            green: 1.0 - self.green,
            blue: 1.0 - self.blue,
        }
    }

    /// Returns the maximum component value among the Red, Green, and Blue channels.
    #[inline(always)]
    pub fn max_component(&self) -> f32 {
        self.red.max(self.green).max(self.blue)
    }

    /// Normalizes (saturates) the color components by dividing each channel by the maximum channel value,
    /// boosting visual intensity to its absolute physical peak (maximum component becomes exactly 1.0).
    #[inline(always)]
    pub fn saturate_components(&self) -> Self {
        let max = self.max_component();
        if max > 0.0 {
            Self {
                red: self.red / max,
                green: self.green / max,
                blue: self.blue / max,
            }
        } else {
            *self
        }
    }

    /// Scales all color channels by a single floating-point factor (e.g. brightness or lighting).
    #[inline(always)]
    pub fn scale(&self, factor: f32) -> Self {
        Self {
            red: self.red * factor,
            green: self.green * factor,
            blue: self.blue * factor,
        }
    }

    /// Clamps all color channels to the standard physical range [0.0, 1.0].
    #[inline(always)]
    pub fn clamp_bounds(&self) -> Self {
        Self {
            red: self.red.clamp(0.0, 1.0),
            green: self.green.clamp(0.0, 1.0),
            blue: self.blue.clamp(0.0, 1.0),
        }
    }
}

/// Represents a named neon color palette with float-based RGB channels.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct NeonColor {
    /// The raw mathematical RGB channels.
    pub color_rgb: ColorRGB,
    /// Descriptive name of the color palette.
    pub name: &'static str,
}

impl NeonColor {
    /// Delegate: Shifts RGB channels cyclically.
    #[inline(always)]
    pub fn rotated(&self) -> ColorRGB {
        self.color_rgb.rotated()
    }

    /// Delegate: Linearly interpolates (LERP) blending with another NeonColor.
    #[inline(always)]
    pub fn lerp(&self, other: &Self, t: f32) -> ColorRGB {
        self.color_rgb.lerp(&other.color_rgb, t)
    }

    /// Delegate: Converts the struct's color channels into a raw float array [red, green, blue].
    #[inline(always)]
    pub const fn to_array(&self) -> [f32; 3] {
        self.color_rgb.to_array()
    }
}

impl fmt::Display for NeonColor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "RGB [{:.2}, {:.2}, {:.2}] {}",
            self.color_rgb.red, self.color_rgb.green, self.color_rgb.blue, self.name
        )
    }
}

pub const NEON_PALETTES: [NeonColor; 20] = [
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.05,
            blue: 0.55,
        },
        name: "Neon Rose - Punchy Pink",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.0,
            green: 0.95,
            blue: 0.85,
        },
        name: "Vivid Turquoise - Electric Teal",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.95,
            green: 0.45,
            blue: 0.0,
        },
        name: "Tangerine Dream - Electric Orange",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.4,
            green: 0.9,
            blue: 0.0,
        },
        name: "Radioactive Lime - Acid Green",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.9,
            green: 0.9,
            blue: 0.0,
        },
        name: "Laser Lemon - Bright Yellow",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.05,
            green: 0.8,
            blue: 1.0,
        },
        name: "Blue Bolt - Intense Sky Cyan",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.85,
            green: 0.15,
            blue: 1.0,
        },
        name: "Radiant Orchid - Electric Violet",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.35,
            blue: 0.1,
        },
        name: "Fiery Coral - Sunset Flame",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.1,
            green: 0.95,
            blue: 0.4,
        },
        name: "Mint Spark - Spring Neon Green",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.0,
            blue: 0.35,
        },
        name: "Cherry Flare - Electric Ruby",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.75,
            green: 0.95,
            blue: 0.0,
        },
        name: "Vibrant Chartreuse - Pear Glow",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.0,
            green: 1.0,
            blue: 0.7,
        },
        name: "Aqua Glow - Supercharged seafoam",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.55,
            blue: 0.4,
        },
        name: "Warm Apricot - Peach Spark",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.9,
            green: 0.2,
            blue: 0.9,
        },
        name: "Cyberpunk Fuchsia - Hot Magenta",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.35,
            green: 1.0,
            blue: 0.5,
        },
        name: "Bright Emerald - Neon Clover",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.85,
            blue: 0.0,
        },
        name: "Solar Flare - Amber Gold",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.55,
            green: 0.35,
            blue: 1.0,
        },
        name: "Electric Amethyst - Bright Violet",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 1.0,
            green: 0.25,
            blue: 0.5,
        },
        name: "Flamingo Neon - Warm Pink",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.2,
            green: 0.9,
            blue: 0.9,
        },
        name: "Glacier Cyan - Ice Blue Glow",
    },
    NeonColor {
        color_rgb: ColorRGB {
            red: 0.85,
            green: 1.0,
            blue: 0.2,
        },
        name: "Radioactive Melon - Lemon Lime Flare",
    },
];