2025-03-15 10:08:39 -07:00
|
|
|
//! The wasm engine interface.
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use gloo_utils::format::JsValueSerdeExt;
|
|
|
|
use kcl_lib::{wasm_engine::FileManager, EngineManager, Program};
|
|
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
pub struct Context {
|
|
|
|
engine: Arc<Box<dyn EngineManager>>,
|
2025-04-17 17:22:19 -07:00
|
|
|
response_context: Arc<kcl_lib::wasm_engine::ResponseContext>,
|
2025-03-15 10:08:39 -07:00
|
|
|
fs: Arc<FileManager>,
|
2025-03-17 18:26:11 -07:00
|
|
|
mock_engine: Arc<Box<dyn EngineManager>>,
|
2025-03-15 10:08:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
impl Context {
|
|
|
|
#[wasm_bindgen(constructor)]
|
|
|
|
pub async fn new(
|
|
|
|
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
|
|
|
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
|
|
|
) -> Result<Self, JsValue> {
|
|
|
|
console_error_panic_hook::set_once();
|
|
|
|
|
2025-04-17 17:22:19 -07:00
|
|
|
let response_context = Arc::new(kcl_lib::wasm_engine::ResponseContext::new());
|
2025-03-15 10:08:39 -07:00
|
|
|
Ok(Self {
|
|
|
|
engine: Arc::new(Box::new(
|
2025-04-17 17:22:19 -07:00
|
|
|
kcl_lib::wasm_engine::EngineConnection::new(engine_manager, response_context.clone())
|
2025-03-15 10:08:39 -07:00
|
|
|
.await
|
|
|
|
.map_err(|e| format!("{:?}", e))?,
|
|
|
|
)),
|
|
|
|
fs: Arc::new(FileManager::new(fs_manager)),
|
2025-03-17 18:26:11 -07:00
|
|
|
mock_engine: Arc::new(Box::new(
|
|
|
|
kcl_lib::mock_engine::EngineConnection::new()
|
|
|
|
.await
|
|
|
|
.map_err(|e| format!("{:?}", e))?,
|
|
|
|
)),
|
2025-04-17 17:22:19 -07:00
|
|
|
response_context,
|
2025-03-15 10:08:39 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
fn create_executor_ctx(
|
|
|
|
&self,
|
|
|
|
settings: &str,
|
|
|
|
path: Option<String>,
|
|
|
|
is_mock: bool,
|
|
|
|
) -> Result<kcl_lib::ExecutorContext, String> {
|
2025-03-15 10:08:39 -07:00
|
|
|
let config: kcl_lib::Configuration = serde_json::from_str(settings).map_err(|e| e.to_string())?;
|
|
|
|
let mut settings: kcl_lib::ExecutorSettings = config.into();
|
|
|
|
if let Some(path) = path {
|
|
|
|
settings.with_current_file(std::path::PathBuf::from(path));
|
|
|
|
}
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
if is_mock {
|
|
|
|
return Ok(kcl_lib::ExecutorContext::new_mock(
|
|
|
|
self.mock_engine.clone(),
|
|
|
|
self.fs.clone(),
|
|
|
|
settings.into(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
Ok(kcl_lib::ExecutorContext::new(
|
|
|
|
self.engine.clone(),
|
|
|
|
self.fs.clone(),
|
|
|
|
settings,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute a program.
|
|
|
|
#[wasm_bindgen]
|
|
|
|
pub async fn execute(
|
|
|
|
&self,
|
|
|
|
program_ast_json: &str,
|
|
|
|
path: Option<String>,
|
|
|
|
settings: &str,
|
|
|
|
) -> Result<JsValue, String> {
|
|
|
|
console_error_panic_hook::set_once();
|
|
|
|
|
|
|
|
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
let ctx = self.create_executor_ctx(settings, path, false)?;
|
2025-03-15 10:08:39 -07:00
|
|
|
match ctx.run_with_caching(program).await {
|
|
|
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
|
|
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
2025-03-29 11:43:42 -07:00
|
|
|
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
|
|
|
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reset the scene and bust the cache.
|
|
|
|
/// ONLY use this if you absolutely need to reset the scene and bust the cache.
|
|
|
|
#[wasm_bindgen(js_name = bustCacheAndResetScene)]
|
|
|
|
pub async fn bust_cache_and_reset_scene(&self, settings: &str, path: Option<String>) -> Result<JsValue, String> {
|
|
|
|
console_error_panic_hook::set_once();
|
|
|
|
|
|
|
|
let ctx = self.create_executor_ctx(settings, path, false)?;
|
|
|
|
match ctx.bust_cache_and_reset_scene().await {
|
|
|
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
|
|
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
2025-03-15 10:08:39 -07:00
|
|
|
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
|
|
|
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
|
|
|
}
|
|
|
|
}
|
2025-03-17 18:26:11 -07:00
|
|
|
|
2025-04-17 17:22:19 -07:00
|
|
|
/// Send a response to kcl lib's engine.
|
|
|
|
#[wasm_bindgen(js_name = sendResponse)]
|
|
|
|
pub async fn send_response(&self, data: js_sys::Uint8Array) -> Result<(), JsValue> {
|
|
|
|
self.response_context.send_response(data).await
|
|
|
|
}
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
/// Execute a program in mock mode.
|
|
|
|
#[wasm_bindgen(js_name = executeMock)]
|
|
|
|
pub async fn execute_mock(
|
|
|
|
&self,
|
|
|
|
program_ast_json: &str,
|
|
|
|
path: Option<String>,
|
|
|
|
settings: &str,
|
|
|
|
use_prev_memory: bool,
|
|
|
|
) -> Result<JsValue, String> {
|
|
|
|
console_error_panic_hook::set_once();
|
|
|
|
|
|
|
|
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| e.to_string())?;
|
|
|
|
|
|
|
|
let ctx = self.create_executor_ctx(settings, path, true)?;
|
|
|
|
match ctx.run_mock(program, use_prev_memory).await {
|
|
|
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
|
|
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
|
|
|
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
|
|
|
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
|
|
|
}
|
|
|
|
}
|
2025-03-18 20:25:51 -07:00
|
|
|
|
|
|
|
/// Export a scene to a file.
|
|
|
|
#[wasm_bindgen]
|
|
|
|
pub async fn export(&self, format_json: &str, settings: &str) -> Result<JsValue, String> {
|
|
|
|
console_error_panic_hook::set_once();
|
|
|
|
|
|
|
|
let format: kittycad_modeling_cmds::format::OutputFormat3d =
|
|
|
|
serde_json::from_str(format_json).map_err(|e| e.to_string())?;
|
|
|
|
|
|
|
|
let ctx = self.create_executor_ctx(settings, None, false)?;
|
|
|
|
|
|
|
|
match ctx.export(format).await {
|
|
|
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
|
|
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
|
|
|
Ok(outcome) => JsValue::from_serde(&outcome).map_err(|e| e.to_string()),
|
|
|
|
Err(err) => Err(serde_json::to_string(&err).map_err(|serde_err| serde_err.to_string())?),
|
|
|
|
}
|
|
|
|
}
|
2025-03-15 10:08:39 -07:00
|
|
|
}
|