mockforge_data/
token_resolver.rs1use crate::{
7 faker::EnhancedFaker,
8 rag::{RagConfig, RagEngine},
9};
10use mockforge_core::{Error, Result};
11use rand::Rng;
12use regex::Regex;
13use serde_json::{json, Value};
14use std::collections::HashMap;
15use std::sync::Arc;
16use tokio::sync::RwLock;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum TokenType {
21 Random(String),
23 Faker(String),
25 Ai(String),
27}
28
29pub struct TokenResolver {
31 faker: Arc<RwLock<EnhancedFaker>>,
33 rag_engine: Option<Arc<RwLock<RagEngine>>>,
35 cache: Arc<RwLock<HashMap<String, Value>>>,
37}
38
39impl TokenResolver {
40 pub fn new() -> Self {
42 Self {
43 faker: Arc::new(RwLock::new(EnhancedFaker::new())),
44 rag_engine: None,
45 cache: Arc::new(RwLock::new(HashMap::new())),
46 }
47 }
48
49 pub fn with_rag(rag_config: RagConfig) -> Self {
51 Self {
52 faker: Arc::new(RwLock::new(EnhancedFaker::new())),
53 rag_engine: Some(Arc::new(RwLock::new(RagEngine::new(rag_config)))),
54 cache: Arc::new(RwLock::new(HashMap::new())),
55 }
56 }
57
58 pub async fn resolve(&self, value: &Value) -> Result<Value> {
60 match value {
61 Value::String(s) => self.resolve_string(s).await,
62 Value::Array(arr) => {
63 let mut resolved = Vec::new();
64 for item in arr {
65 resolved.push(Box::pin(self.resolve(item)).await?);
66 }
67 Ok(Value::Array(resolved))
68 }
69 Value::Object(obj) => {
70 let mut resolved = serde_json::Map::new();
71 for (key, val) in obj {
72 resolved.insert(key.clone(), Box::pin(self.resolve(val)).await?);
73 }
74 Ok(Value::Object(resolved))
75 }
76 _ => Ok(value.clone()),
77 }
78 }
79
80 async fn resolve_string(&self, s: &str) -> Result<Value> {
82 if let Some(token) = self.parse_token(s) {
84 return self.resolve_token(&token).await;
85 }
86
87 let token_regex =
89 Regex::new(r"\$(?:random|faker|ai)(?:\.[a-zA-Z_][a-zA-Z0-9_]*|\([^)]*\))?")
90 .map_err(|e| Error::generic(format!("Regex error: {}", e)))?;
91
92 if token_regex.is_match(s) {
93 let mut result = s.to_string();
94 for cap in token_regex.captures_iter(s) {
95 if let Some(token_str) = cap.get(0) {
96 if let Some(token) = self.parse_token(token_str.as_str()) {
97 let resolved = self.resolve_token(&token).await?;
98 let resolved_str = match resolved {
99 Value::String(s) => s,
100 _ => resolved.to_string(),
101 };
102 result = result.replace(token_str.as_str(), &resolved_str);
103 }
104 }
105 }
106 Ok(Value::String(result))
107 } else {
108 Ok(Value::String(s.to_string()))
109 }
110 }
111
112 fn parse_token(&self, s: &str) -> Option<TokenType> {
114 let s = s.trim();
115
116 if let Some(suffix) = s.strip_prefix("$random.") {
118 return Some(TokenType::Random(suffix.to_string()));
119 }
120
121 if let Some(suffix) = s.strip_prefix("$faker.") {
123 return Some(TokenType::Faker(suffix.to_string()));
124 }
125
126 if s.starts_with("$ai(") && s.ends_with(')') {
128 let prompt = s.strip_prefix("$ai(")?.strip_suffix(')')?;
129 return Some(TokenType::Ai(prompt.trim().to_string()));
130 }
131
132 None
133 }
134
135 async fn resolve_token(&self, token: &TokenType) -> Result<Value> {
137 match token {
138 TokenType::Random(kind) => self.resolve_random(kind).await,
139 TokenType::Faker(kind) => self.resolve_faker(kind).await,
140 TokenType::Ai(prompt) => self.resolve_ai(prompt).await,
141 }
142 }
143
144 async fn resolve_random(&self, kind: &str) -> Result<Value> {
146 match kind {
147 "int" | "integer" => Ok(json!(rand::rng().random_range(0..1000))),
148 "int.small" => Ok(json!(rand::rng().random_range(0..100))),
149 "int.large" => Ok(json!(rand::rng().random_range(0..1_000_000))),
150 "float" | "number" => Ok(json!(rand::rng().random_range(0.0..1000.0))),
151 "bool" | "boolean" => Ok(json!(rand::rng().random_bool(0.5))),
152 "uuid" => Ok(json!(uuid::Uuid::new_v4().to_string())),
153 "hex" => {
154 let bytes: [u8; 16] = rand::rng().random();
155 Ok(json!(hex::encode(bytes)))
156 }
157 "hex.short" => {
158 let bytes: [u8; 4] = rand::rng().random();
159 Ok(json!(hex::encode(bytes)))
160 }
161 "alphanumeric" => {
162 let s: String = (0..10)
163 .map(|_| {
164 let c: u8 = rand::rng().random_range(b'a'..=b'z');
165 c as char
166 })
167 .collect();
168 Ok(json!(s))
169 }
170 "choice" => {
171 let choices = ["option1", "option2", "option3"];
172 let idx = rand::rng().random_range(0..choices.len());
173 Ok(json!(choices[idx]))
174 }
175 _ => Err(Error::generic(format!("Unknown random type: {}", kind))),
176 }
177 }
178
179 async fn resolve_faker(&self, kind: &str) -> Result<Value> {
181 let mut faker = self.faker.write().await;
182
183 let value = match kind {
184 "name" => json!(faker.name()),
186 "email" => json!(faker.email()),
187 "phone" | "phone_number" => json!(faker.phone()),
188
189 "address" => json!(faker.address()),
191
192 "company" => json!(faker.company()),
194
195 "url" => json!(faker.url()),
197 "ipv4" | "ip" => json!(faker.ip_address()),
198
199 "date" | "datetime" | "timestamp" | "iso8601" => json!(faker.date_iso()),
201
202 "word" => json!(faker.word()),
204 "words" => json!(faker.words(5)),
205 "sentence" => json!(faker.sentence()),
206 "paragraph" => json!(faker.paragraph()),
207
208 "uuid" => json!(faker.uuid()),
210
211 _ => faker.generate_by_type(kind),
213 };
214
215 Ok(value)
216 }
217
218 async fn resolve_ai(&self, prompt: &str) -> Result<Value> {
220 let cache_key = format!("ai:{}", prompt);
222 {
223 let cache = self.cache.read().await;
224 if let Some(cached) = cache.get(&cache_key) {
225 return Ok(cached.clone());
226 }
227 }
228
229 if let Some(rag_engine) = &self.rag_engine {
231 let engine = rag_engine.write().await;
232 let response = engine.generate_text(prompt).await?;
233
234 let value = if let Ok(json_value) = serde_json::from_str::<Value>(&response) {
236 json_value
237 } else {
238 json!(response)
239 };
240
241 let mut cache = self.cache.write().await;
243 cache.insert(cache_key, value.clone());
244
245 Ok(value)
246 } else {
247 Ok(json!(format!("[AI: {}]", prompt)))
249 }
250 }
251
252 pub async fn clear_cache(&self) {
254 let mut cache = self.cache.write().await;
255 cache.clear();
256 }
257
258 pub async fn cache_size(&self) -> usize {
260 let cache = self.cache.read().await;
261 cache.len()
262 }
263}
264
265impl Default for TokenResolver {
266 fn default() -> Self {
267 Self::new()
268 }
269}
270
271pub async fn resolve_tokens(value: &Value) -> Result<Value> {
273 let resolver = TokenResolver::new();
274 resolver.resolve(value).await
275}
276
277pub async fn resolve_tokens_with_rag(value: &Value, rag_config: RagConfig) -> Result<Value> {
279 let resolver = TokenResolver::with_rag(rag_config);
280 resolver.resolve(value).await
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_parse_token_random() {
289 let resolver = TokenResolver::new();
290 assert_eq!(resolver.parse_token("$random.int"), Some(TokenType::Random("int".to_string())));
291 assert_eq!(
292 resolver.parse_token("$random.uuid"),
293 Some(TokenType::Random("uuid".to_string()))
294 );
295 }
296
297 #[test]
298 fn test_parse_token_faker() {
299 let resolver = TokenResolver::new();
300 assert_eq!(resolver.parse_token("$faker.name"), Some(TokenType::Faker("name".to_string())));
301 assert_eq!(
302 resolver.parse_token("$faker.email"),
303 Some(TokenType::Faker("email".to_string()))
304 );
305 }
306
307 #[test]
308 fn test_parse_token_ai() {
309 let resolver = TokenResolver::new();
310 assert_eq!(
311 resolver.parse_token("$ai(generate customer data)"),
312 Some(TokenType::Ai("generate customer data".to_string()))
313 );
314 }
315
316 #[test]
317 fn test_parse_token_invalid() {
318 let resolver = TokenResolver::new();
319 assert_eq!(resolver.parse_token("invalid"), None);
320 assert_eq!(resolver.parse_token("$invalid"), None);
321 }
322
323 #[tokio::test]
324 async fn test_resolve_random_int() {
325 let resolver = TokenResolver::new();
326 let result = resolver.resolve_random("int").await.unwrap();
327 assert!(result.is_number());
328 }
329
330 #[tokio::test]
331 async fn test_resolve_random_uuid() {
332 let resolver = TokenResolver::new();
333 let result = resolver.resolve_random("uuid").await.unwrap();
334 assert!(result.is_string());
335 let uuid_str = result.as_str().unwrap();
336 assert!(uuid::Uuid::parse_str(uuid_str).is_ok());
337 }
338
339 #[tokio::test]
340 async fn test_resolve_faker_name() {
341 let resolver = TokenResolver::new();
342 let result = resolver.resolve_faker("name").await.unwrap();
343 assert!(result.is_string());
344 }
345
346 #[tokio::test]
347 async fn test_resolve_faker_email() {
348 let resolver = TokenResolver::new();
349 let result = resolver.resolve_faker("email").await.unwrap();
350 assert!(result.is_string());
351 let email = result.as_str().unwrap();
352 assert!(email.contains('@'));
353 }
354
355 #[tokio::test]
356 async fn test_resolve_simple_string() {
357 let resolver = TokenResolver::new();
358 let value = json!("$random.uuid");
359 let result = resolver.resolve(&value).await.unwrap();
360 assert!(result.is_string());
361 }
362
363 #[tokio::test]
364 async fn test_resolve_object() {
365 let resolver = TokenResolver::new();
366 let value = json!({
367 "id": "$random.uuid",
368 "name": "$faker.name",
369 "email": "$faker.email"
370 });
371 let result = resolver.resolve(&value).await.unwrap();
372 assert!(result.is_object());
373 assert!(result["id"].is_string());
374 assert!(result["name"].is_string());
375 assert!(result["email"].is_string());
376 }
377
378 #[tokio::test]
379 async fn test_resolve_array() {
380 let resolver = TokenResolver::new();
381 let value = json!(["$random.uuid", "$faker.name"]);
382 let result = resolver.resolve(&value).await.unwrap();
383 assert!(result.is_array());
384 let arr = result.as_array().unwrap();
385 assert_eq!(arr.len(), 2);
386 }
387
388 #[tokio::test]
389 async fn test_resolve_nested() {
390 let resolver = TokenResolver::new();
391 let value = json!({
392 "user": {
393 "id": "$random.uuid",
394 "profile": {
395 "name": "$faker.name",
396 "email": "$faker.email"
397 }
398 }
399 });
400 let result = resolver.resolve(&value).await.unwrap();
401 assert!(result["user"]["id"].is_string());
402 assert!(result["user"]["profile"]["name"].is_string());
403 assert!(result["user"]["profile"]["email"].is_string());
404 }
405
406 #[tokio::test]
407 async fn test_cache() {
408 let resolver = TokenResolver::new();
409 assert_eq!(resolver.cache_size().await, 0);
410
411 resolver.clear_cache().await;
414 assert_eq!(resolver.cache_size().await, 0);
415 }
416}