use std::collections::HashMap;
use std::time::Duration;
pub fn retry_with_backoff<F, T, E>(mut op: F, max_retries: u32) -> Result<T, E>
where
F: FnMut() -> Result<T, E>,
{
let mut delay = Duration::from_millis(100);
for attempt in 0..max_retries {
match op() {
Ok(v) => return Ok(v),
Err(e) if attempt == max_retries - 1 => return Err(e),
Err(_) => {
std::thread::sleep(delay);
delay *= 2;
}
}
}
unreachable!()
}
pub fn validate_email(email: &str) -> bool {
let parts: Vec<&str> = email.split('@').collect();
if parts.len() != 2 {
return false;
}
!parts[0].is_empty() && parts[1].contains('.')
}
pub fn parse_json_config(path: &str) -> Result<HashMap<String, String>, std::io::Error> {
let content = std::fs::read_to_string(path)?;
let mut config = HashMap::new();
for line in content.lines() {
if let Some((key, value)) = line.split_once(':') {
config.insert(key.trim().to_string(), value.trim().to_string());
}
}
Ok(config)
}
pub fn hash_sha256(data: &[u8]) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
format!("{:x}", hasher.finish())
}
pub fn format_currency(amount: f64) -> String {
let formatted = format!("{:.2}", amount);
let parts: Vec<&str> = formatted.split('.').collect();
let int_part = parts[0];
let dec_part = parts.get(1).unwrap_or(&"00");
let with_commas: String = int_part
.chars()
.rev()
.enumerate()
.map(|(i, c)| if i > 0 && i % 3 == 0 { format!(",{}", c) } else { c.to_string() })
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
format!("${}.{}", with_commas, dec_part)
}
pub fn http_post_json(url: &str, body: &str) -> Result<String, std::io::Error> {
Ok(format!("POST {} with body: {}", url, body))
}
pub fn read_file_utf8(path: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(path)
}
pub fn write_file_atomic(path: &str, content: &str) -> Result<(), std::io::Error> {
let temp_path = format!("{}.tmp", path);
std::fs::write(&temp_path, content)?;
std::fs::rename(&temp_path, path)
}
pub fn calculate_mean(numbers: &[f64]) -> f64 {
if numbers.is_empty() {
return 0.0;
}
numbers.iter().sum::<f64>() / numbers.len() as f64
}
pub fn find_maximum(numbers: &[i32]) -> Option<i32> {
numbers.iter().copied().max()
}
pub fn camel_to_snake(s: &str) -> String {
let mut result = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() && i > 0 {
result.push('_');
}
result.push(c.to_lowercase().next().unwrap());
}
result
}
pub fn truncate_string(s: &str, max_len: usize) -> String {
if s.len() <= max_len {
s.to_string()
} else {
format!("{}...", &s[..max_len - 3])
}
}
pub fn is_valid_uuid(s: &str) -> bool {
let parts: Vec<&str> = s.split('-').collect();
if parts.len() != 5 {
return false;
}
let lengths = [8, 4, 4, 4, 12];
parts.iter().zip(lengths.iter()).all(|(p, &len)| {
p.len() == len && p.chars().all(|c| c.is_ascii_hexdigit())
})
}
pub fn generate_random_id(length: usize) -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
format!("{:x}", seed)[..length.min(16)].to_string()
}
pub fn compress_rle(data: &[u8]) -> Vec<u8> {
let mut result = Vec::new();
let mut i = 0;
while i < data.len() {
let byte = data[i];
let mut count = 1u8;
while i + count as usize < data.len() && data[i + count as usize] == byte && count < 255 {
count += 1;
}
result.push(count);
result.push(byte);
i += count as usize;
}
result
}
pub fn parse_cli_args(args: &[String]) -> HashMap<String, String> {
let mut result = HashMap::new();
let mut i = 0;
while i < args.len() {
if args[i].starts_with("--") {
let key = args[i][2..].to_string();
let value = args.get(i + 1).cloned().unwrap_or_default();
result.insert(key, value);
i += 2;
} else {
i += 1;
}
}
result
}
pub fn quicksort<T: Ord + Clone>(arr: &mut [T]) {
if arr.len() <= 1 {
return;
}
let pivot = arr.len() / 2;
let pivot_val = arr[pivot].clone();
let (left, right): (Vec<_>, Vec<_>) = arr.iter().cloned().partition(|x| x < &pivot_val);
let mut sorted: Vec<_> = left;
sorted.extend(right);
arr.clone_from_slice(&sorted);
}
pub struct Debouncer {
last_call: std::time::Instant,
delay: Duration,
}
impl Debouncer {
pub fn new(delay_ms: u64) -> Self {
Self {
last_call: std::time::Instant::now(),
delay: Duration::from_millis(delay_ms),
}
}
pub fn should_execute(&mut self) -> bool {
let now = std::time::Instant::now();
if now.duration_since(self.last_call) >= self.delay {
self.last_call = now;
true
} else {
false
}
}
}
pub struct Memoizer<K, V> {
cache: HashMap<K, V>,
}
impl<K: std::hash::Hash + Eq, V: Clone> Memoizer<K, V> {
pub fn new() -> Self {
Self { cache: HashMap::new() }
}
pub fn get_or_compute<F>(&mut self, key: K, compute: F) -> V
where
F: FnOnce() -> V,
{
self.cache.entry(key).or_insert_with(compute).clone()
}
}