use crate::{
faker::EnhancedFaker,
rag::{RagConfig, RagEngine},
};
use crate::{Error, Result};
use rand::Rng;
use regex::Regex;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenType {
Random(String),
Faker(String),
Ai(String),
}
pub struct TokenResolver {
faker: Arc<RwLock<EnhancedFaker>>,
rag_engine: Option<Arc<RwLock<RagEngine>>>,
cache: Arc<RwLock<HashMap<String, Value>>>,
}
impl TokenResolver {
pub fn new() -> Self {
Self {
faker: Arc::new(RwLock::new(EnhancedFaker::new())),
rag_engine: None,
cache: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn with_rag(rag_config: RagConfig) -> Self {
Self {
faker: Arc::new(RwLock::new(EnhancedFaker::new())),
rag_engine: Some(Arc::new(RwLock::new(RagEngine::new(rag_config)))),
cache: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn resolve(&self, value: &Value) -> Result<Value> {
match value {
Value::String(s) => self.resolve_string(s).await,
Value::Array(arr) => {
let mut resolved = Vec::new();
for item in arr {
resolved.push(Box::pin(self.resolve(item)).await?);
}
Ok(Value::Array(resolved))
}
Value::Object(obj) => {
let mut resolved = serde_json::Map::new();
for (key, val) in obj {
resolved.insert(key.clone(), Box::pin(self.resolve(val)).await?);
}
Ok(Value::Object(resolved))
}
_ => Ok(value.clone()),
}
}
async fn resolve_string(&self, s: &str) -> Result<Value> {
if let Some(token) = self.parse_token(s) {
return self.resolve_token(&token).await;
}
let token_regex =
Regex::new(r"\$(?:random|faker|ai)(?:\.[a-zA-Z_][a-zA-Z0-9_]*|\([^)]*\))?")
.map_err(|e| Error::generic(format!("Regex error: {}", e)))?;
if token_regex.is_match(s) {
let mut result = s.to_string();
for cap in token_regex.captures_iter(s) {
if let Some(token_str) = cap.get(0) {
if let Some(token) = self.parse_token(token_str.as_str()) {
let resolved = self.resolve_token(&token).await?;
let resolved_str = match resolved {
Value::String(s) => s,
_ => resolved.to_string(),
};
result = result.replace(token_str.as_str(), &resolved_str);
}
}
}
Ok(Value::String(result))
} else {
Ok(Value::String(s.to_string()))
}
}
fn parse_token(&self, s: &str) -> Option<TokenType> {
let s = s.trim();
if let Some(suffix) = s.strip_prefix("$random.") {
return Some(TokenType::Random(suffix.to_string()));
}
if let Some(suffix) = s.strip_prefix("$faker.") {
return Some(TokenType::Faker(suffix.to_string()));
}
if s.starts_with("$ai(") && s.ends_with(')') {
let prompt = s.strip_prefix("$ai(")?.strip_suffix(')')?;
return Some(TokenType::Ai(prompt.trim().to_string()));
}
None
}
async fn resolve_token(&self, token: &TokenType) -> Result<Value> {
match token {
TokenType::Random(kind) => self.resolve_random(kind).await,
TokenType::Faker(kind) => self.resolve_faker(kind).await,
TokenType::Ai(prompt) => self.resolve_ai(prompt).await,
}
}
async fn resolve_random(&self, kind: &str) -> Result<Value> {
match kind {
"int" | "integer" => Ok(json!(rand::rng().random_range(0..1000))),
"int.small" => Ok(json!(rand::rng().random_range(0..100))),
"int.large" => Ok(json!(rand::rng().random_range(0..1_000_000))),
"float" | "number" => Ok(json!(rand::rng().random_range(0.0..1000.0))),
"bool" | "boolean" => Ok(json!(rand::rng().random_bool(0.5))),
"uuid" => Ok(json!(uuid::Uuid::new_v4().to_string())),
"hex" => {
let bytes: [u8; 16] = rand::rng().random();
Ok(json!(hex::encode(bytes)))
}
"hex.short" => {
let bytes: [u8; 4] = rand::rng().random();
Ok(json!(hex::encode(bytes)))
}
"alphanumeric" => {
let s: String = (0..10)
.map(|_| {
let c: u8 = rand::rng().random_range(b'a'..=b'z');
c as char
})
.collect();
Ok(json!(s))
}
"choice" => {
let choices = ["option1", "option2", "option3"];
let idx = rand::rng().random_range(0..choices.len());
Ok(json!(choices[idx]))
}
_ => Err(Error::generic(format!("Unknown random type: {}", kind))),
}
}
async fn resolve_faker(&self, kind: &str) -> Result<Value> {
let mut faker = self.faker.write().await;
let value = match kind {
"name" => json!(faker.name()),
"email" => json!(faker.email()),
"phone" | "phone_number" => json!(faker.phone()),
"address" => json!(faker.address()),
"company" => json!(faker.company()),
"url" => json!(faker.url()),
"ipv4" | "ip" => json!(faker.ip_address()),
"date" | "datetime" | "timestamp" | "iso8601" => json!(faker.date_iso()),
"word" => json!(faker.word()),
"words" => json!(faker.words(5)),
"sentence" => json!(faker.sentence()),
"paragraph" => json!(faker.paragraph()),
"uuid" => json!(faker.uuid()),
_ => faker.generate_by_type(kind),
};
Ok(value)
}
async fn resolve_ai(&self, prompt: &str) -> Result<Value> {
let cache_key = format!("ai:{}", prompt);
{
let cache = self.cache.read().await;
if let Some(cached) = cache.get(&cache_key) {
return Ok(cached.clone());
}
}
if let Some(rag_engine) = &self.rag_engine {
let engine = rag_engine.write().await;
let response = engine.generate_text(prompt).await?;
let value = if let Ok(json_value) = serde_json::from_str::<Value>(&response) {
json_value
} else {
json!(response)
};
let mut cache = self.cache.write().await;
cache.insert(cache_key, value.clone());
Ok(value)
} else {
Ok(json!(format!("[AI: {}]", prompt)))
}
}
pub async fn clear_cache(&self) {
let mut cache = self.cache.write().await;
cache.clear();
}
pub async fn cache_size(&self) -> usize {
let cache = self.cache.read().await;
cache.len()
}
}
impl Default for TokenResolver {
fn default() -> Self {
Self::new()
}
}
pub async fn resolve_tokens(value: &Value) -> Result<Value> {
let resolver = TokenResolver::new();
resolver.resolve(value).await
}
pub async fn resolve_tokens_with_rag(value: &Value, rag_config: RagConfig) -> Result<Value> {
let resolver = TokenResolver::with_rag(rag_config);
resolver.resolve(value).await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_token_random() {
let resolver = TokenResolver::new();
assert_eq!(resolver.parse_token("$random.int"), Some(TokenType::Random("int".to_string())));
assert_eq!(
resolver.parse_token("$random.uuid"),
Some(TokenType::Random("uuid".to_string()))
);
}
#[test]
fn test_parse_token_faker() {
let resolver = TokenResolver::new();
assert_eq!(resolver.parse_token("$faker.name"), Some(TokenType::Faker("name".to_string())));
assert_eq!(
resolver.parse_token("$faker.email"),
Some(TokenType::Faker("email".to_string()))
);
}
#[test]
fn test_parse_token_ai() {
let resolver = TokenResolver::new();
assert_eq!(
resolver.parse_token("$ai(generate customer data)"),
Some(TokenType::Ai("generate customer data".to_string()))
);
}
#[test]
fn test_parse_token_invalid() {
let resolver = TokenResolver::new();
assert_eq!(resolver.parse_token("invalid"), None);
assert_eq!(resolver.parse_token("$invalid"), None);
}
#[tokio::test]
async fn test_resolve_random_int() {
let resolver = TokenResolver::new();
let result = resolver.resolve_random("int").await.unwrap();
assert!(result.is_number());
}
#[tokio::test]
async fn test_resolve_random_uuid() {
let resolver = TokenResolver::new();
let result = resolver.resolve_random("uuid").await.unwrap();
assert!(result.is_string());
let uuid_str = result.as_str().unwrap();
assert!(uuid::Uuid::parse_str(uuid_str).is_ok());
}
#[tokio::test]
async fn test_resolve_faker_name() {
let resolver = TokenResolver::new();
let result = resolver.resolve_faker("name").await.unwrap();
assert!(result.is_string());
}
#[tokio::test]
async fn test_resolve_faker_email() {
let resolver = TokenResolver::new();
let result = resolver.resolve_faker("email").await.unwrap();
assert!(result.is_string());
let email = result.as_str().unwrap();
assert!(email.contains('@'));
}
#[tokio::test]
async fn test_resolve_simple_string() {
let resolver = TokenResolver::new();
let value = json!("$random.uuid");
let result = resolver.resolve(&value).await.unwrap();
assert!(result.is_string());
}
#[tokio::test]
async fn test_resolve_object() {
let resolver = TokenResolver::new();
let value = json!({
"id": "$random.uuid",
"name": "$faker.name",
"email": "$faker.email"
});
let result = resolver.resolve(&value).await.unwrap();
assert!(result.is_object());
assert!(result["id"].is_string());
assert!(result["name"].is_string());
assert!(result["email"].is_string());
}
#[tokio::test]
async fn test_resolve_array() {
let resolver = TokenResolver::new();
let value = json!(["$random.uuid", "$faker.name"]);
let result = resolver.resolve(&value).await.unwrap();
assert!(result.is_array());
let arr = result.as_array().unwrap();
assert_eq!(arr.len(), 2);
}
#[tokio::test]
async fn test_resolve_nested() {
let resolver = TokenResolver::new();
let value = json!({
"user": {
"id": "$random.uuid",
"profile": {
"name": "$faker.name",
"email": "$faker.email"
}
}
});
let result = resolver.resolve(&value).await.unwrap();
assert!(result["user"]["id"].is_string());
assert!(result["user"]["profile"]["name"].is_string());
assert!(result["user"]["profile"]["email"].is_string());
}
#[tokio::test]
async fn test_cache() {
let resolver = TokenResolver::new();
assert_eq!(resolver.cache_size().await, 0);
resolver.clear_cache().await;
assert_eq!(resolver.cache_size().await, 0);
}
}