1use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
11
12use uuid::Uuid;
13
14static SEQUENTIAL_COUNTER: AtomicU64 = AtomicU64::new(0);
20
21#[inline]
23pub fn generate_uuid() -> String {
24 Uuid::new_v4().to_string()
25}
26
27#[inline]
29pub fn generate_short_uuid() -> String {
30 Uuid::new_v4().to_string()[..8].to_string()
31}
32
33#[inline]
35pub fn generate_sequential_id(prefix: &str) -> String {
36 let seq = SEQUENTIAL_COUNTER.fetch_add(1, Ordering::SeqCst);
37 format!("{}-{:08}", prefix, seq)
38}
39
40#[inline]
42pub fn generate_timestamp_id(prefix: &str) -> String {
43 let ts = SystemTime::now()
44 .duration_since(UNIX_EPOCH)
45 .unwrap_or_default()
46 .as_micros();
47 format!("{}-{}", prefix, ts)
48}
49
50#[inline]
56pub fn current_timestamp_ms() -> u64 {
57 SystemTime::now()
58 .duration_since(UNIX_EPOCH)
59 .unwrap_or_default()
60 .as_millis() as u64
61}
62
63#[inline]
65pub fn current_timestamp_us() -> u64 {
66 SystemTime::now()
67 .duration_since(UNIX_EPOCH)
68 .unwrap_or_default()
69 .as_micros() as u64
70}
71
72#[inline]
74pub fn current_timestamp_ns() -> u128 {
75 SystemTime::now()
76 .duration_since(UNIX_EPOCH)
77 .unwrap_or_default()
78 .as_nanos()
79}
80
81pub fn format_duration(duration: Duration) -> String {
83 let secs = duration.as_secs();
84 let millis = duration.subsec_millis();
85
86 if secs >= 3600 {
87 let hours = secs / 3600;
88 let mins = (secs % 3600) / 60;
89 format!("{}h {}m", hours, mins)
90 } else if secs >= 60 {
91 let mins = secs / 60;
92 let secs = secs % 60;
93 format!("{}m {}s", mins, secs)
94 } else if secs > 0 {
95 format!("{}.{:03}s", secs, millis)
96 } else if millis > 0 {
97 format!("{}ms", millis)
98 } else {
99 format!("{}µs", duration.as_micros())
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct Stopwatch {
106 start: Instant,
107 laps: Vec<(String, Duration)>,
108}
109
110impl Stopwatch {
111 pub fn start() -> Self {
113 Self {
114 start: Instant::now(),
115 laps: Vec::new(),
116 }
117 }
118
119 pub fn elapsed(&self) -> Duration {
121 self.start.elapsed()
122 }
123
124 pub fn lap(&mut self, name: impl Into<String>) {
126 self.laps.push((name.into(), self.elapsed()));
127 }
128
129 pub fn laps(&self) -> &[(String, Duration)] {
131 &self.laps
132 }
133
134 pub fn reset(&mut self) {
136 self.start = Instant::now();
137 self.laps.clear();
138 }
139
140 pub fn elapsed_ms(&self) -> u64 {
142 self.elapsed().as_millis() as u64
143 }
144
145 pub fn elapsed_us(&self) -> u64 {
147 self.elapsed().as_micros() as u64
148 }
149}
150
151impl Default for Stopwatch {
152 fn default() -> Self {
153 Self::start()
154 }
155}
156
157pub fn truncate_string(s: &str, max_len: usize) -> String {
163 if s.len() <= max_len {
164 s.to_string()
165 } else {
166 format!("{}...", &s[..max_len.saturating_sub(3)])
167 }
168}
169
170pub fn format_bytes(bytes: u64) -> String {
172 const KB: u64 = 1024;
173 const MB: u64 = KB * 1024;
174 const GB: u64 = MB * 1024;
175 const TB: u64 = GB * 1024;
176
177 if bytes >= TB {
178 format!("{:.2} TB", bytes as f64 / TB as f64)
179 } else if bytes >= GB {
180 format!("{:.2} GB", bytes as f64 / GB as f64)
181 } else if bytes >= MB {
182 format!("{:.2} MB", bytes as f64 / MB as f64)
183 } else if bytes >= KB {
184 format!("{:.2} KB", bytes as f64 / KB as f64)
185 } else {
186 format!("{} B", bytes)
187 }
188}
189
190pub fn sanitize_identifier(s: &str) -> String {
192 s.chars()
193 .map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' })
194 .collect()
195}
196
197#[macro_export]
217macro_rules! builder_setter {
218 ($field:ident, $type:ty) => {
219 pub fn $field(mut self, value: $type) -> Self {
220 self.$field = value;
221 self
222 }
223 };
224 ($field:ident, $type:ty, $doc:expr) => {
225 #[doc = $doc]
226 pub fn $field(mut self, value: $type) -> Self {
227 self.$field = value;
228 self
229 }
230 };
231}
232
233#[macro_export]
245macro_rules! builder_setter_into {
246 ($field:ident, $type:ty) => {
247 pub fn $field(mut self, value: impl Into<$type>) -> Self {
248 self.$field = value.into();
249 self
250 }
251 };
252 ($field:ident, $type:ty, $doc:expr) => {
253 #[doc = $doc]
254 pub fn $field(mut self, value: impl Into<$type>) -> Self {
255 self.$field = value.into();
256 self
257 }
258 };
259}
260
261#[macro_export]
271macro_rules! builder_setter_option {
272 ($field:ident, $type:ty) => {
273 pub fn $field(mut self, value: impl Into<$type>) -> Self {
274 self.$field = Some(value.into());
275 self
276 }
277 };
278 ($field:ident, $type:ty, $doc:expr) => {
279 #[doc = $doc]
280 pub fn $field(mut self, value: impl Into<$type>) -> Self {
281 self.$field = Some(value.into());
282 self
283 }
284 };
285}
286
287#[macro_export]
297macro_rules! builder_with {
298 ($field:ident, $type:ty) => {
299 paste::paste! {
300 pub fn [<with_ $field>](mut self, value: impl Into<$type>) -> Self {
301 self.$field = value.into();
302 self
303 }
304 }
305 };
306 ($field:ident, $type:ty, $doc:expr) => {
307 paste::paste! {
308 #[doc = $doc]
309 pub fn [<with_ $field>](mut self, value: impl Into<$type>) -> Self {
310 self.$field = value.into();
311 self
312 }
313 }
314 };
315}
316
317#[derive(Debug, Clone)]
323pub struct RetryConfig {
324 pub max_attempts: u32,
326 pub initial_delay: Duration,
328 pub max_delay: Duration,
330 pub multiplier: f64,
332}
333
334impl Default for RetryConfig {
335 fn default() -> Self {
336 Self {
337 max_attempts: 3,
338 initial_delay: Duration::from_millis(100),
339 max_delay: Duration::from_secs(10),
340 multiplier: 2.0,
341 }
342 }
343}
344
345impl RetryConfig {
346 pub fn new(max_attempts: u32) -> Self {
348 Self {
349 max_attempts,
350 ..Default::default()
351 }
352 }
353
354 pub fn with_initial_delay(mut self, delay: Duration) -> Self {
356 self.initial_delay = delay;
357 self
358 }
359
360 pub fn with_max_delay(mut self, delay: Duration) -> Self {
362 self.max_delay = delay;
363 self
364 }
365
366 pub fn with_multiplier(mut self, multiplier: f64) -> Self {
368 self.multiplier = multiplier;
369 self
370 }
371
372 pub fn delay_for_attempt(&self, attempt: u32) -> Duration {
374 if attempt == 0 {
375 return Duration::ZERO;
376 }
377
378 let delay_ms = self.initial_delay.as_millis() as f64
379 * self.multiplier.powi(attempt.saturating_sub(1) as i32);
380 let delay = Duration::from_millis(delay_ms.min(self.max_delay.as_millis() as f64) as u64);
381
382 delay.min(self.max_delay)
383 }
384}
385
386pub async fn retry_async<F, Fut, T, E>(config: RetryConfig, mut f: F) -> Result<T, E>
402where
403 F: FnMut() -> Fut,
404 Fut: std::future::Future<Output = Result<T, E>>,
405{
406 let mut last_error = None;
407
408 for attempt in 0..config.max_attempts {
409 match f().await {
410 Ok(value) => return Ok(value),
411 Err(e) => {
412 last_error = Some(e);
413
414 if attempt + 1 < config.max_attempts {
415 let delay = config.delay_for_attempt(attempt + 1);
416 tokio::time::sleep(delay).await;
417 }
418 }
419 }
420 }
421
422 Err(last_error.expect("Should have at least one error"))
423}
424
425#[derive(Debug)]
431pub struct RateLimiter {
432 capacity: u64,
434 tokens: AtomicU64,
436 refill_rate: f64,
438 last_refill: parking_lot::Mutex<Instant>,
440}
441
442impl RateLimiter {
443 pub fn new(capacity: u64, refill_rate: f64) -> Self {
445 Self {
446 capacity,
447 tokens: AtomicU64::new(capacity),
448 refill_rate,
449 last_refill: parking_lot::Mutex::new(Instant::now()),
450 }
451 }
452
453 pub fn try_acquire(&self) -> bool {
455 self.refill();
456
457 loop {
458 let current = self.tokens.load(Ordering::Acquire);
459 if current == 0 {
460 return false;
461 }
462
463 if self
464 .tokens
465 .compare_exchange_weak(current, current - 1, Ordering::AcqRel, Ordering::Acquire)
466 .is_ok()
467 {
468 return true;
469 }
470 }
471 }
472
473 pub fn try_acquire_n(&self, n: u64) -> bool {
475 self.refill();
476
477 loop {
478 let current = self.tokens.load(Ordering::Acquire);
479 if current < n {
480 return false;
481 }
482
483 if self
484 .tokens
485 .compare_exchange_weak(current, current - n, Ordering::AcqRel, Ordering::Acquire)
486 .is_ok()
487 {
488 return true;
489 }
490 }
491 }
492
493 pub fn available(&self) -> u64 {
495 self.refill();
496 self.tokens.load(Ordering::Acquire)
497 }
498
499 fn refill(&self) {
500 let mut last_refill = self.last_refill.lock();
501 let now = Instant::now();
502 let elapsed = now.duration_since(*last_refill).as_secs_f64();
503
504 if elapsed > 0.0 {
505 let new_tokens = (elapsed * self.refill_rate) as u64;
506 if new_tokens > 0 {
507 let current = self.tokens.load(Ordering::Acquire);
508 let new_value = (current + new_tokens).min(self.capacity);
509 self.tokens.store(new_value, Ordering::Release);
510 *last_refill = now;
511 }
512 }
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 #[test]
521 fn test_generate_uuid() {
522 let uuid1 = generate_uuid();
523 let uuid2 = generate_uuid();
524 assert_ne!(uuid1, uuid2);
525 assert_eq!(uuid1.len(), 36);
526 }
527
528 #[test]
529 fn test_generate_sequential_id() {
530 let id1 = generate_sequential_id("device");
531 let id2 = generate_sequential_id("device");
532 assert!(id1.starts_with("device-"));
533 assert!(id2.starts_with("device-"));
534 assert_ne!(id1, id2);
535 }
536
537 #[test]
538 fn test_format_duration() {
539 assert_eq!(format_duration(Duration::from_secs(3661)), "1h 1m");
540 assert_eq!(format_duration(Duration::from_secs(61)), "1m 1s");
541 assert_eq!(format_duration(Duration::from_millis(1500)), "1.500s");
542 assert_eq!(format_duration(Duration::from_millis(500)), "500ms");
543 assert_eq!(format_duration(Duration::from_micros(500)), "500µs");
544 }
545
546 #[test]
547 fn test_format_bytes() {
548 assert_eq!(format_bytes(500), "500 B");
549 assert_eq!(format_bytes(1024), "1.00 KB");
550 assert_eq!(format_bytes(1536), "1.50 KB");
551 assert_eq!(format_bytes(1048576), "1.00 MB");
552 assert_eq!(format_bytes(1073741824), "1.00 GB");
553 }
554
555 #[test]
556 fn test_truncate_string() {
557 assert_eq!(truncate_string("hello", 10), "hello");
558 assert_eq!(truncate_string("hello world", 8), "hello...");
559 }
560
561 #[test]
562 fn test_sanitize_identifier() {
563 assert_eq!(sanitize_identifier("hello world!"), "hello_world_");
564 assert_eq!(sanitize_identifier("device-001"), "device-001");
565 }
566
567 #[test]
568 fn test_stopwatch() {
569 let mut sw = Stopwatch::start();
570 std::thread::sleep(Duration::from_millis(10));
571 sw.lap("step1");
572 assert!(sw.elapsed().as_millis() >= 10);
573 assert_eq!(sw.laps().len(), 1);
574 }
575
576 #[test]
577 fn test_retry_config() {
578 let config = RetryConfig::new(3);
579 assert_eq!(config.delay_for_attempt(0), Duration::ZERO);
580 assert_eq!(config.delay_for_attempt(1), Duration::from_millis(100));
581 assert_eq!(config.delay_for_attempt(2), Duration::from_millis(200));
582 }
583
584 #[test]
585 fn test_rate_limiter() {
586 let limiter = RateLimiter::new(10, 100.0);
587 assert!(limiter.try_acquire());
588 assert_eq!(limiter.available(), 9);
589
590 for _ in 0..9 {
592 assert!(limiter.try_acquire());
593 }
594 assert!(!limiter.try_acquire());
595 }
596}