use crate::error::ImageError;
use std::borrow::Cow;
pub const SMOOTH_GRADIENT: &str =
" .'`^\",:;Il!i><~+_-?][}{1)(|\\/jtfrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
pub const ASCII_RAMP: &str = " .:-=+*#%@";
pub const BLOCKS_RAMP: &str = " ░▒▓█";
pub const QUANTIZED_RAMP: &str =
" .....:::::-----+++++*****=====%%%%%#####88888WWWWWMMMMM$$$$$@@@@@";
pub const INVERTED_SMOOTH: &str =
"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrftj\\/|()1{}[]?-_+~<>i!lI;:,\"^`'. ";
pub const HIGH_VARIETY: &str =
" .'^\",:;~i|_ilI!><-+?]/\\|()1{}[]rcvunxzjftLCJUYXZO0Qoahkbdpqwm*WMB8&%$#N@";
pub const SEVEN_TIER: &str =
" ..........::::::::::----------**********##########@@@@@@@@@@";
pub const EMOJI_BRIGHTNESS: &str = "⬛⬛🌑🖤💣🌚🏴🕷️🦇♟️♠️♣️◼️◾▪️🔲🔳🟣🟦🟫🟥🟧🟩🟨⚙️🔒📌📍🎯👁️🔮🧿🌕🌝☀️⭐🌟✨💫⚡🔥💥🌼🌻💡🕯️❕❓❗◽▫️⚪🥛☁️❄️🧊🏳️💭💬🗯️🕊️📄📜✉️🏷️◽▫️⚪⬜⬜";
pub const CELESTIAL: &str = "⬛⬛⬛⬛⬛⬛⬛🌑🌑🌑🌑🌑🌑🌑🌒🌒🌒🌒🌒🌒🌒🌓🌓🌓🌓🌓🌓🌓🌔🌔🌔🌔🌔🌔🌔🌕🌕🌕🌕🌕🌕🌕🌝☀️⭐🌟✨💫💡🕯️⚪⚪⚪⚪⚪⚪⚪☁️☁️☁️❄️❄️🕊️🕊️📄📄⬜⬜⬜⬜";
#[derive(Debug)]
pub enum Ramp {
Ascii(&'static str),
Blocks(&'static str),
SmoothGradient(&'static str),
QuantizedRamp(&'static str),
InvertedSmoothGradient(&'static str),
HighVariety(&'static str),
SevenTier(&'static str),
EmojiBrightness(&'static str),
Celestial(&'static str),
CustomRamp(Cow<'static, str>),
}
impl Ramp {
pub fn ascii() -> Self {
Self::Ascii(ASCII_RAMP)
}
pub fn blocks() -> Self {
Self::Blocks(BLOCKS_RAMP)
}
pub fn smooth_gradient() -> Self {
Self::SmoothGradient(SMOOTH_GRADIENT)
}
pub fn quantized() -> Self {
Self::QuantizedRamp(QUANTIZED_RAMP)
}
pub fn inverted_smooth() -> Self {
Self::InvertedSmoothGradient(INVERTED_SMOOTH)
}
pub fn high_variety() -> Self {
Self::HighVariety(HIGH_VARIETY)
}
pub fn seven_tier() -> Self {
Self::SevenTier(SEVEN_TIER)
}
pub fn emoji_brightness() -> Self {
Self::EmojiBrightness(EMOJI_BRIGHTNESS)
}
pub fn celestial() -> Self {
Self::Celestial(CELESTIAL)
}
pub fn new_custom(s: impl Into<Cow<'static, str>>) -> Result<Self, ImageError> {
let cow = s.into();
validate_str(&cow)?;
let inverted: String = cow.chars().rev().collect();
validate_str(&inverted)?;
Ok(Self::CustomRamp(cow))
}
#[inline]
pub fn as_str(&self) -> &str {
match self {
Self::Ascii(s)
| Self::Blocks(s)
| Self::SmoothGradient(s)
| Self::QuantizedRamp(s)
| Self::InvertedSmoothGradient(s)
| Self::HighVariety(s)
| Self::SevenTier(s)
| Self::EmojiBrightness(s)
| Self::Celestial(s) => s,
Self::CustomRamp(s) => s.as_ref(),
}
}
pub fn invert(&self) -> Result<Self, ImageError> {
let reversed: String = self.as_str().chars().rev().collect();
validate_str(&reversed)?;
Ok(Self::CustomRamp(Cow::Owned(reversed)))
}
}
#[derive(Debug)]
pub enum RampType {
Dark(Ramp),
Light(Ramp),
}
impl RampType {
#[inline]
pub fn inner(&self) -> &Ramp {
match self {
Self::Dark(ramp) | Self::Light(ramp) => ramp,
}
}
}
fn validate_str(s: &str) -> Result<(), ImageError> {
if s.is_empty() {
return Err(ImageError::EmptyRamp);
}
if s.bytes().any(|b| b == b'\n' || b == b'\r' || b == b'\0') {
return Err(ImageError::InvalidRamp(
"Ramp contains invalid control characters".into(),
));
}
if let Some(first_char) = s.chars().next() {
if ('\u{FE00}'..='\u{FE0F}').contains(&first_char) {
return Err(ImageError::InvalidRamp(
"Inverted ramp produces orphan variation selector".into(),
));
}
}
Ok(())
}
#[inline]
pub fn validate_ramp(ramp: Option<&Ramp>) -> Result<(), ImageError> {
let Some(ramp) = ramp else {
return Err(ImageError::EmptyRamp);
};
validate_str(ramp.as_str())
}