diff --git a/src/wasm-lib/Cargo.lock b/src/wasm-lib/Cargo.lock index ca2913349..af31d33ee 100644 --- a/src/wasm-lib/Cargo.lock +++ b/src/wasm-lib/Cargo.lock @@ -70,12 +70,55 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.89" @@ -405,8 +448,12 @@ version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", + "unicase", + "unicode-width", ] [[package]] @@ -427,6 +474,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "colored" version = "2.1.0" @@ -1433,6 +1486,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1563,6 +1622,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "kcl-to-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "indexmap 2.5.0", + "kcl-lib", + "kittycad", + "kittycad-modeling-cmds", + "tokio", + "uuid", +] + [[package]] name = "kittycad" version = "0.3.23" @@ -1575,6 +1648,7 @@ dependencies = [ "bigdecimal", "bytes", "chrono", + "clap", "data-encoding", "format_serde_error", "futures", @@ -3348,6 +3422,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -3737,6 +3812,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.10.0" diff --git a/src/wasm-lib/Cargo.toml b/src/wasm-lib/Cargo.toml index 79245ee63..41d6af94e 100644 --- a/src/wasm-lib/Cargo.toml +++ b/src/wasm-lib/Cargo.toml @@ -66,6 +66,7 @@ members = [ "kcl", "kcl-macros", "kcl-test-server", + "kcl-to-core", ] [workspace.dependencies] diff --git a/src/wasm-lib/derive-docs/src/lib.rs b/src/wasm-lib/derive-docs/src/lib.rs index 7d7f85e2d..f57af4b17 100644 --- a/src/wasm-lib/derive-docs/src/lib.rs +++ b/src/wasm-lib/derive-docs/src/lib.rs @@ -758,7 +758,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); diff --git a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen index 0b0bb9099..36f40b933 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_lifetime.gen @@ -14,7 +14,7 @@ mod test_examples_someFn { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/args_with_refs.gen b/src/wasm-lib/derive-docs/tests/args_with_refs.gen index ace98dc92..93479ac9c 100644 --- a/src/wasm-lib/derive-docs/tests/args_with_refs.gen +++ b/src/wasm-lib/derive-docs/tests/args_with_refs.gen @@ -14,7 +14,7 @@ mod test_examples_someFn { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/array.gen b/src/wasm-lib/derive-docs/tests/array.gen index e8bdee385..56e02a823 100644 --- a/src/wasm-lib/derive-docs/tests/array.gen +++ b/src/wasm-lib/derive-docs/tests/array.gen @@ -14,7 +14,7 @@ mod test_examples_show { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } @@ -47,7 +47,7 @@ mod test_examples_show { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/box.gen b/src/wasm-lib/derive-docs/tests/box.gen index 865781969..f9692b8ef 100644 --- a/src/wasm-lib/derive-docs/tests/box.gen +++ b/src/wasm-lib/derive-docs/tests/box.gen @@ -14,7 +14,7 @@ mod test_examples_show { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen index 5fd18d0eb..d171b4c95 100644 --- a/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen +++ b/src/wasm-lib/derive-docs/tests/doc_comment_with_code.gen @@ -15,7 +15,7 @@ mod test_examples_my_func { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } @@ -48,7 +48,7 @@ mod test_examples_my_func { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/lineTo.gen b/src/wasm-lib/derive-docs/tests/lineTo.gen index e5222d26c..f066c4161 100644 --- a/src/wasm-lib/derive-docs/tests/lineTo.gen +++ b/src/wasm-lib/derive-docs/tests/lineTo.gen @@ -15,7 +15,7 @@ mod test_examples_line_to { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } @@ -48,7 +48,7 @@ mod test_examples_line_to { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/min.gen b/src/wasm-lib/derive-docs/tests/min.gen index fba122c3d..8398837af 100644 --- a/src/wasm-lib/derive-docs/tests/min.gen +++ b/src/wasm-lib/derive-docs/tests/min.gen @@ -14,7 +14,7 @@ mod test_examples_min { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } @@ -47,7 +47,7 @@ mod test_examples_min { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/option.gen b/src/wasm-lib/derive-docs/tests/option.gen index fbc842da3..666cee018 100644 --- a/src/wasm-lib/derive-docs/tests/option.gen +++ b/src/wasm-lib/derive-docs/tests/option.gen @@ -14,7 +14,7 @@ mod test_examples_show { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/option_input_format.gen b/src/wasm-lib/derive-docs/tests/option_input_format.gen index d687e1687..f2d5789fd 100644 --- a/src/wasm-lib/derive-docs/tests/option_input_format.gen +++ b/src/wasm-lib/derive-docs/tests/option_input_format.gen @@ -14,7 +14,7 @@ mod test_examples_import { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen index b7b8744d6..9c9987ba8 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_box_sketch.gen @@ -14,7 +14,7 @@ mod test_examples_import { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen index f0917006f..fee2b7f97 100644 --- a/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen +++ b/src/wasm-lib/derive-docs/tests/return_vec_sketch.gen @@ -14,7 +14,7 @@ mod test_examples_import { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/show.gen b/src/wasm-lib/derive-docs/tests/show.gen index af6e43a5a..91fec8682 100644 --- a/src/wasm-lib/derive-docs/tests/show.gen +++ b/src/wasm-lib/derive-docs/tests/show.gen @@ -14,7 +14,7 @@ mod test_examples_show { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen index b5a84a951..84d99382e 100644 --- a/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen +++ b/src/wasm-lib/derive-docs/tests/test_args_with_exec_state.gen @@ -14,7 +14,7 @@ mod test_examples_some_function { fs: std::sync::Arc::new(crate::fs::FileManager::new()), stdlib: std::sync::Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }; ctx.run(&program, None).await.unwrap(); } diff --git a/src/wasm-lib/kcl-to-core/Cargo.toml b/src/wasm-lib/kcl-to-core/Cargo.toml new file mode 100644 index 000000000..4f8e32629 --- /dev/null +++ b/src/wasm-lib/kcl-to-core/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "kcl-to-core" +description = "Utility methods to convert kcl to engine core executable tests" +version = "0.1.0" +edition = "2021" +license = "MIT" +repository = "https://github.com/KittyCAD/modeling-app" + +[lib] + +[[bin]] +name = "kcl-to-core" +path = "src/tool.rs" + +[dependencies] +anyhow = "1" +async-trait = "0.1.81" +indexmap = "2.5.0" +kcl-lib = { path = "../kcl" } +kittycad = { workspace = true, features = ["clap"] } +kittycad-modeling-cmds = { workspace = true } +tokio = { version = "1.38", features = ["full", "time", "rt", "tracing"] } +uuid = { version = "1.9.1", features = ["v4", "js", "serde"] } diff --git a/src/wasm-lib/kcl-to-core/src/conn_mock_core.rs b/src/wasm-lib/kcl-to-core/src/conn_mock_core.rs new file mode 100644 index 000000000..589e5de72 --- /dev/null +++ b/src/wasm-lib/kcl-to-core/src/conn_mock_core.rs @@ -0,0 +1,469 @@ +use anyhow::Result; +use indexmap::IndexMap; +use kcl_lib::{errors::KclError, executor::DefaultPlanes}; +use kittycad_modeling_cmds::{ + self as kcmc, + id::ModelingCmdId, + ok_response::OkModelingCmdResponse, + shared::PathSegment::{self, *}, + websocket::{ModelingBatch, ModelingCmdReq, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse}, +}; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; +use tokio::sync::RwLock; + +const CPP_PREFIX: &str = "const double scaleFactor = 100;\n"; +const NEED_PLANES: bool = true; + +#[derive(Debug, Clone)] +pub struct EngineConnection { + batch: Arc>>, + batch_end: Arc>>, + core_test: Arc>, + default_planes: Arc>>, +} + +impl EngineConnection { + pub async fn new(result: Arc>) -> Result { + if let Ok(mut code) = result.lock() { + code.push_str(CPP_PREFIX); + } + + Ok(EngineConnection { + batch: Arc::new(Mutex::new(Vec::new())), + batch_end: Arc::new(Mutex::new(IndexMap::new())), + core_test: result, + default_planes: Default::default(), + }) + } + + fn handle_command(&self, cmd_id: &ModelingCmdId, cmd: &kcmc::ModelingCmd) -> (String, OkModelingCmdResponse) { + let cpp_id = id_to_cpp(cmd_id); + let cmd_id = format!("{}", cmd_id); + let mut this_response = OkModelingCmdResponse::Empty {}; + + let new_code = match cmd { + kcmc::ModelingCmd::ObjectVisible(kcmc::ObjectVisible { hidden, object_id }) => { + format!(r#"scene->getSceneObject(Utils::UUID("{object_id}"))->setHidden({hidden});"#) + } + kcmc::ModelingCmd::EnableSketchMode(kcmc::EnableSketchMode { + entity_id, + animated: _, + ortho: _, + adjust_camera: _, + planar_normal, + }) => { + if let Some(normal) = planar_normal { + format!( + r#" + if(!scene->enableSketchMode(Utils::UUID("{entity_id}"), glm::dvec3 {{ {}, {}, {}, }}, nullopt)) + {{ + Utils::Plane plane_{cpp_id}(glm::dvec3 {{ 0, 0, 0 }}, glm::dvec3 {{ 1, 0, 0 }}, glm::dvec3 {{ 0, 1, 0 }}); + scene->enableSketchMode(plane_{cpp_id}, nullopt, nullopt, false); + }} + "#, + normal.x, normal.y, normal.z + ) + } else { + "".into() + } + } + kcmc::ModelingCmd::SketchModeDisable(kcmc::SketchModeDisable {}) => "scene->disableSketchMode();".into(), + kcmc::ModelingCmd::MakePlane(kcmc::MakePlane { + origin, + x_axis, + y_axis, + size, + .. + }) => { + let plane_id = format!("plane_{}", cpp_id); + format!( + r#" + auto {plane_id} = make_shared("plane", glm::dvec3 {{ 0, 0, 0 }}); + {plane_id}->setUUID(Utils::UUID("{cmd_id}")); + {plane_id}->makePlane(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }}, glm::dvec3 {{ {}, {}, {} }}, {}, false); + {plane_id}->setHidden(); + scene->addSceneObject({plane_id}); + "#, + origin.x.0, + origin.y.0, + origin.z.0, + x_axis.x, + x_axis.y, + x_axis.z, + y_axis.x, + y_axis.y, + y_axis.z, + size.0 + ) + } + kcmc::ModelingCmd::StartPath(kcmc::StartPath {}) => { + let sketch_id = format!("sketch_{}", cpp_id); + let path_id = format!("path_{}", cpp_id); + format!( + r#" + auto {sketch_id} = make_shared("sketch", glm::dvec3 {{ 0, 0, 0 }}); + {sketch_id}->setUUID(Utils::UUID("{cmd_id}")); + {sketch_id}->makePath(true); + auto {path_id} = {sketch_id}->get(); + scene->addSceneObject({sketch_id}); + "# + ) + } + kcmc::ModelingCmd::MovePathPen(kcmc::MovePathPen { path, to }) => { + format!( + r#" + path_{}->moveTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor); + "#, + id_to_cpp(path), + to.x.0, + to.y.0 + ) + } + kcmc::ModelingCmd::ExtendPath(kcmc::ExtendPath { path, segment }) => match segment { + Line { end, relative } => { + format!( + r#" + path_{}->lineTo(glm::dvec3 {{ {}, {}, 0.0 }} * scaleFactor, {{ {} }}); + "#, + id_to_cpp(path), + end.x.0, + end.y.0, + relative + ) + } + PathSegment::Arc { + center, + radius, + start, + end, + relative, + } => { + let start = start.value; + let end = end.value; + let radius = radius.0; + + format!( + r#" + path_{}->addArc(glm::dvec2 {{ {}, {} }} * scaleFactor, {radius} * scaleFactor, {start}, {end}, {{ {} }}); + "#, + id_to_cpp(path), + center.x.0, + center.y.0, + relative + ) + } + PathSegment::TangentialArcTo { + angle_snap_increment: _, + to, + } => { + format!( + r#" + path_{}->tangentialArcTo(glm::dvec3 {{ {}, {}, {} }} * scaleFactor, nullopt, {{ true }}); + "#, + id_to_cpp(path), + to.x.0, + to.y.0, + to.z.0, + ) + } + _ => { + format!("//{:?}", cmd) + } + }, + kcmc::ModelingCmd::ClosePath(kcmc::ClosePath { path_id }) => { + format!( + r#" + path_{}->close(); + sketch_{}->toSolid2D(); + "#, + uuid_to_cpp(path_id), + uuid_to_cpp(path_id) + ) + } + kcmc::ModelingCmd::Extrude(kcmc::Extrude { distance, target }) => { + format!( + r#" + scene->getSceneObject(Utils::UUID("{target}"))->extrudeToSolid3D({} * scaleFactor, true); + "#, + distance.0 + ) + } + kcmc::ModelingCmd::Revolve(kcmc::Revolve { + angle, + axis, + axis_is_2d, + origin, + target, + tolerance, + }) => { + let ox = origin.x.0; + let oy = origin.y.0; + let oz = origin.z.0; + let ax = axis.x; + let ay = axis.y; + let az = axis.z; + let angle = angle.value; + let tolerance = tolerance.0; + format!( + r#" + scene->getSceneObject(Utils::UUID("{target}"))->revolveToSolid3D(nullopt, glm::dvec3 {{ {ox}, {oy}, {oz} }} * scaleFactor, glm::dvec3 {{ {ax}, {ay}, {az} }}, {axis_is_2d}, {angle}, {tolerance}); + "# + ) + } + kcmc::ModelingCmd::Solid2dAddHole(kcmc::Solid2dAddHole { hole_id, object_id }) => { + format!( + r#"scene->getSceneObject(Utils::UUID("{object_id}"))->get()->addHole( + make_shared(*scene->getSceneObject(Utils::UUID("{hole_id}"))->get()->getPath()) + );"# + ) + } + kcmc::ModelingCmd::Solid3dGetExtrusionFaceInfo(kcmc::Solid3dGetExtrusionFaceInfo { + object_id, + edge_id, + }) => { + format!( + r#" + //face info get {} {} + "#, + object_id, edge_id + ) + } + kcmc::ModelingCmd::EntityCircularPattern(kcmc::EntityCircularPattern { + entity_id, + axis, + center, + num_repetitions, + arc_degrees, + rotate_duplicates, + }) => { + let entity_ids = generate_repl_uuids(*num_repetitions as usize); + + this_response = OkModelingCmdResponse::EntityCircularPattern(kcmc::output::EntityCircularPattern { + entity_ids: entity_ids.clone(), + }); + + let mut base_code: String = format!( + r#" + auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates}); + "#, + entity_id, axis.x, axis.y, axis.z, center.x.0, center.y.0, center.z.0 + ); + + let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids); + base_code.push_str(&repl_uuid_fix_code); + + base_code + } + kcmc::ModelingCmd::EntityLinearPattern(kcmc::EntityLinearPattern { + entity_id: _, + axis: _, + num_repetitions: _, + spacing: _, + }) => { + // let num_transforms = transforms.len(); + // let num_repetitions = transform.iter().map(|t| if t.replicate { 1 } else { 0 } ).sum(); + + // let mut base_code: String = format!( + // r#" + // std::vector> transforms_{cpp_id}({num_transforms}); + // "#); + + // for t in transform { + // translations_xyz.push(t.translate.x.to_millimeters(state.units)); + // translations_xyz.push(t.translate.y.to_millimeters(state.units)); + // translations_xyz.push(t.translate.z.to_millimeters(state.units)); + // scale_xyz.push(t.scale.x); + // scale_xyz.push(t.scale.y); + // scale_xyz.push(t.scale.z); + // } + + // let entity_ids = generate_repl_uuids(*num_repetitions as usize); + + // this_response = OkModelingCmdResponse::EntityLinearPattern { + // data: kittycad::types::EntityLinearPattern { + // entity_ids: entity_ids.clone(), + // }, + // }; + + // let mut base_code: String = format!( + // r#" + // auto reps_{cpp_id} = scene->entityCircularPattern(Utils::UUID("{}"), {num_repetitions}, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, glm::dvec3 {{ {}, {}, {} }} * scaleFactor, {arc_degrees}, {rotate_duplicates}); + // "#, + // entity_id, axis.x, axis.y, axis.z, center.x, center.y, center.z + // ); + + // let repl_uuid_fix_code = codegen_cpp_repl_uuid_setters(&cpp_id, &entity_ids); + // base_code.push_str(&repl_uuid_fix_code); + + // base_code + format!("//{:?}", cmd) + } + _ => { + //helps us follow along with the currently unhandled engine commands + format!("//{:?}", cmd) + } + }; + + (new_code, this_response) + } +} + +fn id_to_cpp(id: &ModelingCmdId) -> String { + uuid_to_cpp(&id.0) +} + +fn uuid_to_cpp(id: &uuid::Uuid) -> String { + let str = format!("{}", id); + str::replace(&str, "-", "_") +} + +fn generate_repl_uuids(count: usize) -> Vec { + let mut repl_ids: Vec = Vec::new(); + + repl_ids.resize_with(count, uuid::Uuid::new_v4); + repl_ids +} + +fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> String { + let mut codegen = String::new(); + + for (i, id) in entity_ids.iter().enumerate() { + let cpp_id = uuid_to_cpp(id); + let iter = format!( + r#" + //change object id -> {id} + auto repl_{cpp_id} = scene->getSceneObject(reps_{reps_id}[{i}]); + scene->removeSceneObject(repl_{cpp_id}->getUUID(), false); + repl_{cpp_id}->setUUID(Utils::UUID("{id}")); + scene->addSceneObject(repl_{cpp_id}); + "# + ); + codegen.push_str(&iter); + } + + codegen +} + +#[async_trait::async_trait] +impl kcl_lib::engine::EngineManager for EngineConnection { + fn batch(&self) -> Arc>> { + self.batch.clone() + } + + fn batch_end(&self) -> Arc>> { + self.batch_end.clone() + } + + async fn default_planes(&self, source_range: kcl_lib::executor::SourceRange) -> Result { + if NEED_PLANES { + { + let opt = self.default_planes.read().await.as_ref().cloned(); + if let Some(planes) = opt { + return Ok(planes); + } + } // drop the read lock + + let new_planes = self.new_default_planes(source_range).await?; + *self.default_planes.write().await = Some(new_planes.clone()); + + Ok(new_planes) + } else { + Ok(DefaultPlanes::default()) + } + } + + async fn clear_scene_post_hook(&self, _source_range: kcl_lib::executor::SourceRange) -> Result<(), KclError> { + Ok(()) + } + + async fn inner_send_modeling_cmd( + &self, + id: uuid::Uuid, + _source_range: kcl_lib::executor::SourceRange, + cmd: WebSocketRequest, + _id_to_source_range: std::collections::HashMap, + ) -> Result { + match cmd { + WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { + ref requests, + batch_id: _, + responses: _, + }) => { + let mut responses = HashMap::new(); + for request in requests { + let (new_code, this_response); + + if let Ok(mut test_code) = self.core_test.lock() { + (new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd); + + if !new_code.is_empty() { + let new_code = new_code + .trim() + .split(' ') + .filter(|s| !s.is_empty()) + .collect::>() + .join(" ") + + "\n"; + //println!("{new_code}"); + test_code.push_str(&new_code); + } + } else { + this_response = OkModelingCmdResponse::Empty {}; + } + + responses.insert( + request.cmd_id, + kcmc::websocket::BatchResponse::Success { + response: this_response, + }, + ); + } + Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse { + success: true, + request_id: Some(id), + resp: OkWebSocketResponseData::ModelingBatch { responses }, + })) + } + WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => { + //also handle unbatched requests inline + let (new_code, this_response); + + if let Ok(mut test_code) = self.core_test.lock() { + (new_code, this_response) = self.handle_command(&cmd_id, &cmd); + + if !new_code.is_empty() { + let new_code = new_code + .trim() + .split(' ') + .filter(|s| !s.is_empty()) + .collect::>() + .join(" ") + + "\n"; + //println!("{new_code}"); + test_code.push_str(&new_code); + } + } else { + this_response = OkModelingCmdResponse::Empty {}; + } + + Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse { + success: true, + request_id: Some(id), + resp: OkWebSocketResponseData::Modeling { + modeling_response: this_response, + }, + })) + } + _ => Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse { + success: true, + request_id: Some(id), + resp: OkWebSocketResponseData::Modeling { + modeling_response: OkModelingCmdResponse::Empty {}, + }, + })), + } + } +} diff --git a/src/wasm-lib/kcl-to-core/src/lib.rs b/src/wasm-lib/kcl-to-core/src/lib.rs new file mode 100644 index 000000000..f3a2674b9 --- /dev/null +++ b/src/wasm-lib/kcl-to-core/src/lib.rs @@ -0,0 +1,30 @@ +use anyhow::Result; +use kcl_lib::executor::ExecutorContext; +use std::sync::{Arc, Mutex}; + +#[cfg(not(target_arch = "wasm32"))] +mod conn_mock_core; + +///Converts the given kcl code to an engine test +pub async fn kcl_to_engine_core(code: &str) -> Result { + let tokens = kcl_lib::token::lexer(code)?; + let parser = kcl_lib::parser::Parser::new(tokens); + let program = parser.ast()?; + + let result = Arc::new(Mutex::new("".into())); + let ref_result = Arc::clone(&result); + + let ctx = ExecutorContext { + engine: Arc::new(Box::new( + crate::conn_mock_core::EngineConnection::new(ref_result).await?, + )), + fs: Arc::new(kcl_lib::fs::FileManager::new()), + stdlib: Arc::new(kcl_lib::std::StdLib::new()), + settings: Default::default(), + context_type: kcl_lib::executor::ContextType::MockCustomForwarded, + }; + let _memory = ctx.run(&program, None).await?; + + let result = result.lock().expect("mutex lock").clone(); + Ok(result) +} diff --git a/src/wasm-lib/kcl-to-core/src/tool.rs b/src/wasm-lib/kcl-to-core/src/tool.rs new file mode 100644 index 000000000..626b0b880 --- /dev/null +++ b/src/wasm-lib/kcl-to-core/src/tool.rs @@ -0,0 +1,19 @@ +use kcl_to_core::*; +use std::{env, fs}; + +#[tokio::main] +async fn main() { + let args: Vec = env::args().collect(); + + if args.len() < 2 { + println!("Usage: kcl-to-core path/to/file.kcl"); + return; + } + + let file_path = &args[1]; + let kcl = fs::read_to_string(file_path).expect("read file"); + + let result = kcl_to_engine_core(&kcl).await.expect("kcl conversion"); + + println!("{}", result); +} diff --git a/src/wasm-lib/kcl-to-core/tests/kcl_to_core_test.rs b/src/wasm-lib/kcl-to-core/tests/kcl_to_core_test.rs new file mode 100644 index 000000000..de2be38ae --- /dev/null +++ b/src/wasm-lib/kcl-to-core/tests/kcl_to_core_test.rs @@ -0,0 +1,19 @@ +use kcl_to_core::*; + +#[tokio::test] +async fn kcl_to_core_test() { + let result = kcl_to_engine_core( + r#" + const part001 = startSketchOn('XY') + |> startProfileAt([11.19, 28.35], %) + |> line([28.67, -13.25], %, $here) + |> line([-4.12, -22.81], %) + |> line([-33.24, 14.55], %) + |> close(%) + |> extrude(5, %) + "#, + ) + .await; + + assert!(result.is_ok()); +} diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 37af8c130..c78f5375e 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -1649,6 +1649,22 @@ impl ExtrudeSurface { } } +/// The type of ExecutorContext being used +#[derive(PartialEq, Debug, Default, Clone)] +pub enum ContextType { + /// Live engine connection + #[default] + Live, + + /// Completely mocked connection + /// Mock mode is only for the modeling app when they just want to mock engine calls and not + /// actually make them. + Mock, + + /// Handled by some other interpreter/conversion system + MockCustomForwarded, +} + /// The executor context. /// Cloning will return another handle to the same engine connection/session, /// as this uses `Arc` under the hood. @@ -1658,9 +1674,7 @@ pub struct ExecutorContext { pub fs: Arc, pub stdlib: Arc, pub settings: ExecutorSettings, - /// Mock mode is only for the modeling app when they just want to mock engine calls and not - /// actually make them. - pub is_mock: bool, + pub context_type: ContextType, } /// The executor settings. @@ -1770,10 +1784,14 @@ impl ExecutorContext { fs: Arc::new(FileManager::new()), stdlib: Arc::new(StdLib::new()), settings, - is_mock: false, + context_type: ContextType::Live, }) } + pub fn is_mock(&self) -> bool { + self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded + } + /// For executing unit tests. #[cfg(not(target_arch = "wasm32"))] pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option) -> Result { @@ -2155,7 +2173,7 @@ mod tests { fs: Arc::new(crate::fs::FileManager::new()), stdlib: Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: ContextType::Mock, }; let exec_state = ctx.run(&program, None).await?; diff --git a/src/wasm-lib/kcl/src/std/args.rs b/src/wasm-lib/kcl/src/std/args.rs index bed601892..15c45eee9 100644 --- a/src/wasm-lib/kcl/src/std/args.rs +++ b/src/wasm-lib/kcl/src/std/args.rs @@ -44,7 +44,7 @@ impl Args { fs: Arc::new(crate::fs::FileManager::new()), stdlib: Arc::new(crate::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: crate::executor::ContextType::Mock, }, }) } diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index b30c86ce6..d130e9b70 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -247,7 +247,7 @@ pub(crate) async fn do_post_extrude(sketch: Sketch, length: f64, args: Args) -> Some(extrude_surface) } } - } else if args.ctx.is_mock { + } else if args.ctx.is_mock() { // Only pre-populate the extrude surface if we are in mock mode. let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane { @@ -296,7 +296,7 @@ fn analyze_faces(args: &Args, face_infos: Vec) -> Faces { sides: HashMap::with_capacity(face_infos.len()), ..Default::default() }; - if args.ctx.is_mock { + if args.ctx.is_mock() { // Create fake IDs for start and end caps, to make extrudes mock-execute safe faces.start_cap_id = Some(Uuid::new_v4()); faces.end_cap_id = Some(Uuid::new_v4()); diff --git a/src/wasm-lib/kcl/src/std/fillet.rs b/src/wasm-lib/kcl/src/std/fillet.rs index 3d97842ef..d238e9ccc 100644 --- a/src/wasm-lib/kcl/src/std/fillet.rs +++ b/src/wasm-lib/kcl/src/std/fillet.rs @@ -228,7 +228,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result name = "getOppositeEdge", }] async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result { - if args.ctx.is_mock { + if args.ctx.is_mock() { return Ok(Uuid::new_v4()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; @@ -309,7 +309,7 @@ async fn inner_get_next_adjacent_edge( exec_state: &mut ExecState, args: Args, ) -> Result { - if args.ctx.is_mock { + if args.ctx.is_mock() { return Ok(Uuid::new_v4()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; @@ -398,7 +398,7 @@ async fn inner_get_previous_adjacent_edge( exec_state: &mut ExecState, args: Args, ) -> Result { - if args.ctx.is_mock { + if args.ctx.is_mock() { return Ok(Uuid::new_v4()); } let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?; diff --git a/src/wasm-lib/kcl/src/std/import.rs b/src/wasm-lib/kcl/src/std/import.rs index fcdefd092..13155475a 100644 --- a/src/wasm-lib/kcl/src/std/import.rs +++ b/src/wasm-lib/kcl/src/std/import.rs @@ -284,7 +284,7 @@ async fn inner_import( } } - if args.ctx.is_mock { + if args.ctx.is_mock() { return Ok(ImportedGeometry { id: uuid::Uuid::new_v4(), value: import_files.iter().map(|f| f.path.to_string()).collect(), diff --git a/src/wasm-lib/kcl/src/std/mirror.rs b/src/wasm-lib/kcl/src/std/mirror.rs index bef70b1a0..92a17c2f2 100644 --- a/src/wasm-lib/kcl/src/std/mirror.rs +++ b/src/wasm-lib/kcl/src/std/mirror.rs @@ -112,7 +112,7 @@ async fn inner_mirror_2d( SketchSet::Sketches(sketches) => sketches, }; - if args.ctx.is_mock { + if args.ctx.is_mock() { return Ok(starting_sketches); } diff --git a/src/wasm-lib/kcl/src/std/patterns.rs b/src/wasm-lib/kcl/src/std/patterns.rs index 19030c53b..9631d218e 100644 --- a/src/wasm-lib/kcl/src/std/patterns.rs +++ b/src/wasm-lib/kcl/src/std/patterns.rs @@ -290,7 +290,7 @@ async fn inner_pattern_transform<'a>( let starting_solids: Vec> = solid_set.into(); - if args.ctx.is_mock { + if args.ctx.context_type == crate::executor::ContextType::Mock { return Ok(starting_solids); } @@ -513,7 +513,7 @@ async fn inner_pattern_linear_2d( ) -> Result>, KclError> { let starting_sketches: Vec> = sketch_set.into(); - if args.ctx.is_mock { + if args.ctx.context_type == crate::executor::ContextType::Mock { return Ok(starting_sketches); } @@ -591,7 +591,7 @@ async fn inner_pattern_linear_3d( let starting_solids: Vec> = solid_set.into(); - if args.ctx.is_mock { + if args.ctx.context_type == crate::executor::ContextType::Mock { return Ok(starting_solids); } @@ -783,7 +783,7 @@ async fn inner_pattern_circular_2d( ) -> Result>, KclError> { let starting_sketches: Vec> = sketch_set.into(); - if args.ctx.is_mock { + if args.ctx.context_type == crate::executor::ContextType::Mock { return Ok(starting_sketches); } @@ -852,7 +852,7 @@ async fn inner_pattern_circular_3d( let starting_solids: Vec> = solid_set.into(); - if args.ctx.is_mock { + if args.ctx.context_type == crate::executor::ContextType::Mock { return Ok(starting_solids); } diff --git a/src/wasm-lib/src/wasm.rs b/src/wasm-lib/src/wasm.rs index 79ffa9bff..03c8eda73 100644 --- a/src/wasm-lib/src/wasm.rs +++ b/src/wasm-lib/src/wasm.rs @@ -42,6 +42,11 @@ pub async fn execute_wasm( )) }; let fs = Arc::new(kcl_lib::fs::FileManager::new(fs_manager)); + let context_type = if is_mock { + kcl_lib::executor::ContextType::Mock + } else { + kcl_lib::executor::ContextType::Live + }; let ctx = kcl_lib::executor::ExecutorContext { engine, fs, @@ -50,7 +55,7 @@ pub async fn execute_wasm( units, ..Default::default() }, - is_mock, + context_type, }; let exec_state = ctx.run(&program, Some(memory)).await.map_err(String::from)?; @@ -274,7 +279,7 @@ pub async fn kcl_lsp_run( units, ..Default::default() }, - is_mock: false, + context_type: kcl_lib::executor::ContextType::Live, }) } else { None diff --git a/src/wasm-lib/tests/executor/no_visuals.rs b/src/wasm-lib/tests/executor/no_visuals.rs index c0e6ddf8a..87d66f70c 100644 --- a/src/wasm-lib/tests/executor/no_visuals.rs +++ b/src/wasm-lib/tests/executor/no_visuals.rs @@ -38,7 +38,7 @@ async fn setup(program: &str) -> (ExecutorContext, Program) { fs: std::sync::Arc::new(kcl_lib::fs::FileManager::new()), stdlib: std::sync::Arc::new(kcl_lib::std::StdLib::new()), settings: Default::default(), - is_mock: true, + context_type: kcl_lib::executor::ContextType::Mock, }; (ctx, program) }