use iascii::error::ImageError;
use iascii::ramp::{Ramp, RampType};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Script {
Latin,
Block,
Braille,
Cyrillic,
Greek,
Cjk,
Devanagari,
Hebrew,
Arabic,
}
impl Script {
fn chars(self) -> &'static str {
match self {
Script::Latin => {
" .'`^\",:;Il!i><~+_-?][}{1)(|\\/jtfrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"
}
Script::Block => " ░▒▓█",
Script::Braille => braille_ramp(),
Script::Cyrillic => " .,\u{0301}\u{0300}ıі´гвсэоауеьъиклнптмчшщДЖЮ",
Script::Greek => " .,'ιτ\u{03B9}ρνςολυπχθδφκγζξψβμω",
Script::Cjk => " .一二人入八十丁力乃三口土工才寸下大丈山川已己工日月木水火田",
Script::Devanagari => " .़्ािीुूॐअइउएकचटतनपमरलवसह",
Script::Hebrew => " .יוזןטבכלמנסעפצקרשתךםףץ",
Script::Arabic => " .ءابتثجحخدذرسصطعفقكلمنهوي",
}
}
pub fn ramp(self) -> Result<Ramp, ImageError> {
Ramp::new_custom(self.chars())
}
pub fn ramp_type(self, dark: bool) -> Result<RampType, ImageError> {
let ramp = self.ramp()?;
Ok(if dark {
RampType::Dark(ramp)
} else {
RampType::Light(ramp)
})
}
}
fn braille_ramp() -> &'static str {
use std::sync::OnceLock;
static RAMP: OnceLock<&'static str> = OnceLock::new();
RAMP.get_or_init(|| {
let mut cells: Vec<u8> = (0u8..=255).collect();
cells.sort_by_key(|c| c.count_ones());
let s: String = cells
.into_iter()
.map(|c| char::from_u32(0x2800 + c as u32).unwrap())
.collect();
Box::leak(s.into_boxed_str())
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_script_builds_a_valid_ramp() {
for script in [
Script::Latin,
Script::Block,
Script::Braille,
Script::Cyrillic,
Script::Greek,
Script::Cjk,
Script::Devanagari,
Script::Hebrew,
Script::Arabic,
] {
let ramp = script.ramp();
assert!(ramp.is_ok(), "{script:?} ramp should validate: {ramp:?}");
}
}
#[test]
fn braille_ramp_has_256_entries_ordered_by_dot_count() {
let s = Script::Braille.chars();
let chars: Vec<char> = s.chars().collect();
assert_eq!(chars.len(), 256);
let counts: Vec<u32> = chars
.iter()
.map(|c| (*c as u32 - 0x2800).count_ones())
.collect();
assert!(counts.windows(2).all(|w| w[0] <= w[1]));
}
#[test]
fn dark_and_light_variants_wrap_correctly() {
assert!(matches!(
Script::Block.ramp_type(true).unwrap(),
RampType::Dark(_)
));
assert!(matches!(
Script::Block.ramp_type(false).unwrap(),
RampType::Light(_)
));
}
}