use std::fmt::Display;
use colored::Colorize;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Language {
pub name: String,
pub colour: Option<(u8, u8, u8)>,
}
impl Display for Language {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.colour {
Some((r, g, b)) => write!(f, "{}", self.name.truecolor(r, g, b)),
None => write!(f, "{}", self.name),
}
}
}
impl From<hyperpolyglot::Language> for Language {
fn from(value: hyperpolyglot::Language) -> Self {
let name = value.name.to_string();
let colour = value.color.and_then(|s| {
let s = s.strip_prefix('#')?;
Some((
u8::from_str_radix(&s[0..2], 16).ok()?,
u8::from_str_radix(&s[2..4], 16).ok()?,
u8::from_str_radix(&s[4..6], 16).ok()?,
))
});
Language { name, colour }
}
}
impl From<&str> for Language {
fn from(value: &str) -> Self {
let mut name = value;
let colour: Option<(u8, u8, u8)> = (|| {
let s = value.strip_prefix("\x1b[38;2;")?;
let (r, s) = s.split_once(';')?;
let (g, s) = s.split_once(';')?;
let (b, s) = s.split_once('m')?;
#[rustfmt::skip]
let col = (
r.parse().ok()?,
g.parse().ok()?,
b.parse().ok()?
);
name = s.strip_suffix("\x1b[0m")?;
Some(col)
})();
let name = name.to_string();
Language { name, colour }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_language_from_ansi_string() {
let lang = Language::from("\x1b[38;2;255;100;0mRust\x1b[0m");
assert_eq!(lang.name, "Rust");
assert_eq!(lang.colour, Some((255, 100, 0)));
}
#[test]
fn test_language_from_plain_string() {
let lang = Language::from("Rust");
assert_eq!(lang.name, "Rust");
assert_eq!(lang.colour, None);
}
#[test]
fn test_language_from_malformed_ansi_string() {
let lang = Language::from("\x1b[38;2;999;0;0mRust\x1b[0m");
assert_eq!(lang.name, "\x1b[38;2;999;0;0mRust\x1b[0m");
assert_eq!(lang.colour, None);
}
}