oramacore_client/
utils.rs1use std::time::{Duration, Instant};
4
5use uuid::Uuid;
6
7pub fn create_random_string(length: usize) -> String {
9 use uuid::Uuid;
10
11 let mut result = String::new();
13 while result.len() < length {
14 let uuid_str = Uuid::new_v4().to_string().replace('-', "");
15 result.push_str(&uuid_str);
16 }
17
18 result.truncate(length);
20 result
21}
22
23pub fn format_duration(duration_ms: u64) -> String {
25 if duration_ms < 1000 {
26 format!("{duration_ms}ms")
27 } else {
28 let seconds = duration_ms as f64 / 1000.0;
29 if seconds.fract() == 0.0 {
30 format!("{}s", seconds as u64)
31 } else {
32 format!("{seconds:.1}s")
33 }
34 }
35}
36
37pub fn current_time_millis() -> u64 {
39 std::time::SystemTime::now()
40 .duration_since(std::time::UNIX_EPOCH)
41 .unwrap_or_default()
42 .as_millis() as u64
43}
44
45pub fn generate_uuid() -> String {
47 Uuid::new_v4().to_string()
48}
49
50pub fn safe_json_parse<T>(data: &str) -> Result<T, Box<dyn std::error::Error + Send + Sync>>
52where
53 T: for<'de> serde::Deserialize<'de>,
54{
55 match serde_json::from_str::<T>(data) {
57 Ok(parsed) => Ok(parsed),
58 Err(_) => {
59 let fixed_json = llm_json::repair_json(data, &Default::default())
61 .map_err(|e| format!("Failed to fix malformed JSON: {e}"))?;
62
63 serde_json::from_str::<T>(&fixed_json)
65 .map_err(|e| format!("Failed to parse even after JSON fixing: {e}").into())
66 }
67 }
68}
69
70pub fn parse_ai_response<T>(data: &str) -> Result<T, Box<dyn std::error::Error + Send + Sync>>
72where
73 T: for<'de> serde::Deserialize<'de>,
74{
75 safe_json_parse(data)
76}
77
78pub struct Throttle {
80 last_called: std::sync::Mutex<Option<Instant>>,
81 limit: Duration,
82}
83
84impl Throttle {
85 pub fn new(limit_ms: u64) -> Self {
87 Self {
88 last_called: std::sync::Mutex::new(None),
89 limit: Duration::from_millis(limit_ms),
90 }
91 }
92
93 pub fn execute<F, R>(&self, f: F) -> Option<R>
95 where
96 F: FnOnce() -> R,
97 {
98 let mut last_called = self.last_called.lock().unwrap();
99 let now = Instant::now();
100
101 match *last_called {
102 Some(last) if now.duration_since(last) < self.limit => None,
103 _ => {
104 *last_called = Some(now);
105 Some(f())
106 }
107 }
108 }
109}
110
111pub struct Debounce {
113 timer: std::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
114 delay: Duration,
115}
116
117impl Debounce {
118 pub fn new(delay_ms: u64) -> Self {
120 Self {
121 timer: std::sync::Mutex::new(None),
122 delay: Duration::from_millis(delay_ms),
123 }
124 }
125
126 pub async fn execute<F, Fut>(&self, f: F)
128 where
129 F: FnOnce() -> Fut + Send + 'static,
130 Fut: std::future::Future<Output = ()> + Send + 'static,
131 {
132 let mut timer = self.timer.lock().unwrap();
133
134 if let Some(handle) = timer.take() {
136 handle.abort();
137 }
138
139 let delay = self.delay;
140 *timer = Some(tokio::spawn(async move {
141 tokio::time::sleep(delay).await;
142 f().await;
143 }));
144 }
145}