diff --git a/src/euph.rs b/src/euph.rs index b2336a2..30d39ee 100644 --- a/src/euph.rs +++ b/src/euph.rs @@ -1,6 +1,8 @@ pub mod api; mod conn; mod room; +mod util; pub use conn::{Joined, Joining, Status}; pub use room::Room; +pub use util::{hue, nick_color, nick_style}; diff --git a/src/euph/util.rs b/src/euph/util.rs new file mode 100644 index 0000000..0c50700 --- /dev/null +++ b/src/euph/util.rs @@ -0,0 +1,48 @@ +use crossterm::style::{Color, ContentStyle, Stylize}; +use palette::{FromColor, Hsl, RgbHue, Srgb}; + +fn normalize(text: &str) -> String { + // TODO Remove emoji names? + text.chars() + .filter(|&c| c.is_ascii_alphanumeric() || c == '_' || c == '-') + .map(|c| c.to_ascii_lowercase()) + .collect() +} + +/// A re-implementation of [euphoria's nick hue hashing algorithm][0]. +/// +/// [0]: https://github.com/euphoria-io/heim/blob/master/client/lib/hueHash.js +fn hue_hash(text: &str, offset: i64) -> u8 { + let mut val = 0_i32; + for char in text.chars() { + let char_val = (char as i32).wrapping_mul(439) % 256; + val = val.wrapping_mul(33).wrapping_add(char_val); + } + + let val: i64 = val as i64 + 2_i64.pow(31); + ((val + offset) % 255) as u8 +} + +const GREENIE_OFFSET: i64 = 148 - 192; // 148 - hue_hash("greenie", 0) + +pub fn hue(text: &str) -> u8 { + let normalized = normalize(text); + if normalized.is_empty() { + hue_hash(text, GREENIE_OFFSET) + } else { + hue_hash(&normalized, GREENIE_OFFSET) + } +} + +pub fn nick_color(nick: &str) -> (u8, u8, u8) { + let hue = RgbHue::from(hue(nick) as f32); + let color = Hsl::new(hue, 1.0, 0.72); + Srgb::from_color(color) + .into_format::() + .into_components() +} + +pub fn nick_style(nick: &str) -> ContentStyle { + let (r, g, b) = nick_color(nick); + ContentStyle::default().bold().with(Color::Rgb { r, g, b }) +}