use crate::Builtins::Core::{DixValue, IBuiltinMethod, DixType};
use crate::Builtins::Static::{
IStaticObject, ArrayObject, DateTimeObject, DixObject,
EnumObject, GuidObject, IpAddressObject, MathObject, RandomObject,
};
use std::collections::HashMap;
use std::sync::{OnceLock, RwLock};
static REGISTRY: OnceLock<StaticObjectRegistry> = OnceLock::new();
pub struct StaticObjectRegistry {
objects: RwLock<HashMap<String, Box<dyn IStaticObject>>>,
}
impl StaticObjectRegistry {
fn new() -> Self {
let mut registry = StaticObjectRegistry {
objects: RwLock::new(HashMap::new()),
};
registry.initialize_objects();
registry
}
fn initialize_objects(&mut self) {
let mut objects = self.objects.write().unwrap();
objects.insert("Dix".to_string(), Box::new(DixObject::new()));
objects.insert("Math".to_string(), Box::new(MathObject::new()));
objects.insert("DateTime".to_string(), Box::new(DateTimeObject::new()));
objects.insert("Array".to_string(), Box::new(ArrayObject::new()));
objects.insert("Random".to_string(), Box::new(RandomObject::new()));
objects.insert("Enum".to_string(), Box::new(EnumObject::new()));
objects.insert("Guid".to_string(), Box::new(GuidObject::new()));
objects.insert("IpAddress".to_string(), Box::new(IpAddressObject::new()));
}
fn get() -> &'static StaticObjectRegistry {
REGISTRY.get_or_init(StaticObjectRegistry::new)
}
}
pub fn initialize_static_registry() {
let _ = StaticObjectRegistry::get();
}
pub fn has_static_object(name: &str) -> bool {
if name.is_empty() {
return false;
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
objects.contains_key(name)
}
pub fn call_static_method(
object_name: &str,
method_name: &str,
args: &[DixValue],
) -> Result<DixValue, String> {
if object_name.is_empty() {
return Err("Object name cannot be empty".to_string());
}
if method_name.is_empty() {
return Err("Method name cannot be empty".to_string());
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
let obj = objects
.get(object_name)
.ok_or_else(|| format!("Unknown static object: {}", object_name))?;
obj.call_method(method_name, args)
}
pub fn has_static_method(object_name: &str, method_name: &str) -> bool {
if object_name.is_empty() || method_name.is_empty() {
return false;
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
objects
.get(object_name)
.map(|obj| obj.has_method(method_name))
.unwrap_or(false)
}
pub fn get_object_names() -> Vec<String> {
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
objects.keys().cloned().collect()
}
pub fn get_method_names(object_name: &str) -> Vec<String> {
if object_name.is_empty() {
return Vec::new();
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
objects
.get(object_name)
.map(|obj| obj.get_method_names())
.unwrap_or_default()
}
pub fn get_method_info(
object_name: &str,
method_name: &str,
) -> Option<MethodInfo> {
if object_name.is_empty() || method_name.is_empty() {
return None;
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
objects.get(object_name).and_then(|obj| {
obj.get_method(method_name).map(|m| MethodInfo {
name: m.name().to_string(),
parameter_count: m.parameter_count(),
min_parameter_count: m.min_parameter_count(),
return_type: m.return_type(),
description: m.description().to_string(),
})
})
}
pub fn get_method(object_name: &str, method_name: &str) -> Option<&'static dyn IBuiltinMethod> {
if has_static_method(object_name, method_name) {
None
} else {
None
}
}
pub fn validate_call_with_types(
object_name: &str,
method_name: &str,
args: &[DixValue],
) -> bool {
if object_name.is_empty() || method_name.is_empty() {
return false;
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
let obj = match objects.get(object_name) {
Some(o) => o,
None => return false,
};
match obj.get_method(method_name) {
Some(method) => method.validate_arguments(args),
None => false,
}
}
pub fn validate_call(
object_name: &str,
method_name: &str,
arg_count: usize,
) -> ValidationResult {
if object_name.is_empty() {
return ValidationResult::error("Object name cannot be empty");
}
if method_name.is_empty() {
return ValidationResult::error("Method name cannot be empty");
}
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
let obj = match objects.get(object_name) {
Some(o) => o,
None => return ValidationResult::error(&format!("Unknown static object: {}", object_name)),
};
if !obj.has_method(method_name) {
return ValidationResult::error(&format!(
"{} has no method: {}",
object_name, method_name
));
}
drop(objects);
if let Some(method_info) = get_method_info(object_name, method_name) {
if method_info.parameter_count != -1 && method_info.parameter_count as usize != arg_count {
return ValidationResult::error(&format!(
"{}.{} expects {} arguments, got {}",
object_name,
method_name,
method_info.parameter_count,
arg_count
));
}
}
ValidationResult::success()
}
pub fn get_full_registry() -> HashMap<String, Vec<String>> {
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
let mut result = HashMap::new();
for (name, obj) in objects.iter() {
result.insert(name.clone(), obj.get_method_names());
}
result
}
pub fn export_registry_info() -> RegistryInfo {
let registry = StaticObjectRegistry::get();
let objects = registry.objects.read().unwrap();
let mut object_infos = Vec::new();
for (name, obj) in objects.iter() {
let mut method_infos = Vec::new();
for method_name in obj.get_method_names() {
if let Some(method) = obj.get_method(&method_name) {
method_infos.push(MethodInfo {
name: method.name().to_string(),
parameter_count: method.parameter_count(),
min_parameter_count: method.min_parameter_count(),
return_type: method.return_type(),
description: method.description().to_string(),
});
}
}
object_infos.push(ObjectInfo {
name: name.clone(),
methods: method_infos,
});
}
RegistryInfo {
objects: object_infos,
}
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
is_valid: bool,
error_message: Option<String>,
}
impl ValidationResult {
pub fn success() -> Self {
ValidationResult {
is_valid: true,
error_message: None,
}
}
pub fn error(message: &str) -> Self {
ValidationResult {
is_valid: false,
error_message: Some(message.to_string()),
}
}
pub fn is_valid(&self) -> bool {
self.is_valid
}
pub fn error_message(&self) -> Option<&str> {
self.error_message.as_deref()
}
}
#[derive(Debug, Clone)]
pub struct RegistryInfo {
pub objects: Vec<ObjectInfo>,
}
#[derive(Debug, Clone)]
pub struct ObjectInfo {
pub name: String,
pub methods: Vec<MethodInfo>,
}
#[derive(Debug, Clone)]
pub struct MethodInfo {
pub name: String,
pub parameter_count: i32,
pub min_parameter_count: i32,
pub return_type: DixType,
pub description: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_registry_initialization() {
initialize_static_registry();
let names = get_object_names();
assert!(!names.is_empty());
assert!(names.contains(&"Math".to_string()));
assert!(names.contains(&"DateTime".to_string()));
}
#[test]
fn test_has_static_object() {
initialize_static_registry();
assert!(has_static_object("Math"));
assert!(has_static_object("DateTime"));
assert!(!has_static_object("NonExistent"));
}
#[test]
fn test_has_static_method() {
initialize_static_registry();
assert!(has_static_method("Math", "max"));
assert!(has_static_method("DateTime", "now"));
assert!(!has_static_method("Math", "nonexistent"));
}
#[test]
fn test_validate_call() {
initialize_static_registry();
let result = validate_call("Math", "max", 2);
assert!(result.is_valid());
let result = validate_call("Math", "max", 3);
assert!(!result.is_valid());
let result = validate_call("NonExistent", "method", 0);
assert!(!result.is_valid());
}
#[test]
fn test_get_method_info() {
initialize_static_registry();
let info = get_method_info("Math", "max");
assert!(info.is_some());
if let Some(method_info) = info {
assert_eq!(method_info.name, "max");
assert_eq!(method_info.parameter_count, 2);
}
}
#[test]
fn test_validate_call_with_types() {
initialize_static_registry();
let ok = validate_call_with_types(
"Math",
"max",
&[DixValue::from_int(1), DixValue::from_int(2)],
);
assert!(ok);
let bad = validate_call_with_types(
"Math",
"max",
&[DixValue::from_string("a".to_string()), DixValue::from_int(2)],
);
assert!(!bad);
let missing = validate_call_with_types("Math", "nonexistent", &[]);
assert!(!missing);
}
}