use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
use uuid::Uuid;
use crate::agent::{Agent, AgentContext, AgentState};
use crate::oxyde_game::bindings::{EngineBinding, load_agent_config, parse_context_json};
use crate::{OxydeError, Result};
pub struct WasmBinding {
agents: Arc<Mutex<HashMap<String, Arc<Agent>>>>,
}
impl WasmBinding {
pub fn new() -> Self {
Self {
agents: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn get_agent(&self, id: &str) -> Result<Arc<Agent>> {
let agents = self.agents.lock().unwrap();
agents.get(id)
.cloned()
.ok_or_else(|| {
OxydeError::BindingError(format!("Agent with ID {} not found", id))
})
}
pub fn register_agent(&self, id: Uuid, agent: Arc<Agent>) {
let mut agents = self.agents.lock().unwrap();
agents.insert(id.to_string(), agent);
}
pub fn parse_wasm_context(&self, context_json: &str) -> Result<AgentContext> {
let context_map = parse_context_json(context_json)?;
let mut agent_context = AgentContext::new();
for (key, value) in context_map {
agent_context.insert(key, value);
}
Ok(agent_context)
}
pub async fn get_agent_state(&self, agent: &Agent) -> AgentState {
agent.state().await
}
}
impl EngineBinding for WasmBinding {
fn create_agent(&self, config_path: &str) -> Result<Arc<Agent>> {
let config = load_agent_config(config_path)?;
let agent = Arc::new(Agent::new(config));
self.register_agent(agent.id(), agent.clone());
Ok(agent)
}
fn create_agent_from_json(&self, json_config: &str) -> Result<Arc<Agent>> {
let config = crate::oxyde_game::bindings::parse_agent_config_json(json_config)?;
let agent = Arc::new(Agent::new(config));
self.register_agent(agent.id(), agent.clone());
Ok(agent)
}
fn update_agent(&self, agent: &Agent, context_json: &str) -> Result<()> {
let context = self.parse_wasm_context(context_json)?;
let agent_id = agent.id();
let agents = self.agents.lock().unwrap();
if let Some(stored_agent) = agents.get(&agent_id.to_string()) {
let agent_ref = stored_agent.clone();
drop(agents);
let runtime = tokio::runtime::Runtime::new().map_err(|e| {
OxydeError::BindingError(format!("Failed to create Tokio runtime: {}", e))
})?;
runtime.block_on(async {
agent_ref.update_context(context).await;
});
}
Ok(())
}
fn process_input(&self, agent: &Agent, input: &str) -> Result<String> {
let runtime = tokio::runtime::Runtime::new().map_err(|e| {
OxydeError::BindingError(format!("Failed to create Tokio runtime: {}", e))
})?;
runtime.block_on(async {
agent.process_input(input).await
})
}
fn name(&self) -> &'static str {
"wasm"
}
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub struct OxydeWasm {
binding: WasmBinding,
}
#[cfg(feature = "wasm")]
#[wasm_bindgen]
impl OxydeWasm {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
binding: WasmBinding::new(),
}
}
#[wasm_bindgen]
pub fn init() -> bool {
console_error_panic_hook::set_once();
true
}
#[wasm_bindgen]
pub fn create_agent(&self, config_path: &str) -> Result<String, JsError> {
match self.binding.create_agent(config_path) {
Ok(agent) => Ok(agent.id().to_string()),
Err(e) => Err(JsError::new(&e.to_string())),
}
}
#[wasm_bindgen]
pub fn create_agent_from_json(&self, json_config: &str) -> Result<String, JsError> {
match self.binding.create_agent_from_json(json_config) {
Ok(agent) => Ok(agent.id().to_string()),
Err(e) => Err(JsError::new(&e.to_string())),
}
}
#[wasm_bindgen]
pub fn update_agent(&self, agent_id: &str, context_json: &str) -> Result<(), JsError> {
match self.binding.get_agent(agent_id) {
Ok(agent) => {
match self.binding.update_agent(&agent, context_json) {
Ok(_) => Ok(()),
Err(e) => Err(JsError::new(&e.to_string())),
}
},
Err(e) => Err(JsError::new(&e.to_string())),
}
}
#[wasm_bindgen]
pub fn process_input(&self, agent_id: &str, input: &str) -> Result<String, JsError> {
match self.binding.get_agent(agent_id) {
Ok(agent) => {
match self.binding.process_input(&agent, input) {
Ok(response) => Ok(response),
Err(e) => Err(JsError::new(&e.to_string())),
}
},
Err(e) => Err(JsError::new(&e.to_string())),
}
}
#[wasm_bindgen]
pub fn get_agent_state(&self, agent_id: &str) -> Result<String, JsError> {
match self.binding.get_agent(agent_id) {
Ok(agent) => {
let runtime = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(e) => return Err(JsError::new(&e.to_string())),
};
let state = runtime.block_on(async {
self.binding.get_agent_state(&agent).await
});
Ok(format!("{:?}", state))
},
Err(e) => Err(JsError::new(&e.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wasm_binding_creation() {
let binding = WasmBinding::new();
assert_eq!(binding.name(), "wasm");
}
#[test]
fn test_parse_wasm_context() {
let binding = WasmBinding::new();
let context_json = r#"{"gameState": {"level": 1, "score": 100}, "playerHealth": 80}"#;
let context = binding.parse_wasm_context(context_json).unwrap();
assert!(context.contains_key("gameState"));
assert_eq!(context.get("playerHealth").unwrap().as_i64().unwrap(), 80);
}
}