fix export and prepare for cli lib (#325)
* add a test Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixup and make cleaner cfg options Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * redo Signed-off-by: Jess Frazelle <github@jessfraz.com> * rearrange Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * bincode error Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * working Signed-off-by: Jess Frazelle <github@jessfraz.com> * switch to bson Signed-off-by: Jess Frazelle <github@jessfraz.com> * remove all bincode Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
2
.github/workflows/cargo-build.yml
vendored
2
.github/workflows/cargo-build.yml
vendored
@ -43,7 +43,5 @@ jobs:
|
||||
- name: Run cargo build
|
||||
run: |
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo build --all --no-default-features --features noweb
|
||||
cargo build --all --no-default-features --features web
|
||||
cargo build --all
|
||||
shell: bash
|
||||
|
5
.github/workflows/cargo-test.yml
vendored
5
.github/workflows/cargo-test.yml
vendored
@ -45,4 +45,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo llvm-cov nextest --lcov --output-path lcov.info --test-threads=1 --no-fail-fast
|
||||
cargo test --all
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,5 +25,6 @@ yarn-error.log*
|
||||
# rust
|
||||
src/wasm-lib/target
|
||||
src/wasm-lib/bindings
|
||||
src/wasm-lib/kcl/bindings
|
||||
public/wasm_lib_bg.wasm
|
||||
src/wasm-lib/lcov.info
|
||||
|
@ -60,9 +60,9 @@
|
||||
"simpleserver": "http-server ./public --cors -p 3000",
|
||||
"fmt": "prettier --write ./src",
|
||||
"fmt-check": "prettier --check ./src",
|
||||
"build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg --no-default-features --features web && cargo test --all) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta",
|
||||
"build:wasm": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test --all) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt && yarn remove-importmeta",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url//g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/bindings",
|
||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||
"lint": "eslint --fix src",
|
||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json"
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ import { parse_js } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { initPromise } from './rust'
|
||||
import { Token } from './tokeniser'
|
||||
import { KCLError } from './errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/bindings/KclError'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
|
||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
|
||||
ranges.map(([start, end]) => [start, end])
|
||||
|
@ -1,20 +1,20 @@
|
||||
export type { Program } from '../wasm-lib/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/bindings/Value'
|
||||
export type { ObjectExpression } from '../wasm-lib/bindings/ObjectExpression'
|
||||
export type { MemberExpression } from '../wasm-lib/bindings/MemberExpression'
|
||||
export type { PipeExpression } from '../wasm-lib/bindings/PipeExpression'
|
||||
export type { VariableDeclaration } from '../wasm-lib/bindings/VariableDeclaration'
|
||||
export type { PipeSubstitution } from '../wasm-lib/bindings/PipeSubstitution'
|
||||
export type { Identifier } from '../wasm-lib/bindings/Identifier'
|
||||
export type { UnaryExpression } from '../wasm-lib/bindings/UnaryExpression'
|
||||
export type { BinaryExpression } from '../wasm-lib/bindings/BinaryExpression'
|
||||
export type { ReturnStatement } from '../wasm-lib/bindings/ReturnStatement'
|
||||
export type { ExpressionStatement } from '../wasm-lib/bindings/ExpressionStatement'
|
||||
export type { CallExpression } from '../wasm-lib/bindings/CallExpression'
|
||||
export type { VariableDeclarator } from '../wasm-lib/bindings/VariableDeclarator'
|
||||
export type { BinaryPart } from '../wasm-lib/bindings/BinaryPart'
|
||||
export type { Literal } from '../wasm-lib/bindings/Literal'
|
||||
export type { ArrayExpression } from '../wasm-lib/bindings/ArrayExpression'
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
||||
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
|
||||
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
|
||||
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
|
||||
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
|
||||
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
|
||||
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
|
||||
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
|
||||
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
|
||||
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
|
||||
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
|
||||
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
|
||||
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
|
||||
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
|
||||
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
|
||||
|
||||
export type SyntaxType =
|
||||
| 'Program'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { KclError as RustKclError } from '../wasm-lib/bindings/KclError'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
|
||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||
export class KCLError {
|
||||
|
@ -4,10 +4,10 @@ import {
|
||||
ArtifactMap,
|
||||
SourceRangeMap,
|
||||
} from './std/engineConnection'
|
||||
import { ProgramReturn } from '../wasm-lib/bindings/ProgramReturn'
|
||||
import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
|
||||
import { execute_wasm } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { KCLError } from './errors'
|
||||
import { KclError as RustKclError } from '../wasm-lib/bindings/KclError'
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
import { rangeTypeFix } from './abstractSyntaxTree'
|
||||
|
||||
export type SourceRange = [number, number]
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { lexer_js } from '../wasm-lib/pkg/wasm_lib'
|
||||
import { initPromise } from './rust'
|
||||
import { Token } from '../wasm-lib/bindings/Token'
|
||||
import { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
|
||||
export type { Token } from '../wasm-lib/bindings/Token'
|
||||
export type { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
|
||||
export async function asyncLexer(str: string): Promise<Token[]> {
|
||||
await initPromise
|
||||
|
263
src/wasm-lib/Cargo.lock
generated
263
src/wasm-lib/Cargo.lock
generated
@ -27,6 +27,17 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.4"
|
||||
@ -109,6 +120,12 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.2"
|
||||
@ -147,6 +164,18 @@ version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@ -156,6 +185,28 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bson"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aeb8bae494e49dbc330dd23cf78f6f7accee22f640ce3ab17841badaa4ce232"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.13.1",
|
||||
"bitvec",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"time 0.3.27",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
@ -281,6 +332,16 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
@ -488,6 +549,12 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
@ -606,28 +673,6 @@ version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-events"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-file"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f"
|
||||
dependencies = [
|
||||
"gloo-events",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.2.0"
|
||||
@ -693,6 +738,12 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.5"
|
||||
@ -881,13 +932,41 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0443a9f76cee80d5a43d076028d3ce39d2f6f6b66fc5c1a0ce24f8d7caf733b9"
|
||||
name = "kcl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"bson",
|
||||
"derive-docs",
|
||||
"expectorate",
|
||||
"futures",
|
||||
"js-sys",
|
||||
"kittycad",
|
||||
"lazy_static",
|
||||
"parse-display",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"ts-rs",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e71916b50966110cb9f70aa6c310748a153fdcb0183a02615324a6f457fd18e8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"data-encoding",
|
||||
@ -1151,6 +1230,12 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.5.1"
|
||||
@ -1244,9 +1329,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -1321,6 +1406,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@ -1433,7 +1524,7 @@ version = "0.11.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@ -1519,9 +1610,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.8"
|
||||
version = "0.38.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
@ -1542,13 +1633,25 @@ dependencies = [
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1582,6 +1685,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.12"
|
||||
@ -1627,6 +1739,29 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
@ -1647,18 +1782,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.186"
|
||||
version = "1.0.187"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1"
|
||||
checksum = "30a7fe14252655bd1e578af19f5fa00fe02fd0013b100ca6b49fde31c41bae4c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.186"
|
||||
name = "serde_bytes"
|
||||
version = "0.11.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
|
||||
checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.187"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e46b2a6ca578b3f1d4501b12f78ed4692006d79d82a1a7c561c12dbc3d625eb8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1682,6 +1826,7 @@ version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@ -1925,6 +2070,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "task-local-extensions"
|
||||
version = "0.1.4"
|
||||
@ -2110,7 +2261,10 @@ checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
@ -2227,6 +2381,7 @@ dependencies = [
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"rustls",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
@ -2435,30 +2590,11 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||
name = "wasm-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backtrace",
|
||||
"bincode",
|
||||
"derive-docs",
|
||||
"expectorate",
|
||||
"futures",
|
||||
"gloo-file",
|
||||
"bson",
|
||||
"gloo-utils",
|
||||
"http",
|
||||
"httparse",
|
||||
"js-sys",
|
||||
"kcl",
|
||||
"kittycad",
|
||||
"lazy_static",
|
||||
"parse-display",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"ts-rs",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
@ -2661,6 +2797,15 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
@ -8,28 +8,11 @@ edition = "2021"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
backtrace = "0.3"
|
||||
bincode = "1.3.3"
|
||||
derive-docs = { path = "derive-docs" }
|
||||
futures = { version = "0.3.28", optional = true }
|
||||
gloo-file = { version = "0.3.0", optional = true }
|
||||
bson = { version = "2.6.1", features = ["uuid-1", "chrono"] }
|
||||
gloo-utils = "0.2.0"
|
||||
http = "0.2.9"
|
||||
httparse = { version = "1.8.0", optional = true }
|
||||
js-sys = { version = "0.3.64", optional = true }
|
||||
kcl = { path = "kcl" }
|
||||
kittycad = { version = "0.2.15", default-features = false, features = ["js"] }
|
||||
lazy_static = "1.4.0"
|
||||
parse-display = "0.8.2"
|
||||
regex = "1.7.1"
|
||||
schemars = { version = "0.8", features = ["url", "uuid1"] }
|
||||
serde = {version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
thiserror = "1.0.47"
|
||||
tokio = { version = "1.32.0", features = ["full"], optional = true }
|
||||
tokio-tungstenite = { version = "0.20.0", optional = true }
|
||||
ts-rs = { git = "https://github.com/kittycad/ts-rs.git", branch = "serde_json", features = ["serde-json-impl", "uuid-impl"] }
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-bindgen-futures = "0.4.37"
|
||||
|
||||
@ -37,17 +20,8 @@ wasm-bindgen-futures = "0.4.37"
|
||||
panic = "abort"
|
||||
debug = true
|
||||
|
||||
[dev-dependencies]
|
||||
expectorate = "1.0.7"
|
||||
pretty_assertions = "1.4.0"
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
web = ["dep:gloo-file", "dep:js-sys"]
|
||||
noweb = ["dep:futures", "dep:httparse", "dep:tokio", "dep:tokio-tungstenite"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"derive-docs"
|
||||
"derive-docs",
|
||||
"kcl"
|
||||
]
|
||||
|
42
src/wasm-lib/kcl/Cargo.toml
Normal file
42
src/wasm-lib/kcl/Cargo.toml
Normal file
@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "kcl"
|
||||
description = "KittyCAD Language"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
derive-docs = { path = "../derive-docs" }
|
||||
kittycad = { version = "0.2.15", default-features = false, features = ["js"] }
|
||||
lazy_static = "1.4.0"
|
||||
parse-display = "0.8.2"
|
||||
regex = "1.7.1"
|
||||
schemars = { version = "0.8", features = ["url", "uuid1"] }
|
||||
serde = {version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
thiserror = "1.0.47"
|
||||
ts-rs = { git = "https://github.com/kittycad/ts-rs.git", branch = "serde_json", features = ["serde-json-impl", "uuid-impl"] }
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-bindgen-futures = "0.4.37"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = { version = "0.3.64" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
bson = { version = "2.6.1", features = ["uuid-1", "chrono"] }
|
||||
futures = { version = "0.3.28" }
|
||||
reqwest = { version = "0.11.20", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
|
||||
[dev-dependencies]
|
||||
expectorate = "1.0.7"
|
||||
pretty_assertions = "1.4.0"
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
211
src/wasm-lib/kcl/src/engine/conn.rs
Normal file
211
src/wasm-lib/kcl/src/engine/conn.rs
Normal file
@ -0,0 +1,211 @@
|
||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||
//! engine.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use kittycad::types::{OkWebSocketResponseData, WebSocketRequest, WebSocketResponse};
|
||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||
|
||||
use crate::errors::{KclError, KclErrorDetails};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConnection {
|
||||
tcp_write:
|
||||
futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>,
|
||||
tcp_read_handle: tokio::task::JoinHandle<Result<()>>,
|
||||
export_notifier: Arc<tokio::sync::Notify>,
|
||||
}
|
||||
|
||||
impl Drop for EngineConnection {
|
||||
fn drop(&mut self) {
|
||||
// Drop the read handle.
|
||||
self.tcp_read_handle.abort();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpRead {
|
||||
stream: futures::stream::SplitStream<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>>,
|
||||
}
|
||||
|
||||
impl TcpRead {
|
||||
pub async fn read(&mut self) -> Result<WebSocketResponse> {
|
||||
let msg = self.stream.next().await.unwrap()?;
|
||||
let msg: WebSocketResponse = match msg {
|
||||
WsMsg::Text(text) => serde_json::from_str(&text)?,
|
||||
WsMsg::Binary(bin) => bson::from_slice(&bin)?,
|
||||
other => anyhow::bail!("Unexpected websocket message from server: {}", other),
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
pub async fn new(ws: reqwest::Upgraded, export_dir: &str) -> Result<EngineConnection> {
|
||||
// Make sure the export directory exists and that it is a directory.
|
||||
let export_dir = std::path::Path::new(export_dir).to_owned();
|
||||
if !export_dir.exists() {
|
||||
anyhow::bail!("Export directory does not exist: {}", export_dir.display());
|
||||
}
|
||||
// Make sure it is a directory.
|
||||
if !export_dir.is_dir() {
|
||||
anyhow::bail!(
|
||||
"Export directory is not a directory: {}",
|
||||
export_dir.display()
|
||||
);
|
||||
}
|
||||
|
||||
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
|
||||
ws,
|
||||
tokio_tungstenite::tungstenite::protocol::Role::Client,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tcp_write, tcp_read) = ws_stream.split();
|
||||
|
||||
let mut tcp_read = TcpRead { stream: tcp_read };
|
||||
|
||||
let export_notifier = Arc::new(tokio::sync::Notify::new());
|
||||
let export_notifier_clone = export_notifier.clone();
|
||||
|
||||
let tcp_read_handle = tokio::spawn(async move {
|
||||
// Get Websocket messages from API server
|
||||
loop {
|
||||
match tcp_read.read().await {
|
||||
Ok(ws_resp) => {
|
||||
if !ws_resp.success {
|
||||
println!("got ws errors: {:?}", ws_resp.errors);
|
||||
export_notifier.notify_one();
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(msg) = ws_resp.resp {
|
||||
match msg {
|
||||
OkWebSocketResponseData::IceServerInfo { ice_servers } => {
|
||||
println!("got ice server info: {:?}", ice_servers);
|
||||
}
|
||||
OkWebSocketResponseData::SdpAnswer { answer } => {
|
||||
println!("got sdp answer: {:?}", answer);
|
||||
}
|
||||
OkWebSocketResponseData::TrickleIce { candidate } => {
|
||||
println!("got trickle ice: {:?}", candidate);
|
||||
}
|
||||
OkWebSocketResponseData::Modeling { .. } => {}
|
||||
OkWebSocketResponseData::Export { files } => {
|
||||
// Save the files to our export directory.
|
||||
for file in files {
|
||||
let path = export_dir.join(file.name);
|
||||
std::fs::write(&path, file.contents)?;
|
||||
println!("Wrote file: {}", path.display());
|
||||
}
|
||||
|
||||
// Tell the export notifier that we have new files.
|
||||
export_notifier.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("got ws error: {:?}", e);
|
||||
export_notifier.notify_one();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(EngineConnection {
|
||||
tcp_write,
|
||||
tcp_read_handle,
|
||||
export_notifier: export_notifier_clone,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn wait_for_files(&self) {
|
||||
self.export_notifier.notified().await;
|
||||
}
|
||||
|
||||
pub async fn tcp_send(&mut self, msg: WebSocketRequest) -> Result<()> {
|
||||
let msg = serde_json::to_string(&msg)?;
|
||||
self.tcp_write.send(WsMsg::Text(msg)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_modeling_cmd(
|
||||
&mut self,
|
||||
id: uuid::Uuid,
|
||||
source_range: crate::executor::SourceRange,
|
||||
cmd: kittycad::types::ModelingCmd,
|
||||
) -> Result<(), KclError> {
|
||||
futures::executor::block_on(
|
||||
self.tcp_send(WebSocketRequest::ModelingCmdReq { cmd, cmd_id: id }),
|
||||
)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to send modeling command: {}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::executor::{execute, BodyType, ProgramMemory, SourceRange};
|
||||
|
||||
pub async fn parse_execute_export(code: &str) -> Result<()> {
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let export_dir_str = "/tmp/";
|
||||
let program = crate::parser::abstract_syntax_tree(&tokens)?;
|
||||
let mut mem: ProgramMemory = Default::default();
|
||||
let mut engine = EngineConnection::new(
|
||||
"wss://api.dev.kittycad.io/ws/modeling/commands?webrtc=false",
|
||||
std::env::var("KITTYCAD_API_TOKEN").unwrap().as_str(),
|
||||
"modeling-app-tests",
|
||||
export_dir_str,
|
||||
)
|
||||
.await?;
|
||||
let _ = execute(program, &mut mem, BodyType::Root, &mut engine)?;
|
||||
// Send an export request to the engine.
|
||||
engine.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
kittycad::types::ModelingCmd::Export {
|
||||
entity_ids: vec![],
|
||||
format: kittycad::types::OutputFormat::Gltf {
|
||||
presentation: kittycad::types::Presentation::Pretty,
|
||||
storage: kittycad::types::Storage::Embedded,
|
||||
},
|
||||
},
|
||||
)?;
|
||||
|
||||
engine.wait_for_files().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_export_file() {
|
||||
let ast = r#"const part001 = startSketchAt([-0.01, -0.06])
|
||||
|> line([0, 20], %)
|
||||
|> line([5, 0], %)
|
||||
|> line([-10, 5], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
|
||||
show(part001)"#;
|
||||
parse_execute_export(ast).await.unwrap();
|
||||
|
||||
// Ensure we have a file called "part001.gltf" in the export directory.
|
||||
let export_dir = std::path::Path::new("/tmp/");
|
||||
let export_file = export_dir.join("part001.gltf");
|
||||
assert!(export_file.exists());
|
||||
// Make sure the file is not empty.
|
||||
assert!(export_file.metadata().unwrap().len() != 0);
|
||||
}
|
||||
}
|
@ -9,11 +9,7 @@ use crate::errors::KclError;
|
||||
pub struct EngineConnection {}
|
||||
|
||||
impl EngineConnection {
|
||||
pub async fn new(
|
||||
_conn_str: &str,
|
||||
_auth_token: &str,
|
||||
_origin: &str,
|
||||
) -> Result<EngineConnection> {
|
||||
pub async fn new() -> Result<EngineConnection> {
|
||||
Ok(EngineConnection {})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::errors::{KclError, KclErrorDetails};
|
||||
|
||||
#[wasm_bindgen(module = "/../lang/std/engineConnection.ts")]
|
||||
#[wasm_bindgen(module = "/../../lang/std/engineConnection.ts")]
|
||||
extern "C" {
|
||||
#[derive(Debug, Clone)]
|
||||
pub type EngineCommandManager;
|
@ -2,19 +2,19 @@
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(feature = "noweb")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(test))]
|
||||
pub mod conn_noweb;
|
||||
#[cfg(feature = "noweb")]
|
||||
pub mod conn;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(not(test))]
|
||||
pub use conn_noweb::EngineConnection;
|
||||
pub use conn::EngineConnection;
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
pub mod conn_web;
|
||||
#[cfg(feature = "web")]
|
||||
pub mod conn_wasm;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
pub use conn_web::EngineConnection;
|
||||
pub use conn_wasm::EngineConnection;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod conn_mock;
|
||||
@ -31,27 +31,16 @@ pub struct EngineManager {
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl EngineManager {
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(not(test))]
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub async fn new(manager: conn_web::EngineCommandManager) -> EngineManager {
|
||||
pub async fn new(manager: conn_wasm::EngineCommandManager) -> EngineManager {
|
||||
EngineManager {
|
||||
// This unwrap is safe because the connection is always created.
|
||||
connection: EngineConnection::new(manager).await.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "web"))]
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub async fn new(conn_str: &str, auth_token: &str, origin: &str) -> EngineManager {
|
||||
EngineManager {
|
||||
// TODO: fix unwrap.
|
||||
connection: EngineConnection::new(conn_str, auth_token, origin)
|
||||
.await
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_modeling_cmd(&mut self, id_str: &str, cmd_str: &str) -> Result<(), String> {
|
||||
let id = uuid::Uuid::parse_str(id_str).map_err(|e| e.to_string())?;
|
||||
let cmd = serde_json::from_str(cmd_str).map_err(|e| e.to_string())?;
|
@ -6,9 +6,6 @@ use anyhow::Result;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(not(test))]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{
|
||||
abstract_syntax_tree_types::{BodyItem, FunctionExpression, Value},
|
||||
engine::EngineConnection,
|
||||
@ -506,7 +503,7 @@ impl Default for PipeInfo {
|
||||
}
|
||||
|
||||
/// Execute a AST's program.
|
||||
fn execute(
|
||||
pub fn execute(
|
||||
program: crate::abstract_syntax_tree_types::Program,
|
||||
memory: &mut ProgramMemory,
|
||||
options: BodyType,
|
||||
@ -681,53 +678,6 @@ fn execute(
|
||||
Ok(memory.clone())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[cfg(feature = "web")]
|
||||
#[cfg(not(test))]
|
||||
#[wasm_bindgen]
|
||||
pub async fn execute_wasm(
|
||||
program_str: &str,
|
||||
memory_str: &str,
|
||||
manager: crate::engine::conn_web::EngineCommandManager,
|
||||
) -> Result<JsValue, String> {
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
|
||||
// deserialize the ast from a stringified json
|
||||
let program: crate::abstract_syntax_tree_types::Program =
|
||||
serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||
let mut mem: ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut engine = EngineConnection::new(manager)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &mut engine).map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
// gloo-serialize crate instead.
|
||||
JsValue::from_serde(&memory).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[cfg(not(feature = "web"))]
|
||||
#[wasm_bindgen]
|
||||
pub async fn execute_wasm(program_str: &str, memory_str: &str) -> Result<JsValue, String> {
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
|
||||
// deserialize the ast from a stringified json
|
||||
let program: crate::abstract_syntax_tree_types::Program =
|
||||
serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||
let mut mem: ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut engine = EngineConnection::new("dev.kittycad.io", "some-token", "")
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &mut engine).map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
// gloo-serialize crate instead.
|
||||
JsValue::from_serde(&memory).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -737,7 +687,7 @@ mod tests {
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let program = crate::parser::abstract_syntax_tree(&tokens)?;
|
||||
let mut mem: ProgramMemory = Default::default();
|
||||
let mut engine = EngineConnection::new("dev.kittycad.io", "some-token", "").await?;
|
||||
let mut engine = EngineConnection::new().await?;
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &mut engine)?;
|
||||
|
||||
Ok(memory)
|
10
src/wasm-lib/kcl/src/lib.rs
Normal file
10
src/wasm-lib/kcl/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
pub mod abstract_syntax_tree_types;
|
||||
mod docs;
|
||||
pub mod engine;
|
||||
pub mod errors;
|
||||
pub mod executor;
|
||||
pub mod math_parser;
|
||||
pub mod parser;
|
||||
pub mod recast;
|
||||
pub mod std;
|
||||
pub mod tokeniser;
|
@ -9,12 +9,8 @@ use crate::abstract_syntax_tree_types::{
|
||||
};
|
||||
use crate::errors::{KclError, KclErrorDetails};
|
||||
use crate::math_parser::parse_expression;
|
||||
use crate::tokeniser::lexer;
|
||||
use crate::tokeniser::{Token, TokenType};
|
||||
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
fn make_identifier(tokens: &[Token], index: usize) -> Identifier {
|
||||
let current_token = &tokens[index];
|
||||
Identifier {
|
||||
@ -1540,15 +1536,6 @@ pub fn abstract_syntax_tree(tokens: &[Token]) -> Result<Program, KclError> {
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_js(js: &str) -> Result<JsValue, String> {
|
||||
let tokens = lexer(js);
|
||||
let program = abstract_syntax_tree(&tokens).map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
// gloo-serialize crate instead.
|
||||
JsValue::from_serde(&program).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -1556,7 +1543,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_identifier() {
|
||||
let tokens = lexer("a");
|
||||
let tokens = crate::tokeniser::lexer("a");
|
||||
let identifier = make_identifier(&tokens, 0);
|
||||
assert_eq!(
|
||||
Identifier {
|
||||
@ -1570,7 +1557,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_identifier_with_const_myvar_equals_5_and_index_2() {
|
||||
let tokens = lexer("const myVar = 5");
|
||||
let tokens = crate::tokeniser::lexer("const myVar = 5");
|
||||
let identifier = make_identifier(&tokens, 2);
|
||||
assert_eq!(
|
||||
Identifier {
|
||||
@ -1584,7 +1571,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_identifier_multiline() {
|
||||
let tokens = lexer("const myVar = 5\nconst newVar = myVar + 1");
|
||||
let tokens = crate::tokeniser::lexer("const myVar = 5\nconst newVar = myVar + 1");
|
||||
let identifier = make_identifier(&tokens, 2);
|
||||
assert_eq!(
|
||||
Identifier {
|
||||
@ -1607,7 +1594,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_identifier_call_expression() {
|
||||
let tokens = lexer("log(5, \"hello\", aIdentifier)");
|
||||
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)");
|
||||
let identifier = make_identifier(&tokens, 0);
|
||||
assert_eq!(
|
||||
Identifier {
|
||||
@ -1629,7 +1616,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn test_make_none_code_node() {
|
||||
let tokens = lexer("log(5, \"hello\", aIdentifier)");
|
||||
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)");
|
||||
let index = 4;
|
||||
let expected_output = (
|
||||
Some(NoneCodeNode {
|
||||
@ -1651,7 +1638,7 @@ mod tests {
|
||||
7,
|
||||
);
|
||||
assert_eq!(make_none_code_node(&tokens, index), expected_output);
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"
|
||||
const yo = { a: { b: { c: '123' } } }
|
||||
// this is a comment
|
||||
@ -1700,7 +1687,7 @@ const key = 'c'"#,
|
||||
31,
|
||||
);
|
||||
assert_eq!(make_none_code_node(&tokens, index), expected_output);
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
@ -1724,7 +1711,7 @@ const key = 'c'"#,
|
||||
|
||||
#[test]
|
||||
fn test_collect_object_keys() {
|
||||
let tokens = lexer("const prop = yo.one[\"two\"]");
|
||||
let tokens = crate::tokeniser::lexer("const prop = yo.one[\"two\"]");
|
||||
let keys_info = collect_object_keys(&tokens, 6, None).unwrap();
|
||||
assert_eq!(keys_info.len(), 2);
|
||||
let first_key = match keys_info[0].key.clone() {
|
||||
@ -1743,7 +1730,7 @@ const key = 'c'"#,
|
||||
|
||||
#[test]
|
||||
fn test_make_literal_call_expression() {
|
||||
let tokens = lexer("log(5, \"hello\", aIdentifier)");
|
||||
let tokens = crate::tokeniser::lexer("log(5, \"hello\", aIdentifier)");
|
||||
let literal = make_literal(&tokens, 2).unwrap();
|
||||
assert_eq!(
|
||||
Literal {
|
||||
@ -1833,7 +1820,7 @@ const key = 'c'"#,
|
||||
#[test]
|
||||
fn test_next_meaningful_token() {
|
||||
let _offset = 1;
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
@ -2218,7 +2205,7 @@ const key = 'c'"#,
|
||||
|
||||
#[test]
|
||||
fn test_find_closing_brace() {
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
@ -2234,22 +2221,25 @@ const key = 'c'"#,
|
||||
assert_eq!(find_closing_brace(&tokens, 90, 0, "").unwrap(), 92);
|
||||
|
||||
let basic = "( hey )";
|
||||
assert_eq!(find_closing_brace(&lexer(basic), 0, 0, "").unwrap(), 4);
|
||||
assert_eq!(
|
||||
find_closing_brace(&crate::tokeniser::lexer(basic), 0, 0, "").unwrap(),
|
||||
4
|
||||
);
|
||||
|
||||
let handles_non_zero_index =
|
||||
"(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)";
|
||||
assert_eq!(
|
||||
find_closing_brace(&lexer(handles_non_zero_index), 2, 0, "").unwrap(),
|
||||
find_closing_brace(&crate::tokeniser::lexer(handles_non_zero_index), 2, 0, "").unwrap(),
|
||||
4
|
||||
);
|
||||
assert_eq!(
|
||||
find_closing_brace(&lexer(handles_non_zero_index), 0, 0, "").unwrap(),
|
||||
find_closing_brace(&crate::tokeniser::lexer(handles_non_zero_index), 0, 0, "").unwrap(),
|
||||
6
|
||||
);
|
||||
|
||||
let handles_nested = "{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }";
|
||||
assert_eq!(
|
||||
find_closing_brace(&lexer(handles_nested), 0, 0, "").unwrap(),
|
||||
find_closing_brace(&crate::tokeniser::lexer(handles_nested), 0, 0, "").unwrap(),
|
||||
18
|
||||
);
|
||||
|
||||
@ -2258,7 +2248,7 @@ const key = 'c'"#,
|
||||
|
||||
#[test]
|
||||
fn test_is_call_expression() {
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
@ -2278,7 +2268,7 @@ const key = 'c'"#,
|
||||
|
||||
#[test]
|
||||
fn test_find_next_declaration_keyword() {
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
@ -2295,7 +2285,7 @@ const key = 'c'"#,
|
||||
}
|
||||
);
|
||||
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const myVar = 5
|
||||
const newVar = myVar + 1
|
||||
"#,
|
||||
@ -2327,7 +2317,7 @@ const newVar = myVar + 1
|
||||
lineTo(2, 3)
|
||||
} |> rx(45, %)
|
||||
"#;
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
assert_eq!(
|
||||
has_pipe_operator(&tokens, 0, None).unwrap(),
|
||||
TokenReturnWithNonCode {
|
||||
@ -2349,7 +2339,7 @@ const newVar = myVar + 1
|
||||
lineTo(2, 3)
|
||||
} |> rx(45, %) |> rx(45, %)
|
||||
"#;
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
assert_eq!(
|
||||
has_pipe_operator(&tokens, 0, None).unwrap(),
|
||||
TokenReturnWithNonCode {
|
||||
@ -2374,7 +2364,7 @@ const newVar = myVar + 1
|
||||
const yo = myFunc(9()
|
||||
|> rx(45, %)
|
||||
"#;
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
assert_eq!(
|
||||
has_pipe_operator(&tokens, 0, None).unwrap(),
|
||||
TokenReturnWithNonCode {
|
||||
@ -2385,7 +2375,7 @@ const yo = myFunc(9()
|
||||
);
|
||||
|
||||
let code = "const myVar2 = 5 + 1 |> myFn(%)";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
assert_eq!(
|
||||
has_pipe_operator(&tokens, 1, None).unwrap(),
|
||||
TokenReturnWithNonCode {
|
||||
@ -2410,7 +2400,7 @@ const yo = myFunc(9()
|
||||
lineTo(1,1)
|
||||
} |> rx(90, %)
|
||||
show(mySk1)"#;
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let token_with_my_path_index = tokens
|
||||
.iter()
|
||||
.position(|token| token.value == "myPath")
|
||||
@ -2454,7 +2444,7 @@ show(mySk1)"#;
|
||||
|
||||
#[test]
|
||||
fn test_make_member_expression() {
|
||||
let tokens = lexer("const prop = yo.one[\"two\"]");
|
||||
let tokens = crate::tokeniser::lexer("const prop = yo.one[\"two\"]");
|
||||
let member_expression_return = make_member_expression(&tokens, 6).unwrap();
|
||||
let member_expression = member_expression_return.expression;
|
||||
let last_index = member_expression_return.last_index;
|
||||
@ -2495,12 +2485,12 @@ show(mySk1)"#;
|
||||
#[test]
|
||||
fn test_find_end_of_binary_expression() {
|
||||
let code = "1 + 2 * 3\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].value, "3");
|
||||
|
||||
let code = "(1 + 25) / 5 - 3\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].value, "3");
|
||||
let index_of_5 = code.find('5').unwrap();
|
||||
@ -2508,44 +2498,44 @@ show(mySk1)"#;
|
||||
assert_eq!(end_starting_at_the_5, end);
|
||||
// whole thing wraped
|
||||
let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].end, code.find("3)").unwrap() + 2);
|
||||
// whole thing wraped but given index after the first brace
|
||||
let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 1).unwrap();
|
||||
assert_eq!(tokens[end].value, "3");
|
||||
// given the index of a small wrapped section i.e. `1 + 2` in ((1 + 2) / 5 - 3)'
|
||||
let code = "((1 + 2) / 5 - 3)\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 2).unwrap();
|
||||
assert_eq!(tokens[end].value, "2");
|
||||
// lots of silly nesting
|
||||
let code = "(1 + 2) / (5 - (3))\nconst yo = 5";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].end, code.find("))").unwrap() + 2);
|
||||
// with pipe operator at the end
|
||||
let code = "(1 + 2) / (5 - (3))\n |> fn(%)";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].end, code.find("))").unwrap() + 2);
|
||||
// with call expression at the start of binary expression
|
||||
let code = "yo(2) + 3\n |> fn(%)";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(tokens[end].value, "3");
|
||||
// with call expression at the end of binary expression
|
||||
let code = "3 + yo(2)\n |> fn(%)";
|
||||
let tokens = lexer(code);
|
||||
let tokens = crate::tokeniser::lexer(code);
|
||||
let _end = find_end_of_binary_expression(&tokens, 0).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_make_array_expression() {
|
||||
// input_index: 6, output_index: 14, output: {"type":"ArrayExpression","start":11,"end":26,"elements":[{"type":"Literal","start":12,"end":15,"value":"1","raw":"\"1\""},{"type":"Literal","start":17,"end":18,"value":2,"raw":"2"},{"type":"Identifier","start":20,"end":25,"name":"three"}]}
|
||||
let tokens = lexer("const yo = [\"1\", 2, three]");
|
||||
let tokens = crate::tokeniser::lexer("const yo = [\"1\", 2, three]");
|
||||
let array_expression = make_array_expression(&tokens, 6).unwrap();
|
||||
let expression = array_expression.expression;
|
||||
assert_eq!(array_expression.last_index, 14);
|
||||
@ -2583,7 +2573,7 @@ show(mySk1)"#;
|
||||
|
||||
#[test]
|
||||
fn test_make_call_expression() {
|
||||
let tokens = lexer("foo(\"a\", a, 3)");
|
||||
let tokens = crate::tokeniser::lexer("foo(\"a\", a, 3)");
|
||||
let result = make_call_expression(&tokens, 0).unwrap();
|
||||
assert_eq!(result.last_index, 9);
|
||||
assert_eq!(result.expression.start, 0);
|
||||
@ -2616,7 +2606,7 @@ show(mySk1)"#;
|
||||
|
||||
#[test]
|
||||
fn test_make_variable_declaration() {
|
||||
let tokens = lexer(
|
||||
let tokens = crate::tokeniser::lexer(
|
||||
r#"const yo = startSketch([0, 0])
|
||||
|> lineTo([1, myVar], %)
|
||||
|> foo(myVar2, %)
|
||||
@ -2685,7 +2675,7 @@ show(mySk1)"#;
|
||||
|
||||
#[test]
|
||||
fn test_make_body() {
|
||||
let tokens = lexer("const myVar = 5");
|
||||
let tokens = crate::tokeniser::lexer("const myVar = 5");
|
||||
let body = make_body(
|
||||
&tokens,
|
||||
0,
|
||||
@ -2702,7 +2692,7 @@ show(mySk1)"#;
|
||||
#[test]
|
||||
fn test_abstract_syntax_tree() {
|
||||
let code = "5 +6";
|
||||
let result = abstract_syntax_tree(&lexer(code)).unwrap();
|
||||
let result = abstract_syntax_tree(&crate::tokeniser::lexer(code)).unwrap();
|
||||
let expected_result = Program {
|
||||
start: 0,
|
||||
end: 4,
|
@ -1,7 +1,5 @@
|
||||
//! Generates source code from the AST.
|
||||
//! The inverse of parsing (which generates an AST from the source code)
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::abstract_syntax_tree_types::{
|
||||
ArrayExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, FunctionExpression,
|
||||
@ -400,14 +398,3 @@ pub fn recast_function(expression: FunctionExpression) -> String {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for recast
|
||||
// test for this function and by extension the recaster are done in javascript land src/lang/recast.test.ts
|
||||
#[wasm_bindgen]
|
||||
pub fn recast_wasm(json_str: &str) -> Result<JsValue, JsError> {
|
||||
// deserialize the ast from a stringified json
|
||||
let program: Program = serde_json::from_str(json_str).map_err(JsError::from)?;
|
||||
|
||||
let result = recast(&program, "", false);
|
||||
Ok(JsValue::from_serde(&result)?)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
//! Functions implemented for language execution.
|
||||
|
||||
mod extrude;
|
||||
mod segment;
|
||||
mod sketch;
|
||||
mod utils;
|
||||
pub mod extrude;
|
||||
pub mod segment;
|
||||
pub mod sketch;
|
||||
pub mod utils;
|
||||
|
||||
// TODO: Something that would be nice is if we could generate docs for Kcl based on the
|
||||
// actual stdlib functions below.
|
||||
@ -663,7 +663,7 @@ mod tests {
|
||||
buf.push_str(&fn_docs);
|
||||
}
|
||||
|
||||
expectorate::assert_contents("../../docs/kcl.md", &buf);
|
||||
expectorate::assert_contents("../../../docs/kcl.md", &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -677,7 +677,7 @@ mod tests {
|
||||
}
|
||||
|
||||
expectorate::assert_contents(
|
||||
"../../docs/kcl.json",
|
||||
"../../../docs/kcl.json",
|
||||
&serde_json::to_string_pretty(&json_data).unwrap(),
|
||||
);
|
||||
}
|
@ -44,7 +44,7 @@ pub fn normalize_rad(angle: f64) -> f64 {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(delta_angle(std::f64::consts::PI/8.0, std::f64::consts::PI/4.0), std::f64::consts::PI/8.0);
|
||||
/// assert_eq!(kcl::std::utils::delta_angle(std::f64::consts::PI/8.0, std::f64::consts::PI/4.0), std::f64::consts::PI/8.0);
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
pub fn delta_angle(from_angle: f64, to_angle: f64) -> f64 {
|
||||
@ -69,8 +69,8 @@ pub fn delta_angle(from_angle: f64, to_angle: f64) -> f64 {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// assert_eq!(distance_between_points(&[0.0, 0.0], &[0.0, 5.0]), 5.0);
|
||||
/// assert_eq!(distance_between_points(&[0.0, 0.0], &[3.0, 4.0]), 5.0);
|
||||
/// assert_eq!(kcl::std::utils::distance_between_points(&[0.0, 0.0], &[0.0, 5.0]), 5.0);
|
||||
/// assert_eq!(kcl::std::utils::distance_between_points(&[0.0, 0.0], &[3.0, 4.0]), 5.0);
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
pub fn distance_between_points(point_a: &[f64; 2], point_b: &[f64; 2]) -> f64 {
|
@ -1,8 +1,6 @@
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize, ts_rs::TS)]
|
||||
#[ts(export)]
|
||||
@ -273,14 +271,6 @@ pub fn lexer(str: &str) -> Vec<Token> {
|
||||
recursively_tokenise(str, 0, Vec::new())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for lexer
|
||||
// test for this function and by extension lexer are done in javascript land src/lang/tokeniser.test.ts
|
||||
#[wasm_bindgen]
|
||||
pub fn lexer_js(str: &str) -> Result<JsValue, JsError> {
|
||||
let tokens = lexer(str);
|
||||
Ok(JsValue::from_serde(&tokens)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
@ -1,159 +0,0 @@
|
||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||
//! engine.
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use kittycad::types::{OkWebSocketResponseData, WebSocketRequest, WebSocketResponse};
|
||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||
|
||||
use crate::errors::{KclError, KclErrorDetails};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EngineConnection {
|
||||
tcp_write: futures::stream::SplitSink<
|
||||
tokio_tungstenite::WebSocketStream<
|
||||
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
||||
>,
|
||||
WsMsg,
|
||||
>,
|
||||
tcp_read_handle: tokio::task::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl Drop for EngineConnection {
|
||||
fn drop(&mut self) {
|
||||
// Drop the read handle.
|
||||
self.tcp_read_handle.abort();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpRead {
|
||||
stream: futures::stream::SplitStream<
|
||||
tokio_tungstenite::WebSocketStream<
|
||||
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl TcpRead {
|
||||
pub async fn read(&mut self) -> Result<WebSocketResponse> {
|
||||
let msg = self.stream.next().await.unwrap()?;
|
||||
let msg = match msg {
|
||||
WsMsg::Text(text) => text,
|
||||
WsMsg::Binary(bin) => bincode::deserialize(&bin)?,
|
||||
other => anyhow::bail!("Unexpected websocket message from server: {}", other),
|
||||
};
|
||||
let msg = serde_json::from_str::<WebSocketResponse>(&msg)?;
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
pub async fn new(conn_str: &str, auth_token: &str, origin: &str) -> Result<EngineConnection> {
|
||||
let method = http::Method::GET.to_string();
|
||||
let key = tokio_tungstenite::tungstenite::handshake::client::generate_key();
|
||||
|
||||
// Establish a websocket connection.
|
||||
let (ws_stream, _) = tokio_tungstenite::connect_async(httparse::Request {
|
||||
method: Some(&method),
|
||||
path: Some(conn_str),
|
||||
// TODO pass in the origin from elsewhere.
|
||||
headers: &mut websocket_headers(auth_token, &key, origin),
|
||||
version: Some(1), // HTTP/1.1
|
||||
})
|
||||
.await?;
|
||||
|
||||
let (tcp_write, tcp_read) = ws_stream.split();
|
||||
|
||||
let mut tcp_read = TcpRead { stream: tcp_read };
|
||||
|
||||
let tcp_read_handle = tokio::spawn(async move {
|
||||
// Get Websocket messages from API server
|
||||
while let Ok(ws_resp) = tcp_read.read().await {
|
||||
if !ws_resp.success {
|
||||
println!("got ws errors: {:?}", ws_resp.errors);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(msg) = ws_resp.resp {
|
||||
match msg {
|
||||
OkWebSocketResponseData::IceServerInfo { ice_servers } => {
|
||||
println!("got ice server info: {:?}", ice_servers);
|
||||
}
|
||||
OkWebSocketResponseData::SdpAnswer { answer } => {
|
||||
println!("got sdp answer: {:?}", answer);
|
||||
}
|
||||
OkWebSocketResponseData::TrickleIce { candidate } => {
|
||||
println!("got trickle ice: {:?}", candidate);
|
||||
}
|
||||
OkWebSocketResponseData::Modeling { .. } => {}
|
||||
OkWebSocketResponseData::Export { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(EngineConnection {
|
||||
tcp_write,
|
||||
tcp_read_handle,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn tcp_send(&mut self, msg: WebSocketRequest) -> Result<()> {
|
||||
let msg = serde_json::to_string(&msg)?;
|
||||
self.tcp_write.send(WsMsg::Text(msg)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_modeling_cmd(
|
||||
&mut self,
|
||||
id: uuid::Uuid,
|
||||
source_range: crate::executor::SourceRange,
|
||||
cmd: kittycad::types::ModelingCmd,
|
||||
) -> Result<(), KclError> {
|
||||
futures::executor::block_on(
|
||||
self.tcp_send(WebSocketRequest::ModelingCmdReq { cmd, cmd_id: id }),
|
||||
)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to send modeling command: {}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Headers for starting a websocket session with api-deux.
|
||||
fn websocket_headers<'a>(
|
||||
token: &'a str,
|
||||
key: &'a str,
|
||||
origin: &'a str,
|
||||
) -> [httparse::Header<'a>; 6] {
|
||||
[
|
||||
httparse::Header {
|
||||
name: "Authorization",
|
||||
value: token.as_bytes(),
|
||||
},
|
||||
httparse::Header {
|
||||
name: "Connection",
|
||||
value: b"Upgrade",
|
||||
},
|
||||
httparse::Header {
|
||||
name: "Upgrade",
|
||||
value: b"websocket",
|
||||
},
|
||||
httparse::Header {
|
||||
name: "Sec-WebSocket-Version",
|
||||
value: b"13",
|
||||
},
|
||||
httparse::Header {
|
||||
name: "Sec-WebSocket-Key",
|
||||
value: key.as_bytes(),
|
||||
},
|
||||
httparse::Header {
|
||||
name: "Host",
|
||||
value: origin.as_bytes(),
|
||||
},
|
||||
]
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
//! Functions for exported files from the server.
|
||||
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn deserialize_files(data: &[u8]) -> Result<JsValue, JsError> {
|
||||
let ws_resp: kittycad::types::WebSocketResponse = bincode::deserialize(data)?;
|
||||
|
||||
if !ws_resp.success {
|
||||
return Err(JsError::new(&format!(
|
||||
"Server returned error: {:?}",
|
||||
ws_resp.errors
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(kittycad::types::OkWebSocketResponseData::Export { files }) = ws_resp.resp {
|
||||
return Ok(JsValue::from_serde(&files)?);
|
||||
}
|
||||
|
||||
Err(JsError::new(&format!(
|
||||
"Invalid response type, got: {:?}",
|
||||
ws_resp
|
||||
)))
|
||||
}
|
@ -1,11 +1,84 @@
|
||||
mod abstract_syntax_tree_types;
|
||||
mod docs;
|
||||
mod engine;
|
||||
mod errors;
|
||||
mod executor;
|
||||
mod export;
|
||||
mod math_parser;
|
||||
mod parser;
|
||||
mod recast;
|
||||
mod std;
|
||||
mod tokeniser;
|
||||
//! Wasm bindings for `kcl`.
|
||||
|
||||
use gloo_utils::format::JsValueSerdeExt;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub async fn execute_wasm(
|
||||
program_str: &str,
|
||||
memory_str: &str,
|
||||
manager: kcl::engine::conn_wasm::EngineCommandManager,
|
||||
) -> Result<JsValue, String> {
|
||||
// deserialize the ast from a stringified json
|
||||
let program: kcl::abstract_syntax_tree_types::Program =
|
||||
serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||
let mut mem: kcl::executor::ProgramMemory =
|
||||
serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut engine = kcl::engine::EngineConnection::new(manager)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let memory = kcl::executor::execute(
|
||||
program,
|
||||
&mut mem,
|
||||
kcl::executor::BodyType::Root,
|
||||
&mut engine,
|
||||
)
|
||||
.map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
// gloo-serialize crate instead.
|
||||
JsValue::from_serde(&memory).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn deserialize_files(data: &[u8]) -> Result<JsValue, JsError> {
|
||||
let ws_resp: kittycad::types::WebSocketResponse = bson::from_slice(data)?;
|
||||
|
||||
if !ws_resp.success {
|
||||
return Err(JsError::new(&format!(
|
||||
"Server returned error: {:?}",
|
||||
ws_resp.errors
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(kittycad::types::OkWebSocketResponseData::Export { files }) = ws_resp.resp {
|
||||
return Ok(JsValue::from_serde(&files)?);
|
||||
}
|
||||
|
||||
Err(JsError::new(&format!(
|
||||
"Invalid response type, got: {:?}",
|
||||
ws_resp
|
||||
)))
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for lexer
|
||||
// test for this function and by extension lexer are done in javascript land src/lang/tokeniser.test.ts
|
||||
#[wasm_bindgen]
|
||||
pub fn lexer_js(js: &str) -> Result<JsValue, JsError> {
|
||||
let tokens = kcl::tokeniser::lexer(js);
|
||||
Ok(JsValue::from_serde(&tokens)?)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_js(js: &str) -> Result<JsValue, String> {
|
||||
let tokens = kcl::tokeniser::lexer(js);
|
||||
let program = kcl::parser::abstract_syntax_tree(&tokens).map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
// gloo-serialize crate instead.
|
||||
JsValue::from_serde(&program).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for recast
|
||||
// test for this function and by extension the recaster are done in javascript land src/lang/recast.test.ts
|
||||
#[wasm_bindgen]
|
||||
pub fn recast_wasm(json_str: &str) -> Result<JsValue, JsError> {
|
||||
// deserialize the ast from a stringified json
|
||||
let program: kcl::abstract_syntax_tree_types::Program =
|
||||
serde_json::from_str(json_str).map_err(JsError::from)?;
|
||||
|
||||
let result = kcl::recast::recast(&program, "", false);
|
||||
Ok(JsValue::from_serde(&result)?)
|
||||
}
|
||||
|
Reference in New Issue
Block a user