const SCRAMBLE_GLYPHS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@$%&*!?/\\+=~<>";
pub fn typewriter_text(text: &str, progress: f64, show_cursor: bool, cursor_char: char) -> String {
let progress = progress.clamp(0.0, 1.0);
let chars: Vec<char> = text.chars().collect();
let total_chars = chars.len();
if total_chars == 0 {
return String::new();
}
let visible_count = ((total_chars as f64) * progress).round() as usize;
let visible_count = visible_count.min(total_chars);
let mut result: String = chars[..visible_count].iter().collect();
if show_cursor && progress < 1.0 {
result.push(cursor_char);
}
result
}
pub fn typewriter_words(text: &str, progress: f64) -> String {
let progress = progress.clamp(0.0, 1.0);
let words: Vec<&str> = text.split_whitespace().collect();
let total_words = words.len();
if total_words == 0 {
return String::new();
}
let visible_count = ((total_words as f64) * progress).round() as usize;
let visible_count = visible_count.min(total_words);
words[..visible_count].join(" ")
}
pub fn scramble_text(target_text: &str, progress: f64, seed: u64) -> String {
let progress = progress.clamp(0.0, 1.0);
if progress >= 1.0 {
return target_text.to_string();
}
let chars: Vec<char> = target_text.chars().collect();
let total_chars = chars.len();
if total_chars == 0 {
return String::new();
}
let decoded_count = ((total_chars as f64) * progress).floor() as usize;
let mut output = String::with_capacity(target_text.len());
for (i, &ch) in chars.iter().enumerate() {
if i < decoded_count || ch.is_whitespace() {
output.push(ch);
} else {
let step = (progress * 50.0) as u64;
let hash = seed
.wrapping_mul(6364136223846793005)
.wrapping_add((i as u64).wrapping_mul(1442695040888963407))
.wrapping_add(step.wrapping_mul(1013904223));
let glyph_idx = (hash as usize) % SCRAMBLE_GLYPHS.len();
output.push(SCRAMBLE_GLYPHS[glyph_idx] as char);
}
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_typewriter_progress() {
let text = "Hello";
assert_eq!(typewriter_text(text, 0.0, false, '|'), "");
assert_eq!(typewriter_text(text, 0.0, true, '|'), "|");
assert_eq!(typewriter_text(text, 0.4, false, '|'), "He");
assert_eq!(typewriter_text(text, 1.0, true, '|'), "Hello");
}
#[test]
fn test_typewriter_words() {
let text = "The quick brown fox";
assert_eq!(typewriter_words(text, 0.0), "");
assert_eq!(typewriter_words(text, 0.25), "The");
assert_eq!(typewriter_words(text, 0.5), "The quick");
assert_eq!(typewriter_words(text, 0.75), "The quick brown");
assert_eq!(typewriter_words(text, 1.0), "The quick brown fox");
}
#[test]
fn test_scramble_text() {
let text = "HELLO WORLD";
let half = scramble_text(text, 0.5, 42);
assert_eq!(half.chars().count(), text.chars().count());
assert_eq!(half.chars().nth(5).unwrap(), ' ');
assert!(half.starts_with("HELLO"));
let full = scramble_text(text, 1.0, 42);
assert_eq!(full, text);
}
}