From b0d521918e564a33e701fa44553fa0f79c9cc500 Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Wed, 13 Dec 2023 12:02:02 -0600 Subject: [PATCH] Move execution-plan module into this workspace/repo --- .github/workflows/ci.yml | 2 + Cargo.lock | 35 ++ Cargo.toml | 1 + execution-plan/Cargo.lock | 867 ++++++++++++++++++++++++++++++ execution-plan/Cargo.toml | 19 + execution-plan/src/arithmetic.rs | 71 +++ execution-plan/src/lib.rs | 215 ++++++++ execution-plan/src/primitive.rs | 101 ++++ execution-plan/src/tests.rs | 118 ++++ execution-plan/src/value.rs | 13 + execution-plan/src/value/impls.rs | 98 ++++ 11 files changed, 1540 insertions(+) create mode 100644 execution-plan/Cargo.lock create mode 100644 execution-plan/Cargo.toml create mode 100644 execution-plan/src/arithmetic.rs create mode 100644 execution-plan/src/lib.rs create mode 100644 execution-plan/src/primitive.rs create mode 100644 execution-plan/src/tests.rs create mode 100644 execution-plan/src/value.rs create mode 100644 execution-plan/src/value/impls.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2031f50..2e00c8c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,3 +102,5 @@ jobs: uses: actions/checkout@v4 - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + package: kittycad-modeling-cmds diff --git a/Cargo.lock b/Cargo.lock index b7196551..8fbff084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -943,6 +943,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kittycad-execution-plan" +version = "0.1.0" +dependencies = [ + "bytes", + "kittycad-modeling-cmds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "thiserror", + "uuid", +] + [[package]] name = "kittycad-modeling-cmds" version = "0.1.7" @@ -970,6 +981,30 @@ dependencies = [ "webrtc", ] +[[package]] +name = "kittycad-modeling-cmds" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb3b5710639ea3000da0c11844d16d1371f82f7a20340ea887ac06ecf8d3bc5" +dependencies = [ + "anyhow", + "chrono", + "data-encoding", + "diesel_derives", + "enum-iterator", + "enum-iterator-derive", + "euler", + "http", + "kittycad-unit-conversion-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "measurements", + "parse-display", + "parse-display-derive", + "schemars", + "serde", + "serde_bytes", + "uuid", +] + [[package]] name = "kittycad-unit-conversion-derive" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a7307359..7b532464 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "modeling-cmds", + "execution-plan", "unit-conversion-derive", ] diff --git a/execution-plan/Cargo.lock b/execution-plan/Cargo.lock new file mode 100644 index 00000000..82eb633a --- /dev/null +++ b/execution-plan/Cargo.lock @@ -0,0 +1,867 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "approx" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bigdecimal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits 0.2.17", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgmath" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c" +dependencies = [ + "approx", + "mint", + "num-traits 0.1.43", + "rand", +] + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits 0.2.17", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "diesel_derives" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +dependencies = [ + "diesel_table_macro_syntax", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn 2.0.41", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "enum-iterator" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7add3873b5dd076766ee79c8e406ad1a472c385476b9e38849f8eec24f1be689" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "euler" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f19d11568a4a46aee488bdab3a2963e5e2c3cfd6091aa0abceaddcea82c0bc1" +dependencies = [ + "approx", + "cgmath", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kittycad-execution-plan" +version = "0.1.0" +dependencies = [ + "bytes", + "kittycad-modeling-cmds", + "serde", + "thiserror", + "uuid", +] + +[[package]] +name = "kittycad-modeling-cmds" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb3b5710639ea3000da0c11844d16d1371f82f7a20340ea887ac06ecf8d3bc5" +dependencies = [ + "anyhow", + "chrono", + "data-encoding", + "diesel_derives", + "enum-iterator", + "enum-iterator-derive", + "euler", + "http", + "kittycad-unit-conversion-derive", + "measurements", + "parse-display", + "parse-display-derive", + "schemars", + "serde", + "serde_bytes", + "uuid", +] + +[[package]] +name = "kittycad-unit-conversion-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7001c46a92c1edce6722a3900539b198230980799035f02d92b4e7df3fc08738" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "measurements" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5b734b4e8187ea5777bc29c086f0970a27d8de42061b48f5af32cafc0ca904b" +dependencies = [ + "libm", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.17", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits 0.2.17", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.17", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parse-display" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6509d08722b53e8dafe97f2027b22ccbe3a5db83cb352931e9716b0aa44bc5c" +dependencies = [ + "once_cell", + "parse-display-derive", + "regex", +] + +[[package]] +name = "parse-display-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68517892c8daf78da08c0db777fcc17e07f2f63ef70041718f8a7630ad84f341" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.7.5", + "structmeta", + "syn 2.0.41", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "bigdecimal", + "chrono", + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "structmeta" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ad9e09554f0456d67a69c1584c9798ba733a5b50349a6c0d0948710523922d" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.41", +] + +[[package]] +name = "structmeta-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "serde", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/execution-plan/Cargo.toml b/execution-plan/Cargo.toml new file mode 100644 index 00000000..38b1f456 --- /dev/null +++ b/execution-plan/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kittycad-execution-plan" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/KittyCAD/execution-plan" +rust-version = "1.73" +description = "A DSL for composing KittyCAD API queries" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytes = "1.5" +kittycad-modeling-cmds = "0.1.7" +serde = { version = "1", features = ["derive"] } +thiserror = "1" +uuid = "1.6.1" + +[lints] +workspace = true diff --git a/execution-plan/src/arithmetic.rs b/execution-plan/src/arithmetic.rs new file mode 100644 index 00000000..2f2f26da --- /dev/null +++ b/execution-plan/src/arithmetic.rs @@ -0,0 +1,71 @@ +use crate::primitive::{NumericPrimitive, Primitive}; +use crate::{ExecutionError, Memory, Operand, Operation}; +use serde::{Deserialize, Serialize}; + +/// Instruction to perform arithmetic on values in memory. +#[derive(Deserialize, Serialize)] +pub struct Arithmetic { + /// Apply this operation + pub operation: Operation, + /// First operand for the operation + pub operand0: Operand, + /// Second operand for the operation + pub operand1: Operand, +} + +macro_rules! arithmetic_body { + ($arith:ident, $mem:ident, $method:ident) => { + match ( + $arith.operand0.eval(&$mem)?.clone(), + $arith.operand1.eval(&$mem)?.clone(), + ) { + // If both operands are numeric, then do the arithmetic operation. + (Primitive::NumericValue(x), Primitive::NumericValue(y)) => { + let num = match (x, y) { + (NumericPrimitive::Integer(x), NumericPrimitive::Integer(y)) => { + NumericPrimitive::Integer(x.$method(y)) + } + (NumericPrimitive::Integer(x), NumericPrimitive::Float(y)) => { + NumericPrimitive::Float((x as f64).$method(y)) + } + (NumericPrimitive::Float(x), NumericPrimitive::Integer(y)) => { + NumericPrimitive::Float(x.$method(y as f64)) + } + (NumericPrimitive::Float(x), NumericPrimitive::Float(y)) => { + NumericPrimitive::Float(x.$method(y)) + } + }; + Ok(Primitive::NumericValue(num)) + } + // This operation can only be done on numeric types. + _ => Err(ExecutionError::CannotApplyOperation { + op: $arith.operation, + operands: vec![ + $arith.operand0.eval(&$mem)?.clone().to_owned(), + $arith.operand1.eval(&$mem)?.clone().to_owned(), + ], + }), + } + }; +} +impl Arithmetic { + /// Calculate the the arithmetic equation. + /// May read values from the given memory. + pub fn calculate(self, mem: &Memory) -> Result { + use std::ops::{Add, Div, Mul, Sub}; + match self.operation { + Operation::Add => { + arithmetic_body!(self, mem, add) + } + Operation::Mul => { + arithmetic_body!(self, mem, mul) + } + Operation::Sub => { + arithmetic_body!(self, mem, sub) + } + Operation::Div => { + arithmetic_body!(self, mem, div) + } + } + } +} diff --git a/execution-plan/src/lib.rs b/execution-plan/src/lib.rs new file mode 100644 index 00000000..54bef7f9 --- /dev/null +++ b/execution-plan/src/lib.rs @@ -0,0 +1,215 @@ +//! A KittyCAD execution plan (KCEP) is a list of +//! - KittyCAD API requests to make +//! - Values to send in API requests +//! - Values to assign from API responses +//! - Computation to perform on values +//! You can think of it as a domain-specific language for making KittyCAD API calls and using +//! the results to make other API calls. + +use self::arithmetic::Arithmetic; +use self::primitive::Primitive; +use serde::{Deserialize, Serialize}; +use std::fmt; +use value::Value; + +mod arithmetic; +mod primitive; +#[cfg(test)] +mod tests; +mod value; + +/// KCEP's program memory. A flat, linear list of values. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct Memory(Vec>); + +impl Default for Memory { + fn default() -> Self { + Self(vec![None; 1024]) + } +} + +/// An address in KCEP's program memory. +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct Address(usize); + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl From for Address { + fn from(value: usize) -> Self { + Self(value) + } +} + +impl Memory { + /// Get a value from KCEP's program memory. + pub fn get(&self, Address(addr): &Address) -> Option<&Primitive> { + self.0[*addr].as_ref() + } + + /// Store a value in KCEP's program memory. + pub fn set(&mut self, Address(addr): Address, value: Primitive) { + // If isn't big enough for this value, double the size of memory until it is. + while addr > self.0.len() { + self.0.extend(vec![None; self.0.len()]); + } + self.0[addr] = Some(value); + } + + /// Store a value value (i.e. a value which takes up multiple addresses in memory). + /// Store its parts in consecutive memory addresses starting at `start`. + pub fn set_composite(&mut self, composite_value: T, start: Address) { + let parts = composite_value.into_parts().into_iter(); + for (value, addr) in parts.zip(start.0..) { + self.0[addr] = Some(value); + } + } + + /// Get a value value (i.e. a value which takes up multiple addresses in memory). + /// Its parts are stored in consecutive memory addresses starting at `start`. + pub fn get_composite(&self, start: Address) -> Result { + let values = &self.0[start.0..]; + T::from_parts(values) + } +} + +/// One step of the execution plan. +#[derive(Serialize, Deserialize)] +pub enum Instruction { + /// Call the KittyCAD API. + ApiRequest { + /// Which ModelingCmd to call. + /// It's a value value starting at the given address. + endpoint: Address, + /// Which address should the response be stored in? + store_response: Option, + /// Look up each API request in this register number. + arguments: Vec
, + }, + /// Set a value in memory. + Set { + /// Which memory address to set. + address: Address, + /// What value to set the memory address to. + value: Primitive, + }, + /// Perform arithmetic on values in memory. + Arithmetic { + /// What to do. + arithmetic: Arithmetic, + /// Write the output to this memory address. + destination: Address, + }, +} + +/// Operations that can be applied to values in memory. +#[derive(Debug, Deserialize, Serialize, Clone, Copy)] +pub enum Operation { + /// Addition + Add, + /// Multiplication + Mul, + /// Subtraction + Sub, + /// Division + Div, +} + +impl fmt::Display for Operation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Operation::Add => "+", + Operation::Mul => "*", + Operation::Sub => "-", + Operation::Div => "/", + } + .fmt(f) + } +} + +/// Argument to an operation. +#[derive(Deserialize, Serialize)] +pub enum Operand { + /// A literal value. + Literal(Primitive), + /// An address which contains some literal value. + Reference(Address), +} + +impl Operand { + /// Evaluate the operand, getting its value. + fn eval(&self, mem: &Memory) -> Result { + match self { + Operand::Literal(v) => Ok(v.to_owned()), + Operand::Reference(addr) => match mem.get(addr) { + None => Err(ExecutionError::MemoryEmpty { addr: *addr }), + Some(v) => Ok(v.to_owned()), + }, + } + } +} + +/// Execute the plan. +pub fn execute(mem: &mut Memory, plan: Vec) -> Result<()> { + for step in plan { + match step { + Instruction::ApiRequest { .. } => todo!("Execute API calls"), + Instruction::Set { address, value } => { + mem.set(address, value); + } + Instruction::Arithmetic { + arithmetic, + destination, + } => { + let out = arithmetic.calculate(mem)?; + mem.set(destination, out); + } + } + } + Ok(()) +} + +type Result = std::result::Result; + +/// Errors that could occur when executing a KittyCAD execution plan. +#[derive(Debug, thiserror::Error, Clone)] +pub enum ExecutionError { + /// Memory address was not set. + #[error("Memory address {addr} was not set")] + MemoryEmpty { + /// Which address was missing + addr: Address, + }, + /// Type error, cannot apply the operation to the given operands. + #[error("Cannot apply operation {op} to operands {operands:?}")] + CannotApplyOperation { + /// Operation being attempted + op: Operation, + /// Operands being attempted + operands: Vec, + }, + /// Type error, memory contained the wrong type. + #[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")] + MemoryWrongType { + /// What the KittyCAD executor expected memory to contain + expected: &'static str, + /// What was actually in memory + actual: String, + }, + /// Memory address was not set. + #[error("Wanted {expected} values but did not get enough")] + MemoryWrongSize { + /// How many values were expected + expected: usize, + }, + /// You tried to call a KittyCAD endpoint that doesn't exist or isn't implemented. + #[error("No endpoint {name} recognized")] + UnrecognizedEndpoint { + /// Endpoint name being attempted. + name: String, + }, +} diff --git a/execution-plan/src/primitive.rs b/execution-plan/src/primitive.rs new file mode 100644 index 00000000..7001578a --- /dev/null +++ b/execution-plan/src/primitive.rs @@ -0,0 +1,101 @@ +use crate::ExecutionError; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// A value stored in KCEP program memory. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub enum Primitive { + String(String), + NumericValue(NumericPrimitive), + Uuid(Uuid), +} + +impl From for Primitive { + fn from(u: Uuid) -> Self { + Self::Uuid(u) + } +} + +impl From for Primitive { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for Primitive { + fn from(value: f64) -> Self { + Self::NumericValue(NumericPrimitive::Float(value)) + } +} + +impl TryFrom for String { + type Error = ExecutionError; + + fn try_from(value: Primitive) -> Result { + if let Primitive::String(s) = value { + Ok(s) + } else { + Err(ExecutionError::MemoryWrongType { + expected: "string", + actual: format!("{value:?}"), + }) + } + } +} + +impl TryFrom for Uuid { + type Error = ExecutionError; + + fn try_from(value: Primitive) -> Result { + if let Primitive::Uuid(u) = value { + Ok(u) + } else { + Err(ExecutionError::MemoryWrongType { + expected: "uuid", + actual: format!("{value:?}"), + }) + } + } +} + +impl TryFrom for f64 { + type Error = ExecutionError; + + fn try_from(value: Primitive) -> Result { + if let Primitive::NumericValue(NumericPrimitive::Float(x)) = value { + Ok(x) + } else { + Err(ExecutionError::MemoryWrongType { + expected: "float", + actual: format!("{value:?}"), + }) + } + } +} + +#[cfg(test)] +impl From for Primitive { + fn from(value: usize) -> Self { + Self::NumericValue(NumericPrimitive::Integer(value)) + } +} + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub enum NumericPrimitive { + Integer(usize), + Float(f64), +} + +impl crate::value::Value for Primitive { + fn into_parts(self) -> Vec { + vec![self] + } + + fn from_parts(values: &[Option]) -> Result { + let v = values + .get(0) + .ok_or(ExecutionError::MemoryWrongSize { expected: 1 })?; + v.to_owned() + .ok_or(ExecutionError::MemoryWrongSize { expected: 1 }) + } +} diff --git a/execution-plan/src/tests.rs b/execution-plan/src/tests.rs new file mode 100644 index 00000000..5058acd6 --- /dev/null +++ b/execution-plan/src/tests.rs @@ -0,0 +1,118 @@ +use kittycad_modeling_cmds::{id::ModelingCmdId, shared::Point3d, ModelingCmd, MovePathPen}; +use uuid::Uuid; + +use super::*; + +#[test] +fn write_addr_to_memory() { + let plan = vec![Instruction::Set { + address: Address(0), + value: 3.4.into(), + }]; + let mut mem = Memory::default(); + execute(&mut mem, plan).unwrap(); + assert_eq!(mem.get(&Address(0)), Some(&3.4.into())) +} + +#[test] +fn add_literals() { + let plan = vec![Instruction::Arithmetic { + arithmetic: Arithmetic { + operation: Operation::Add, + operand0: Operand::Literal(3.into()), + operand1: Operand::Literal(2.into()), + }, + destination: Address(1), + }]; + let mut mem = Memory::default(); + execute(&mut mem, plan).unwrap(); + assert_eq!(mem.get(&Address(1)), Some(&5.into())) +} + +#[test] +fn add_literal_to_reference() { + let plan = vec![ + // Memory addr 0 contains 450 + Instruction::Set { + address: Address(0), + value: 450.into(), + }, + // Add 20 to addr 0 + Instruction::Arithmetic { + arithmetic: Arithmetic { + operation: Operation::Add, + operand0: Operand::Reference(Address(0)), + operand1: Operand::Literal(20.into()), + }, + destination: Address(1), + }, + ]; + // 20 + 450 = 470 + let mut mem = Memory::default(); + execute(&mut mem, plan).unwrap(); + assert_eq!(mem.get(&Address(1)), Some(&470.into())) +} + +#[test] +fn add_to_composite_value() { + let mut mem = Memory::default(); + + // Write a point to memory. + let point_before = Point3d { + x: 2.0f64, + y: 3.0, + z: 4.0, + }; + let start_addr = Address(0); + mem.set_composite(point_before, start_addr); + assert_eq!(mem.0[0], Some(2.0.into())); + assert_eq!(mem.0[1], Some(3.0.into())); + assert_eq!(mem.0[2], Some(4.0.into())); + + // Update the point's x-value in memory. + execute( + &mut mem, + vec![Instruction::Arithmetic { + arithmetic: Arithmetic { + operation: Operation::Add, + operand0: Operand::Reference(start_addr), + operand1: Operand::Literal(40.into()), + }, + destination: start_addr, + }], + ) + .unwrap(); + + // Read the point out of memory, validate it. + let point_after: Point3d = mem.get_composite(start_addr).unwrap(); + assert_eq!( + point_after, + Point3d { + x: 42.0, + y: 3.0, + z: 4.0 + } + ) +} + +#[test] +fn api_types() { + let mut mem = Memory::default(); + let start_addr = Address(0); + let id = ModelingCmdId(Uuid::parse_str("6306afa2-3999-4b03-af30-1efad7cdc6fc").unwrap()); + let p = Point3d { + x: 2.0f64, + y: 3.0, + z: 4.0, + }; + let val_in = ModelingCmd::MovePathPen(MovePathPen { path: id, to: p }); + mem.set_composite(val_in, start_addr); + let val_out: ModelingCmd = mem.get_composite(start_addr).unwrap(); + match val_out { + ModelingCmd::MovePathPen(params) => { + assert_eq!(params.to, p); + assert_eq!(params.path, id); + } + _ => panic!("unexpected ModelingCmd variant"), + } +} diff --git a/execution-plan/src/value.rs b/execution-plan/src/value.rs new file mode 100644 index 00000000..4a18e83e --- /dev/null +++ b/execution-plan/src/value.rs @@ -0,0 +1,13 @@ +use crate::{ExecutionError, Primitive}; + +mod impls; + +/// Types that can be written to or read from KCEP program memory. +/// If they require multiple memory addresses, they will be laid out +/// into multiple consecutive memory addresses. +pub trait Value: Sized { + /// Store the value in memory. + fn into_parts(self) -> Vec; + /// Read the value from memory. + fn from_parts(values: &[Option]) -> Result; +} diff --git a/execution-plan/src/value/impls.rs b/execution-plan/src/value/impls.rs new file mode 100644 index 00000000..69a864c6 --- /dev/null +++ b/execution-plan/src/value/impls.rs @@ -0,0 +1,98 @@ +use kittycad_modeling_cmds::{id::ModelingCmdId, shared::Point3d, MovePathPen}; +use uuid::Uuid; + +use crate::{Address, ExecutionError, Primitive}; + +use super::Value; + +impl Value for kittycad_modeling_cmds::shared::Point3d +where + Primitive: From, + T: TryFrom, +{ + fn into_parts(self) -> Vec { + let points = [self.x, self.y, self.z]; + points.into_iter().map(|component| component.into()).collect() + } + + fn from_parts(values: &[Option]) -> Result { + let err = ExecutionError::MemoryWrongSize { expected: 3 }; + let [x, y, z] = [0, 1, 2].map(|n| values.get(n).ok_or(err.clone())); + let x = x?.to_owned().ok_or(err.clone())?.try_into()?; + let y = y?.to_owned().ok_or(err.clone())?.try_into()?; + let z = z?.to_owned().ok_or(err.clone())?.try_into()?; + Ok(Self { x, y, z }) + } +} + +const START_PATH: &str = "StartPath"; +const MOVE_PATH_PEN: &str = "MovePathPen"; + +impl Value for MovePathPen { + fn into_parts(self) -> Vec { + let MovePathPen { path, to } = self; + let to = to.into_parts(); + let mut vals = Vec::with_capacity(1 + to.len()); + vals.push(Primitive::Uuid(path.into())); + vals.extend(to); + vals + } + + fn from_parts(values: &[Option]) -> Result { + let path = get_some(values, 0)?; + let path = Uuid::try_from(path)?; + let path = ModelingCmdId::from(path); + let to = Point3d::from_parts(&values[1..])?; + let params = MovePathPen { path, to }; + Ok(params) + } +} + +/// Memory layout for modeling commands: +/// Field 0 is the command's name. +/// Fields 1 onwards are the command's fields. +impl Value for kittycad_modeling_cmds::ModelingCmd { + fn into_parts(self) -> Vec { + let (endpoint_name, params) = match self { + kittycad_modeling_cmds::ModelingCmd::StartPath => (START_PATH, vec![]), + kittycad_modeling_cmds::ModelingCmd::MovePathPen(MovePathPen { path, to }) => { + let to = to.into_parts(); + let mut vals = Vec::with_capacity(1 + to.len()); + vals.push(Primitive::Uuid(path.into())); + vals.extend(to); + (MOVE_PATH_PEN, vals) + } + _ => todo!(), + }; + let mut out = Vec::with_capacity(params.len() + 1); + out.push(endpoint_name.to_owned().into()); + out.extend(params); + out + } + + fn from_parts(values: &[Option]) -> Result { + // Check the array has an element at index 0 + let first_memory = values + .get(0) + .ok_or(ExecutionError::MemoryWrongSize { expected: 1 })? + .to_owned(); + // The element should be Some + let first_memory = first_memory.ok_or(ExecutionError::MemoryWrongSize { expected: 1 })?; + let endpoint_name: String = first_memory.try_into()?; + match endpoint_name.as_str() { + START_PATH => Ok(Self::StartPath), + MOVE_PATH_PEN => { + let params = MovePathPen::from_parts(&values[1..])?; + Ok(Self::MovePathPen(params)) + } + other => Err(ExecutionError::UnrecognizedEndpoint { name: other.to_owned() }), + } + } +} + +fn get_some(values: &[Option], i: usize) -> Result { + let addr = Address(0); // TODO: pass the `start` addr in + let v = values.get(i).ok_or(ExecutionError::MemoryEmpty { addr })?.to_owned(); + let v = v.ok_or(ExecutionError::MemoryEmpty { addr })?.to_owned(); + Ok(v) +}