use crate::md_parser::md_parser_constants::{HEADING, LIST_SPACE_DISPLAY,
LIST_SPACE_DISPLAY_CHAR, SPACE, SPACE_CHAR};
use std::{borrow::Cow,
collections::HashMap,
sync::{LazyLock, Mutex}};
static SPACE_CACHE: LazyLock<HashMap<usize, String>> = LazyLock::new(|| {
let mut cache = HashMap::new();
for i in 0..=64 {
cache.insert(i, SPACE.repeat(i));
}
cache
});
static HORIZ_LINE_CACHE: LazyLock<HashMap<usize, String>> = LazyLock::new(|| {
let mut cache = HashMap::new();
for i in 0..=64 {
cache.insert(i, LIST_SPACE_DISPLAY.repeat(i));
}
cache
});
pub static DYNAMIC_CACHE: LazyLock<Mutex<HashMap<(char, usize), String>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
static HASH_CACHE: LazyLock<HashMap<usize, String>> = LazyLock::new(|| {
let mut cache = HashMap::new();
for i in 0..=10 {
cache.insert(i, HEADING.repeat(i));
}
cache
});
fn get_cached_repeated_string(
count: usize,
static_cache: &'static HashMap<usize, String>,
char_to_repeat: char,
str_to_repeat: &str,
) -> Cow<'static, str> {
if let Some(cached_str) = static_cache.get(&count) {
Cow::Borrowed(cached_str.as_str())
} else if count == 0 {
Cow::Borrowed("")
} else {
let mut cache = DYNAMIC_CACHE.lock().unwrap();
let repeated_str = cache
.entry((char_to_repeat, count))
.or_insert_with(|| str_to_repeat.repeat(count))
.clone();
Cow::Owned(repeated_str)
}
}
#[must_use]
pub fn get_spaces(count: usize) -> Cow<'static, str> {
get_cached_repeated_string(count, &SPACE_CACHE, SPACE_CHAR, SPACE)
}
#[must_use]
pub fn get_horiz_lines(count: usize) -> Cow<'static, str> {
get_cached_repeated_string(
count,
&HORIZ_LINE_CACHE,
LIST_SPACE_DISPLAY_CHAR,
LIST_SPACE_DISPLAY,
)
}
#[must_use]
pub fn get_hashes(count: usize) -> Cow<'static, str> {
get_cached_repeated_string(count, &HASH_CACHE, '#', HEADING)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cached_spaces() {
assert_eq!(get_spaces(0), "");
assert_eq!(get_spaces(1), SPACE);
assert_eq!(get_spaces(2), " ");
assert_eq!(get_spaces(4), " ");
assert_eq!(get_spaces(8), " ");
assert_eq!(get_spaces(16), " ");
assert_eq!(get_spaces(32), " ");
assert_eq!(get_spaces(64), SPACE.repeat(64));
}
#[test]
fn test_large_space_count() {
let spaces100 = get_spaces(100);
assert_eq!(spaces100.len(), 100);
assert!(spaces100.chars().all(|c| c == SPACE_CHAR));
}
#[test]
fn test_cache_consistency() {
let spaces1 = get_spaces(4);
let spaces2 = get_spaces(4);
assert_eq!(spaces1, spaces2);
}
#[test]
fn test_edge_cases() {
assert_eq!(get_spaces(0), "");
assert_eq!(get_spaces(1), SPACE);
assert_eq!(get_spaces(64), SPACE.repeat(64)); assert_eq!(get_spaces(65), SPACE.repeat(65)); }
#[test]
fn test_cached_horiz_lines() {
assert_eq!(get_horiz_lines(0), "");
assert_eq!(get_horiz_lines(1), LIST_SPACE_DISPLAY);
assert_eq!(get_horiz_lines(2), "──");
assert_eq!(get_horiz_lines(4), "────");
assert_eq!(get_horiz_lines(8), "────────");
assert_eq!(get_horiz_lines(16), "────────────────");
assert_eq!(get_horiz_lines(32), "────────────────────────────────");
assert_eq!(get_horiz_lines(64), LIST_SPACE_DISPLAY.repeat(64));
}
#[test]
fn test_large_horiz_line_count() {
let horiz_lines100 = get_horiz_lines(100);
assert_eq!(horiz_lines100.chars().count(), 100);
assert!(horiz_lines100.chars().all(|c| c == LIST_SPACE_DISPLAY_CHAR));
}
#[test]
fn test_horiz_line_cache_consistency() {
let horiz_lines1 = get_horiz_lines(4);
let horiz_lines2 = get_horiz_lines(4);
assert_eq!(horiz_lines1, horiz_lines2);
}
#[test]
fn test_cached_hashes() {
assert_eq!(get_hashes(0), "");
assert_eq!(get_hashes(1), "#");
assert_eq!(get_hashes(2), "##");
assert_eq!(get_hashes(3), "###");
assert_eq!(get_hashes(4), "####");
assert_eq!(get_hashes(5), "#####");
assert_eq!(get_hashes(6), "######");
assert_eq!(get_hashes(10), "##########");
}
#[test]
fn test_large_hash_count() {
let hashes15 = get_hashes(15);
assert_eq!(hashes15.chars().count(), 15);
assert!(hashes15.chars().all(|c| c == '#'));
}
#[test]
fn test_hash_cache_consistency() {
let hashes1 = get_hashes(3);
let hashes2 = get_hashes(3);
assert_eq!(hashes1, hashes2);
}
#[test]
fn test_dynamic_cache_persistence() {
let spaces_100_first = get_spaces(100);
let horiz_lines_100_first = get_horiz_lines(100);
let hashes_20_first = get_hashes(20);
let spaces_100_second = get_spaces(100);
let horiz_lines_100_second = get_horiz_lines(100);
let hashes_20_second = get_hashes(20);
assert_eq!(spaces_100_first, spaces_100_second);
assert_eq!(horiz_lines_100_first, horiz_lines_100_second);
assert_eq!(hashes_20_first, hashes_20_second);
let cache = DYNAMIC_CACHE.lock().unwrap();
assert!(cache.contains_key(&(SPACE_CHAR, 100)));
assert!(cache.contains_key(&(LIST_SPACE_DISPLAY_CHAR, 100)));
assert!(cache.contains_key(&('#', 20)));
}
#[test]
fn test_dynamic_cache_different_counts() {
let spaces_70 = get_spaces(70);
let spaces_80 = get_spaces(80);
let spaces_90 = get_spaces(90);
assert_eq!(spaces_70.len(), 70);
assert_eq!(spaces_80.len(), 80);
assert_eq!(spaces_90.len(), 90);
let cache = DYNAMIC_CACHE.lock().unwrap();
assert!(cache.contains_key(&(SPACE_CHAR, 70)));
assert!(cache.contains_key(&(SPACE_CHAR, 80)));
assert!(cache.contains_key(&(SPACE_CHAR, 90)));
}
#[test]
fn test_thread_safety() {
use std::thread;
let handles: Vec<_> = (0..10)
.map(|i| {
thread::spawn(move || {
let base = 100 + i * 10;
let spaces = get_spaces(base);
let horiz_lines = get_horiz_lines(base);
let hashes = get_hashes(base);
assert_eq!(spaces.len(), base);
assert_eq!(horiz_lines.chars().count(), base);
assert_eq!(hashes.chars().count(), base);
})
})
.collect();
for handle in handles {
handle.join().expect("Thread panicked");
}
let cache = DYNAMIC_CACHE.lock().unwrap();
for i in 0..10 {
let base = 100 + i * 10;
assert!(cache.contains_key(&(SPACE_CHAR, base)));
assert!(cache.contains_key(&(LIST_SPACE_DISPLAY_CHAR, base)));
assert!(cache.contains_key(&('#', base)));
}
}
#[test]
fn test_get_cached_repeated_string_directly() {
static TEST_CACHE: LazyLock<HashMap<usize, String>> = LazyLock::new(|| {
let mut cache = HashMap::new();
cache.insert(3, "xxx".to_string());
cache
});
let result = get_cached_repeated_string(3, &TEST_CACHE, 'x', "x");
assert_eq!(result, "xxx");
assert!(matches!(result, Cow::Borrowed(_)));
let result = get_cached_repeated_string(0, &TEST_CACHE, 'x', "x");
assert_eq!(result, "");
assert!(matches!(result, Cow::Borrowed(_)));
let result = get_cached_repeated_string(5, &TEST_CACHE, 'y', "y");
assert_eq!(result, "yyyyy");
assert!(matches!(result, Cow::Owned(_)));
let cache = DYNAMIC_CACHE.lock().unwrap();
assert!(cache.contains_key(&('y', 5)));
}
}