use std::collections::BTreeSet;
use std::collections::{BTreeMap, HashMap};
use crate::string_interner::StringInterner;
use anyhow::{anyhow, Result};
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::hash::Hash;
pub struct EmbeddedBuiltinsManager {
string_interner: Arc<StringInterner>,
stats: Arc<BuiltinStats>,
builtin_cache: HashMap<String, BuiltinFunction>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum BuiltinType {
String,
Number,
Array,
Object,
Json,
Crypto,
Math,
Date,
Url,
Buffer,
Base64,
Hash,
JsonParse,
JsonStringify,
TypeOf,
InstanceOf,
ObjectKeys,
ObjectValues,
ObjectEntries,
ArrayMap,
ArrayFilter,
ArrayReduce,
}
pub struct BuiltinFunction {
pub name: &'static str,
pub builtin_type: BuiltinType,
pub func: fn(&[String]) -> Result<String>,
}
#[derive(Debug, Clone, Default)]
pub struct BuiltinStats {
pub total_calls: Arc<AtomicUsize>,
pub total_time_ms: Arc<AtomicUsize>,
pub cache_hits: Arc<AtomicUsize>,
pub cache_misses: Arc<AtomicUsize>,
}
pub struct BuiltinResult {
pub value: String,
pub execution_time_us: u64,
pub cache_hit: bool,
}
impl BuiltinStats {
pub fn new() -> Self {
Self::default()
}
pub fn record_call(&self, time_us: u64) {
self.total_calls.fetch_add(1, Ordering::Relaxed);
self.total_time_ms.fetch_add((time_us / 1000) as usize, Ordering::Relaxed);
}
pub fn record_cache_hit(&self) {
self.cache_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn record_cache_miss(&self) {
self.cache_misses.fetch_add(1, Ordering::Relaxed);
}
pub fn hit_rate(&self) -> f64 {
let hits: _ = self.cache_hits.load(Ordering::Relaxed) as f64;
let total: _ = hits + self.cache_misses.load(Ordering::Relaxed) as f64;
if total > 0.0 { hits / total } else { 0.0 }
}
pub fn avg_execution_time_us(&self) -> f64 {
let calls: _ = self.total_calls.load(Ordering::Relaxed) as f64;
let total_time: _ = self.total_time_ms.load(Ordering::Relaxed) as f64 * 1000.0;
if calls > 0.0 { total_time / calls } else { 0.0 }
}
}
impl EmbeddedBuiltinsManager {
pub fn new() -> Self {
let mut manager = Self {
string_interner: Arc::new(Mutex::new(StringInterner::new()))
stats: Arc::new(Mutex::new(BuiltinStats::new()))
builtin_cache: HashMap::new(),
};
manager.register_builtins();
manager
}
fn register_builtins(&mut self) {
self.register_builtin("string_concat", BuiltinType::String, builtin_string_concat);
self.register_builtin("string_length", BuiltinType::String, builtin_string_length);
self.register_builtin("string_slice", BuiltinType::String, builtin_string_slice);
self.register_builtin("string_index_of", BuiltinType::String, builtin_string_index_of);
self.register_builtin("string_to_upper", BuiltinType::String, builtin_string_to_upper);
self.register_builtin("string_to_lower", BuiltinType::String, builtin_string_to_lower);
self.register_builtin("string_trim", BuiltinType::String, builtin_string_trim);
self.register_builtin("json_parse", BuiltinType::JsonParse, builtin_json_parse);
self.register_builtin("json_stringify", BuiltinType::JsonStringify, builtin_json_stringify);
self.register_builtin("typeof", BuiltinType::TypeOf, builtin_typeof);
self.register_builtin("object_keys", BuiltinType::ObjectKeys, builtin_object_keys);
self.register_builtin("object_values", BuiltinType::ObjectValues, builtin_object_values);
self.register_builtin("array_length", BuiltinType::Array, builtin_array_length);
self.register_builtin("base64_encode", BuiltinType::Base64, builtin_base64_encode);
self.register_builtin("base64_decode", BuiltinType::Base64, builtin_base64_decode);
self.register_builtin("hash_md5", BuiltinType::Hash, builtin_hash_md5);
self.register_builtin("math_abs", BuiltinType::Math, builtin_math_abs);
self.register_builtin("math_floor", BuiltinType::Math, builtin_math_floor);
self.register_builtin("math_ceil", BuiltinType::Math, builtin_math_ceil);
self.register_builtin("math_round", BuiltinType::Math, builtin_math_round);
self.register_builtin("url_parse", BuiltinType::Url, builtin_url_parse);
self.register_builtin("buffer_from", BuiltinType::Buffer, builtin_buffer_from);
self.register_builtin("buffer_length", BuiltinType::Buffer, builtin_buffer_length);
eprintln!("✅ EmbeddedBuiltinsManager: Registered {} built-in functions",
self.builtin_cache.len());
}
fn register_builtin(
&mut self,
name: &'static str,
builtin_type: BuiltinType,
func: fn(&[String]) -> Result<String>,
) {
self.builtin_cache.insert(
name.to_string(),
BuiltinFunction {
name,
builtin_type,
func,
},
);
}
pub fn execute_builtin(&self, name: &str, args: &[String]) -> Result<String> {
let start: _ = Instant::now();
let builtin: _ = self.builtin_cache.get(name)
.ok_or_else(|| anyhow!("Unknown builtin function: {}", name))?;
let result: _ = (builtin.func)(args)?;
let elapsed: _ = start.elapsed();
self.stats.record_call(elapsed.as_micros() as u64);
Ok(result)
}
pub fn measure_builtin_performance(&self, name: &str, iterations: usize) -> Duration {
let start: _ = Instant::now();
for _ in 0..iterations {
let _: _ = self.execute_builtin(name, &["1".to_string(), "2".to_string()]);
}
start.elapsed()
}
pub fn get_builtins_count(&self) -> usize {
self.builtin_cache.len()
}
pub fn get_builtin_types(&self) -> Vec<String> {
let mut types = std::collections::BTreeSet::new();
for builtin in self.builtin_cache.values() {
types.insert(format!("{:?}", builtin.builtin_type).to_lowercase());
}
types.into_iter().collect()
}
pub fn get_stats(&self) -> Arc<BuiltinStats> {
Arc::clone(&self.stats)
}
}
fn builtin_string_concat(args: &[String]) -> Result<String> {
if args.len() < 2 {
return Err(anyhow!("string_concat requires at least 2 arguments"));
}
Ok(args.iter().fold(String::new(), |acc, s| acc + s.as_str())
}
fn builtin_string_length(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("string_length requires 1 argument"));
}
Ok(args[0].len().to_string())
}
fn builtin_string_slice(args: &[String]) -> Result<String> {
if args.len() < 3 {
return Err(anyhow!("string_slice requires 3 arguments: string, start, end"));
}
let start: _ = args[1].parse::<usize>().unwrap_or(0);
let end: _ = args[2].parse::<usize>().unwrap_or(args[0].len());
Ok(args[0].get(start..end).unwrap_or_default().to_string())
}
fn builtin_string_index_of(args: &[String]) -> Result<String> {
if args.len() < 2 {
return Err(anyhow!("string_index_of requires 2 arguments"));
}
Ok(args[0].find(&args[1]).unwrap_or(usize::MAX).to_string())
}
fn builtin_string_to_upper(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("string_to_upper requires 1 argument"));
}
Ok(args[0].to_uppercase())
}
fn builtin_string_to_lower(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("string_to_lower requires 1 argument"));
}
Ok(args[0].to_lowercase())
}
fn builtin_string_trim(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("string_trim requires 1 argument"));
}
Ok(args[0].trim().to_string())
}
fn builtin_json_parse(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("json_parse requires 1 argument"));
}
let s: _ = &args[0];
if !s.starts_with('{') && !s.starts_with('[') {
return Err(anyhow!("Invalid JSON"));
}
Ok(args[0].clone())
}
fn builtin_json_stringify(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("json_stringify requires 1 argument"));
}
Ok(args[0].clone())
}
fn builtin_typeof(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("typeof requires 1 argument"));
}
let arg: _ = &args[0];
let r#type = if arg.parse::<i64>().is_ok() {
"number"
} else if arg == "true" || arg == "false" {
"boolean"
} else if arg.starts_with('"') && arg.ends_with('"') {
"string"
} else if arg == "null" {
"object"
} else {
"string"
};
Ok(r#type.to_string())
}
fn builtin_object_keys(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("object_keys requires 1 argument"));
}
Ok("[]".to_string())
}
fn builtin_object_values(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("object_values requires 1 argument"));
}
Ok("[]".to_string())
}
fn builtin_array_length(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("array_length requires 1 argument"));
}
let commas: _ = args[0].matches(',').count();
Ok((commas + 1).to_string())
}
fn builtin_base64_encode(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("base64_encode requires 1 argument"));
}
Ok(general_purpose::STANDARD.encode(&args[0]))
}
fn builtin_base64_decode(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("base64_decode requires 1 argument"));
}
let decoded: _ = general_purpose::STANDARD.decode(&args[0])?;
Ok(String::from_utf8(decoded)?)
}
fn builtin_hash_md5(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("hash_md5 requires 1 argument"));
}
use md5;
let hash: _ = md5::compute(&args[0]);
Ok(format!("{:x}", hash))
}
fn builtin_math_abs(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("math_abs requires 1 argument"));
}
let val: f64 = args[0].parse().unwrap_or(0.0);
Ok(val.abs().to_string())
}
fn builtin_math_floor(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("math_floor requires 1 argument"));
}
let val: f64 = args[0].parse().unwrap_or(0.0);
Ok(val.floor().to_string())
}
fn builtin_math_ceil(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("math_ceil requires 1 argument"));
}
let val: f64 = args[0].parse().unwrap_or(0.0);
Ok(val.ceil().to_string())
}
fn builtin_math_round(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("math_round requires 1 argument"));
}
let val: f64 = args[0].parse().unwrap_or(0.0);
Ok(val.round().to_string())
}
fn builtin_url_parse(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("url_parse requires 1 argument"));
}
Ok(format!("{{\"protocol\":\"https\",\"hostname\":\"example.com\"}}"))
}
fn builtin_buffer_from(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("buffer_from requires 1 argument"));
}
Ok(format!("Buffer[{}]", args[0].len())
}
fn builtin_buffer_length(args: &[String]) -> Result<String> {
if args.is_empty() {
return Err(anyhow!("buffer_length requires 1 argument"));
}
Ok(args[0].len().to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, Instant};
std::sync::{Arc, Mutex}, atomic::{AtomicUsize, Ordering}};
#[test]
fn test_builtin_registration() {
let manager: _ = EmbeddedBuiltinsManager::new();
assert!(manager.get_builtins_count() >= 20);
}
#[test]
fn test_string_concat() {
let manager: _ = EmbeddedBuiltinsManager::new();
let result: _ = manager.execute_builtin("string_concat", &[
"hello".to_string(),
" ".to_string(),
"world".to_string(),
]).unwrap();
assert_eq!(result, "hello world");
}
#[test]
fn test_json_parse() {
let manager: _ = EmbeddedBuiltinsManager::new();
let result: _ = manager.execute_builtin("json_parse", &[
"{\"key\":\"value\"}".to_string(),
]).unwrap();
assert_eq!(result, "{\"key\":\"value\"}");
}
#[test]
fn test_builtin_types() {
let manager: _ = EmbeddedBuiltinsManager::new();
let types: _ = manager.get_builtin_types();
assert!(types.contains(&"string".to_string());
assert!(types.contains(&"jsonparse".to_string()) || types.contains(&"json".to_string());
assert!(types.contains(&"math".to_string());
}
#[test]
fn test_base64_encode() {
let manager: _ = EmbeddedBuiltinsManager::new();
let result: _ = manager.execute_builtin("base64_encode", &[
"hello".to_string(),
]).unwrap();
assert_eq!(result, "aGVsbG8=");
}
#[test]
fn test_error_handling() {
let manager: _ = EmbeddedBuiltinsManager::new();
let result: _ = manager.execute_builtin("string_concat", &[]);
assert!(result.is_err());
}
}