Skip to main content

oramacore_client/
utils.rs

1//! Utility functions for the Orama client.
2
3use std::time::{Duration, Instant};
4
5use uuid::Uuid;
6
7/// Create a random string of specified length
8pub fn create_random_string(length: usize) -> String {
9    use uuid::Uuid;
10
11    // Generate multiple UUIDs if needed to reach the desired length
12    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    // Truncate to exact length
19    result.truncate(length);
20    result
21}
22
23/// Format duration in milliseconds to human readable string
24pub 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
37/// Get current timestamp in milliseconds
38pub 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
45/// Generate a new UUID v4 as string
46pub fn generate_uuid() -> String {
47    Uuid::new_v4().to_string()
48}
49
50/// Safely parse JSON with LLM response fixing
51pub 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    // First try direct parsing
56    match serde_json::from_str::<T>(data) {
57        Ok(parsed) => Ok(parsed),
58        Err(_) => {
59            // If direct parsing fails, try to fix the JSON with llm_json
60            let fixed_json = llm_json::repair_json(data, &Default::default())
61                .map_err(|e| format!("Failed to fix malformed JSON: {e}"))?;
62
63            // Try parsing the fixed JSON
64            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
70/// Parse potentially malformed JSON from AI responses
71pub 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
78/// Throttle function execution
79pub struct Throttle {
80    last_called: std::sync::Mutex<Option<Instant>>,
81    limit: Duration,
82}
83
84impl Throttle {
85    /// Create a new throttle with the specified limit in milliseconds
86    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    /// Execute function if enough time has passed since last call
94    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
111/// Debounce function execution
112pub struct Debounce {
113    timer: std::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
114    delay: Duration,
115}
116
117impl Debounce {
118    /// Create a new debounce with the specified delay in milliseconds
119    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    /// Execute function after delay, cancelling any previous pending execution
127    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        // Cancel previous timer if exists
135        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}