use std::collections::HashMap;
use std::sync::{OnceLock, RwLock};
use crate::script_engine::compiler::TerminatorMetadata;
use crate::script_engine::instruction::{InstructionData, InstructionHandler, InstructionMetadata, ScriptError};
use crate::script_engine::VMContext;
type TerminatorHandlerFn = Box<dyn Fn(&mut VMContext, &TerminatorMetadata) -> Result<usize, ScriptError> + Send + Sync>;
static TERMINATOR_REGISTRY: OnceLock<RwLock<HashMap<String, TerminatorHandlerFn>>> = OnceLock::new();
#[inline]
fn get_registry() -> &'static RwLock<HashMap<String, TerminatorHandlerFn>> {
TERMINATOR_REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
}
pub struct TerminatorHandler;
impl TerminatorHandler {
pub fn register_handler<F>(block_type: &str, handler: F)
where
F: Fn(&mut VMContext, &TerminatorMetadata) -> Result<usize, ScriptError> + Send + Sync + 'static,
{
let mut registry = get_registry().write().unwrap();
registry.insert(block_type.to_string(), Box::new(handler));
}
}
impl InstructionHandler for TerminatorHandler {
fn name(&self) -> &str {
"end"
}
fn parse(&self, _args: &[&str]) -> Result<InstructionData, ScriptError> {
Ok(InstructionData::None)
}
fn execute(
&self,
vm: &mut VMContext,
_data: &InstructionData,
metadata: Option<&InstructionMetadata>,
) -> Result<(), ScriptError> {
let term_metadata = metadata
.and_then(|m| m.get::<crate::script_engine::compiler::TerminatorMetadata>())
.ok_or_else(|| {
ScriptError::ExecutionError("'end' instruction missing terminator metadata".into())
})?;
let block_type = &term_metadata.block_type;
let registry = get_registry().read().map_err(|e| {
ScriptError::ExecutionError(format!("Failed to access terminator registry: {}", e))
})?;
if let Some(handler) = registry.get(block_type.as_str()) {
let next_ip = handler(vm, term_metadata)?;
vm.ip = next_ip;
} else {
vm.ip += 1;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_end_no_params() {
let handler = TerminatorHandler;
let result = handler.parse(&[]).unwrap();
assert!(matches!(result, InstructionData::None));
}
#[test]
fn test_parse_end_ignores_args() {
let handler = TerminatorHandler;
let result = handler.parse(&["extra"]).unwrap();
assert!(matches!(result, InstructionData::None));
}
}