Compare commits

...

10 Commits

Author SHA1 Message Date
d11d363f19 Cut release v0.16.0 (#1763) 2024-03-20 08:44:09 -04:00
f22ad7c4e7 Fix file route resolution to restor file switching (#1768)
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-03-20 11:39:57 +11:00
1913519f68 Revert "Add ping pong health, remove a timeout interval, fix up netwo… (#1771)
Revert "Add ping pong health, remove a timeout interval, fix up network events (#1555)"

This reverts commit 61d7950ca3.

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-03-20 11:39:49 +11:00
4b9d4fd45b Bump mio in fuzz tests (#1773) 2024-03-19 19:30:47 -05:00
78e6816b06 Always run cargo clippy in CI (#1772)
It's now a required job before merge is allowed. Unfortunately GitHub now blocks any non-Rust PR, because they require cargo clippy but don't trigger it to run.

Solution is simple, just always run cargo clippy, so it can pass, so that merge is allowed.
2024-03-20 11:02:49 +11:00
6607ea1663 Bump tauri-plugin-fs-extra from d95a1b3 to 6db4320 in /src-tauri (#1756)
Bumps [tauri-plugin-fs-extra](https://github.com/tauri-apps/plugins-workspace) from `d95a1b3` to `6db4320`.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](d95a1b382f...6db4320e98)

---
updated-dependencies:
- dependency-name: tauri-plugin-fs-extra
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-19 15:53:07 -07:00
644a8ef3ca Remove cargo build CI job (#1767)
We shouldn't actually need `cargo build` CI checks. Because we're not building any binaries. Just `cargo check` should be enough. WASM builds are tested elsewhere.
2024-03-19 21:38:21 +00:00
e3e132c0d5 Fix cargo warnings (#1766)
CI is currently broken [logs here](https://github.com/KittyCAD/modeling-app/actions/runs/8349085118/job/22852456873?pr=1765#step:9:1046). Trying to fix.

OK I've fixed it. Process to fix was:

1. Revert be3fed8427 ("Add support for line, xLine, yLine, xLineTo, yLineTo (#1754)")
2. Restore that commit without any of its changes to Cargo.lock (it had, IMO, a lot of unnecessary changes)
3. `cargo update -p kittycad-execution-plan` (redoing only the necessary changes)
2024-03-19 21:19:57 +00:00
be3fed8427 Add support for line, xLine, yLine, xLineTo, yLineTo (#1754)
* Add support for line, xLine, yLine, xLineTo, yLineTo

* Fix minor memory misalignment

* Address PR comments
2024-03-19 12:11:45 -04:00
cefa6f85fe tag changes followup (#1747)
* tag changes followup

* fmt
2024-03-17 18:24:03 +11:00
24 changed files with 893 additions and 504 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey
skip: **/target,node_modules,build,**/Cargo.lock

View File

@ -1,50 +0,0 @@
on:
push:
branches:
- main
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-build.yml
pull_request:
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo build
jobs:
cargobuild:
name: cargo build
runs-on: ubuntu-latest
strategy:
matrix:
dir: ['src/wasm-lib']
steps:
- uses: actions/checkout@v4
- name: Install latest rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: install dependencies
if: matrix.dir == 'src-tauri'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: Run cargo build
run: |
cd "${{ matrix.dir }}"
cargo build --all
shell: bash

View File

@ -9,12 +9,6 @@ on:
- '**.rs'
- .github/workflows/cargo-clippy.yml
pull_request:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- '**.rs'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.15.6",
"version": "0.16.0",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.10.2",

2
src-tauri/Cargo.lock generated
View File

@ -3876,7 +3876,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs-extra"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#d95a1b382f512a0be748e7a89e0f8fc4278010e2"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#6db4320e98a4278d605dd05d4d87e1433939a7cd"
dependencies = [
"log",
"serde",

View File

@ -7,7 +7,7 @@
},
"package": {
"productName": "zoo-modeling-app",
"version": "0.15.6"
"version": "0.16.0"
},
"tauri": {
"allowlist": {

View File

@ -59,6 +59,7 @@ const router = createBrowserRouter([
{
loader: fileLoader,
id: paths.FILE,
path: paths.FILE + '/:id',
element: (
<Auth>
<FileMachineProvider>
@ -74,8 +75,11 @@ const router = createBrowserRouter([
),
children: [
{
path: paths.FILE + '/:id',
loader: onboardingRedirectLoader,
index: true,
element: <></>,
},
{
children: [
{
path: makeUrlPathRelative(paths.SETTINGS),

View File

@ -30,7 +30,7 @@ describe('NetworkHealthIndicator tests', () => {
fireEvent.click(screen.getByTestId('network-toggle'))
expect(screen.getByTestId('network')).toHaveTextContent(
NETWORK_HEALTH_TEXT[NetworkHealthState.Issue]
NETWORK_HEALTH_TEXT[NetworkHealthState.Ok]
)
})

View File

@ -6,8 +6,7 @@ import {
ConnectingTypeGroup,
DisconnectingType,
engineCommandManager,
EngineCommandManagerEvents,
EngineConnectionEvents,
EngineConnectionState,
EngineConnectionStateType,
ErrorType,
initialConnectingTypeGroupState,
@ -82,35 +81,37 @@ const overallConnectionStateIcon: Record<
}
export function useNetworkStatus() {
const [steps, setSteps] = useState(
structuredClone(initialConnectingTypeGroupState)
)
const [steps, setSteps] = useState(initialConnectingTypeGroupState)
const [internetConnected, setInternetConnected] = useState<boolean>(true)
const [overallState, setOverallState] = useState<NetworkHealthState>(
NetworkHealthState.Disconnected
NetworkHealthState.Ok
)
const [pingPongHealth, setPingPongHealth] = useState<'OK' | 'BAD'>('BAD')
const [hasCopied, setHasCopied] = useState<boolean>(false)
const [error, setError] = useState<ErrorType | undefined>(undefined)
const hasIssue = (i: [ConnectingType, boolean | undefined]) =>
i[1] === undefined ? i[1] : !i[1]
const issues: Record<ConnectingTypeGroup, boolean> = {
[ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
}
const [issues, setIssues] = useState<
Record<ConnectingTypeGroup, boolean | undefined>
>({
[ConnectingTypeGroup.WebSocket]: undefined,
[ConnectingTypeGroup.ICE]: undefined,
[ConnectingTypeGroup.WebRTC]: undefined,
})
const hasIssues: boolean =
issues[ConnectingTypeGroup.WebSocket] ||
issues[ConnectingTypeGroup.ICE] ||
issues[ConnectingTypeGroup.WebRTC]
const [hasIssues, setHasIssues] = useState<boolean | undefined>(undefined)
useEffect(() => {
setOverallState(
!internetConnected
? NetworkHealthState.Disconnected
: hasIssues || hasIssues === undefined
: hasIssues
? NetworkHealthState.Issue
: NetworkHealthState.Ok
)
@ -133,59 +134,19 @@ export function useNetworkStatus() {
}, [])
useEffect(() => {
console.log(pingPongHealth)
}, [pingPongHealth])
useEffect(() => {
const issues = {
[ConnectingTypeGroup.WebSocket]: steps[
ConnectingTypeGroup.WebSocket
].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
}
setIssues(issues)
}, [steps])
useEffect(() => {
setHasIssues(
issues[ConnectingTypeGroup.WebSocket] ||
issues[ConnectingTypeGroup.ICE] ||
issues[ConnectingTypeGroup.WebRTC]
)
}, [issues])
useEffect(() => {
const onPingPongChange = ({ detail: state }: CustomEvent) => {
setPingPongHealth(state)
}
const onConnectionStateChange = ({
detail: engineConnectionState,
}: CustomEvent) => {
setSteps((steps) => {
let nextSteps = structuredClone(steps)
engineCommandManager.onConnectionStateChange(
(engineConnectionState: EngineConnectionState) => {
let hasSetAStep = false
if (
engineConnectionState.type === EngineConnectionStateType.Connecting
) {
const groups = Object.values(nextSteps)
const groups = Object.values(steps)
for (let group of groups) {
for (let step of group) {
if (step[0] !== engineConnectionState.value.type) continue
step[1] = true
hasSetAStep = true
}
}
}
@ -193,7 +154,7 @@ export function useNetworkStatus() {
if (
engineConnectionState.type === EngineConnectionStateType.Disconnecting
) {
const groups = Object.values(nextSteps)
const groups = Object.values(steps)
for (let group of groups) {
for (let step of group) {
if (
@ -204,6 +165,7 @@ export function useNetworkStatus() {
?.type === step[0]
) {
step[1] = false
hasSetAStep = true
}
}
}
@ -214,50 +176,11 @@ export function useNetworkStatus() {
}
}
// Reset the state of all steps if we have disconnected.
if (
engineConnectionState.type === EngineConnectionStateType.Disconnected
) {
return structuredClone(initialConnectingTypeGroupState)
if (hasSetAStep) {
setSteps(steps)
}
return nextSteps
})
}
const onEngineAvailable = ({ detail: engineConnection }: CustomEvent) => {
engineConnection.addEventListener(
EngineConnectionEvents.PingPongChanged,
onPingPongChange as EventListener
)
engineConnection.addEventListener(
EngineConnectionEvents.ConnectionStateChanged,
onConnectionStateChange as EventListener
)
}
engineCommandManager.addEventListener(
EngineCommandManagerEvents.EngineAvailable,
onEngineAvailable as EventListener
}
)
return () => {
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.EngineAvailable,
onEngineAvailable as EventListener
)
// When the component is unmounted these should be assigned, but it's possible
// the component mounts and unmounts before engine is available.
engineCommandManager.engineConnection?.addEventListener(
EngineConnectionEvents.PingPongChanged,
onPingPongChange as EventListener
)
engineCommandManager.engineConnection?.addEventListener(
EngineConnectionEvents.ConnectionStateChanged,
onConnectionStateChange as EventListener
)
}
}, [])
return {
@ -269,7 +192,6 @@ export function useNetworkStatus() {
error,
setHasCopied,
hasCopied,
pingPongHealth,
}
}
@ -334,18 +256,18 @@ export const NetworkHealthIndicator = () => {
size="lg"
icon={
hasIssueToIcon[
String(issues[name as ConnectingTypeGroup])
issues[name as ConnectingTypeGroup].toString()
]
}
iconClassName={
hasIssueToIconColors[
String(issues[name as ConnectingTypeGroup])
issues[name as ConnectingTypeGroup].toString()
].icon
}
bgClassName={
'rounded-sm ' +
hasIssueToIconColors[
String(issues[name as ConnectingTypeGroup])
issues[name as ConnectingTypeGroup].toString()
].bg
}
/>

View File

@ -32,7 +32,6 @@ export const Stream = ({ className = '' }: { className?: string }) => {
const { state } = useModelingContext()
const { isExecuting } = useKclContext()
const { overallState } = useNetworkStatus()
const isNetworkOkay = overallState === NetworkHealthState.Ok
useEffect(() => {

View File

@ -1,5 +1,5 @@
import { PathToNode, Program, SourceRange } from 'lang/wasm'
import { VITE_KC_API_WS_MODELING_URL } from 'env'
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid'
@ -8,9 +8,6 @@ import { sceneInfra } from 'clientSideScene/sceneInfra'
let lastMessage = ''
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000
interface CommandInfo {
commandType: CommandTypes
range: SourceRange
@ -40,6 +37,11 @@ export interface ArtifactMap {
[key: string]: ResultCommand | PendingCommand | FailedCommand
}
interface NewTrackArgs {
conn: EngineConnection
mediaStream: MediaStream
}
// This looks funny, I know. This is needed because node and the browser
// disagree as to the type. In a browser it's a number, but in node it's a
// "Timeout".
@ -156,28 +158,10 @@ export type EngineConnectionState =
| State<EngineConnectionStateType.Disconnecting, DisconnectingValue>
| State<EngineConnectionStateType.Disconnected, void>
export type PingPongState = 'OK' | 'BAD'
export enum EngineConnectionEvents {
// Fires for each ping-pong success or failure.
PingPongChanged = 'ping-pong-changed', // (state: PingPongState) => void
// For now, this is only used by the NetworkHealthIndicator.
// We can eventually use it for more, but one step at a time.
ConnectionStateChanged = 'connection-state-changed', // (state: EngineConnectionState) => void
// These are used for the EngineCommandManager and were created
// before onConnectionStateChange existed.
ConnectionStarted = 'connection-started', // (engineConnection: EngineConnection) => void
Opened = 'opened', // (engineConnection: EngineConnection) => void
Closed = 'closed', // (engineConnection: EngineConnection) => void
NewTrack = 'new-track', // (track: NewTrackArgs) => void
}
// EngineConnection encapsulates the connection(s) to the Engine
// for the EngineCommandManager; namely, the underlying WebSocket
// and WebRTC connections.
class EngineConnection extends EventTarget {
class EngineConnection {
websocket?: WebSocket
pc?: RTCPeerConnection
unreliableDataChannel?: RTCDataChannel
@ -211,12 +195,7 @@ class EngineConnection extends EventTarget {
}
}
this._state = next
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.ConnectionStateChanged, {
detail: this._state,
})
)
this.onConnectionStateChange(this._state)
}
private failedConnTimeout: Timeout | null
@ -224,39 +203,74 @@ class EngineConnection extends EventTarget {
readonly url: string
private readonly token?: string
// For now, this is only used by the NetworkHealthIndicator.
// We can eventually use it for more, but one step at a time.
private onConnectionStateChange: (state: EngineConnectionState) => void
// These are used for the EngineCommandManager and were created
// before onConnectionStateChange existed.
private onEngineConnectionOpen: (engineConnection: EngineConnection) => void
private onConnectionStarted: (engineConnection: EngineConnection) => void
private onClose: (engineConnection: EngineConnection) => void
private onNewTrack: (track: NewTrackArgs) => void
// TODO: actual type is ClientMetrics
private webrtcStatsCollector?: () => Promise<ClientMetrics>
private pingPongSpan: { ping?: Date; pong?: Date }
constructor({ url, token }: { url: string; token?: string }) {
super()
constructor({
url,
token,
onConnectionStateChange = () => {},
onNewTrack = () => {},
onEngineConnectionOpen = () => {},
onConnectionStarted = () => {},
onClose = () => {},
}: {
url: string
token?: string
onConnectionStateChange?: (state: EngineConnectionState) => void
onEngineConnectionOpen?: (engineConnection: EngineConnection) => void
onConnectionStarted?: (engineConnection: EngineConnection) => void
onClose?: (engineConnection: EngineConnection) => void
onNewTrack?: (track: NewTrackArgs) => void
}) {
this.url = url
this.token = token
this.failedConnTimeout = null
this.onConnectionStateChange = onConnectionStateChange
this.onEngineConnectionOpen = onEngineConnectionOpen
this.onConnectionStarted = onConnectionStarted
this.pingPongSpan = { ping: undefined, pong: undefined }
this.onClose = onClose
this.onNewTrack = onNewTrack
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000
// Without an interval ping, our connection will timeout.
setInterval(() => {
let pingInterval = setInterval(() => {
switch (this.state.type as EngineConnectionStateType) {
case EngineConnectionStateType.ConnectionEstablished:
this.send({ type: 'ping' })
this.pingPongSpan.ping = new Date()
break
case EngineConnectionStateType.Disconnecting:
case EngineConnectionStateType.Disconnected:
// Reconnect if we have disconnected.
if (!this.isConnecting()) this.connect()
clearInterval(pingInterval)
break
default:
if (this.isConnecting()) break
// Means we never could do an initial connection. Reconnect everything.
if (!this.pingPongSpan.ping) this.connect()
break
}
}, pingIntervalMs)
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
let connectRetryInterval = setInterval(() => {
if (this.state.type !== EngineConnectionStateType.Disconnected) return
// Only try reconnecting when completely disconnected.
clearInterval(connectRetryInterval)
console.log('Trying to reconnect')
this.connect()
}, connectionTimeoutMs)
}
isConnecting() {
@ -338,11 +352,7 @@ class EngineConnection extends EventTarget {
// dance is it safest to connect the video tracks / stream
case 'connected':
// Let the browser attach to the video stream now
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.NewTrack, {
detail: { conn: this, mediaStream: this.mediaStream! },
})
)
this.onNewTrack({ conn: this, mediaStream: this.mediaStream! })
break
case 'failed':
this.disconnectAll()
@ -458,9 +468,7 @@ class EngineConnection extends EventTarget {
// Everything is now connected.
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.Opened, { detail: this })
)
this.onEngineConnectionOpen(this)
})
this.unreliableDataChannel.addEventListener('close', (event) => {
@ -502,10 +510,6 @@ class EngineConnection extends EventTarget {
},
}
// Send an initial ping
this.send({ type: 'ping' })
this.pingPongSpan.ping = new Date()
// This is required for when KCMA is running stand-alone / within Tauri.
// Otherwise when run in a browser, the token is sent implicitly via
// the Cookie header.
@ -571,34 +575,12 @@ failed cmd type was ${artifactThatFailed?.commandType}`
let resp = message.resp
// If there's no body to the response, we can bail here.
// !resp.type is usually "pong" response for our "ping"
if (!resp || !resp.type) {
return
}
switch (resp.type) {
case 'pong':
this.pingPongSpan.pong = new Date()
if (this.pingPongSpan.ping && this.pingPongSpan.pong) {
if (
Math.abs(
this.pingPongSpan.pong.valueOf() -
this.pingPongSpan.ping.valueOf()
) >= pingIntervalMs
) {
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
detail: 'BAD',
})
)
} else {
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
detail: 'OK',
})
)
}
}
break
case 'ice_server_info':
let ice_servers = resp.data?.ice_servers
@ -745,11 +727,27 @@ failed cmd type was ${artifactThatFailed?.commandType}`
}
})
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.ConnectionStarted, {
detail: this,
})
)
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
if (this.failedConnTimeout) {
clearTimeout(this.failedConnTimeout)
this.failedConnTimeout = null
}
this.failedConnTimeout = setTimeout(() => {
if (this.isReady()) {
return
}
this.failedConnTimeout = null
this.state = {
type: EngineConnectionStateType.Disconnecting,
value: {
type: DisconnectingType.Timeout,
},
}
this.disconnectAll()
this.finalizeIfAllConnectionsClosed()
}, connectionTimeoutMs)
this.onConnectionStarted(this)
}
unreliableSend(message: object | string) {
// TODO(paultag): Add in logic to determine the connection state and
@ -798,8 +796,6 @@ interface UnreliableSubscription<T extends UnreliableResponses['type']> {
callback: (data: Extract<UnreliableResponses, { type: T }>) => void
}
// TODO: Should eventually be replaced with native EventTarget event system,
// as it manages events in a more familiar way to other developers.
export interface Subscription<T extends ModelTypes> {
event: T
callback: (
@ -827,11 +823,7 @@ export type CommandLog =
data: null
}
export enum EngineCommandManagerEvents {
EngineAvailable = 'engine-available',
}
export class EngineCommandManager extends EventTarget {
export class EngineCommandManager {
artifactMap: ArtifactMap = {}
lastArtifactMap: ArtifactMap = {}
sceneCommandArtifacts: ArtifactMap = {}
@ -865,9 +857,10 @@ export class EngineCommandManager extends EventTarget {
}
} = {} as any
constructor() {
super()
callbacksEngineStateConnection: ((state: EngineConnectionState) => void)[] =
[]
constructor() {
this.engineConnection = undefined
;(async () => {
// circular dependency needs one to be lazy loaded
@ -908,17 +901,12 @@ export class EngineCommandManager extends EventTarget {
this.engineConnection = new EngineConnection({
url,
token,
})
this.dispatchEvent(
new CustomEvent(EngineCommandManagerEvents.EngineAvailable, {
detail: this.engineConnection,
})
)
this.engineConnection.addEventListener(
EngineConnectionEvents.Opened,
() => {
onConnectionStateChange: (state: EngineConnectionState) => {
for (let cb of this.callbacksEngineStateConnection) {
cb(state)
}
},
onEngineConnectionOpen: () => {
// Make the axis gizmo.
// We do this after the connection opened to avoid a race condition.
// Connected opened is the last thing that happens when the stream
@ -953,98 +941,78 @@ export class EngineCommandManager extends EventTarget {
setIsStreamReady(true)
executeCode(undefined, true)
})
}
)
this.engineConnection.addEventListener(
EngineConnectionEvents.Closed,
() => {
},
onClose: () => {
setIsStreamReady(false)
}
)
},
onConnectionStarted: (engineConnection) => {
engineConnection?.pc?.addEventListener('datachannel', (event) => {
let unreliableDataChannel = event.channel
this.engineConnection.addEventListener(
EngineConnectionEvents.ConnectionStarted,
(({ detail: engineConnection }: CustomEvent) => {
engineConnection?.pc?.addEventListener(
'datachannel',
(event: RTCDataChannelEvent) => {
let unreliableDataChannel = event.channel
unreliableDataChannel.addEventListener(
'message',
(event: MessageEvent) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result?.data?.sequence &&
result?.data.sequence > this.inSequence &&
result.type === 'highlight_set_entity'
) {
this.inSequence = result.data.sequence
callback(result)
}
}
)
unreliableDataChannel.addEventListener('message', (event) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result?.data?.sequence &&
result?.data.sequence > this.inSequence &&
result.type === 'highlight_set_entity'
) {
this.inSequence = result.data.sequence
callback(result)
}
}
)
}
)
})
})
// When the EngineConnection starts a connection, we want to register
// callbacks into the WebSocket/PeerConnection.
engineConnection.websocket?.addEventListener(
'message',
(event: MessageEvent) => {
if (event.data instanceof ArrayBuffer) {
// If the data is an ArrayBuffer, it's the result of an export command,
// because in all other cases we send JSON strings. But in the case of
// export we send a binary blob.
// Pass this to our export function.
void exportSave(event.data)
} else {
const message: Models['WebSocketResponse_type'] = JSON.parse(
event.data
)
if (
message.success &&
message.resp.type === 'modeling' &&
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
} else if (
!message.success &&
message.request_id &&
this.artifactMap[message.request_id]
) {
this.handleFailedModelingCommand(message)
}
engineConnection.websocket?.addEventListener('message', (event) => {
if (event.data instanceof ArrayBuffer) {
// If the data is an ArrayBuffer, it's the result of an export command,
// because in all other cases we send JSON strings. But in the case of
// export we send a binary blob.
// Pass this to our export function.
void exportSave(event.data)
} else {
const message: Models['WebSocketResponse_type'] = JSON.parse(
event.data
)
if (
message.success &&
message.resp.type === 'modeling' &&
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
} else if (
!message.success &&
message.request_id &&
this.artifactMap[message.request_id]
) {
this.handleFailedModelingCommand(message)
}
}
)
}) as EventListener
)
})
},
onNewTrack: ({ mediaStream }) => {
console.log('received track', mediaStream)
this.engineConnection.addEventListener(EngineConnectionEvents.NewTrack, (({
detail: { mediaStream },
}: CustomEvent) => {
console.log('received track', mediaStream)
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
console.log('peer is not sending video to us')
// this.engineConnection?.close()
// this.engineConnection?.connect()
})
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
console.log('peer is not sending video to us')
// this.engineConnection?.close()
// this.engineConnection?.connect()
})
setMediaStream(mediaStream)
}) as EventListener)
setMediaStream(mediaStream)
},
})
this.engineConnection?.connect()
}
@ -1234,6 +1202,9 @@ export class EngineCommandManager extends EventTarget {
) {
delete this.unreliableSubscriptions[event][id]
}
onConnectionStateChange(callback: (state: EngineConnectionState) => void) {
this.callbacksEngineStateConnection.push(callback)
}
endSession() {
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
// we need to loop over them each individually because if the engine doesn't recognise a single

View File

@ -149,7 +149,7 @@ export const lineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('default'),
addTag: addTag(),
}
export const line: SketchLineHelper = {
@ -240,7 +240,7 @@ export const line: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('default'),
addTag: addTag(),
}
export const xLineTo: SketchLineHelper = {
@ -288,7 +288,7 @@ export const xLineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('default'),
addTag: addTag(),
}
export const yLineTo: SketchLineHelper = {
@ -336,7 +336,7 @@ export const yLineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('default'),
addTag: addTag(),
}
export const xLine: SketchLineHelper = {
@ -386,7 +386,7 @@ export const xLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('length'),
addTag: addTag(),
}
export const yLine: SketchLineHelper = {
@ -430,7 +430,7 @@ export const yLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('length'),
addTag: addTag(),
}
export const tangentialArcTo: SketchLineHelper = {
@ -510,7 +510,7 @@ export const tangentialArcTo: SketchLineHelper = {
}
},
// TODO copy-paste from angledLine
addTag: addTagWithTo('angleLength'),
addTag: addTag(),
}
export const angledLine: SketchLineHelper = {
add: ({
@ -576,7 +576,7 @@ export const angledLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleLength'),
addTag: addTag(),
}
export const angledLineOfXLength: SketchLineHelper = {
@ -649,7 +649,7 @@ export const angledLineOfXLength: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleLength'),
addTag: addTag(),
}
export const angledLineOfYLength: SketchLineHelper = {
@ -723,7 +723,7 @@ export const angledLineOfYLength: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleLength'),
addTag: addTag(),
}
export const angledLineToX: SketchLineHelper = {
@ -792,7 +792,7 @@ export const angledLineToX: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleTo'),
addTag: addTag(),
}
export const angledLineToY: SketchLineHelper = {
@ -862,7 +862,7 @@ export const angledLineToY: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleTo'),
addTag: addTag(),
}
export const angledLineThatIntersects: SketchLineHelper = {
@ -951,7 +951,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
pathToNode,
}
},
addTag: addTagWithTo('angleTo'), // TODO might be wrong
addTag: addTag(), // TODO might be wrong
}
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
@ -1174,29 +1174,31 @@ function isAngleLiteral(lineArugement: Value): boolean {
type addTagFn = (a: ModifyAstBase) => { modifiedAst: Program; tag: string }
function addTagWithTo(
argType: 'angleLength' | 'angleTo' | 'default' | 'length'
): addTagFn {
function addTag(): addTagFn {
return ({ node, pathToNode }) => {
let tagName = findUniqueName(node, 'seg', 2)
const _node = { ...node }
const { node: callExpression } = getNodeFromPath<CallExpression>(
const { node: primaryCallExp } = getNodeFromPath<CallExpression>(
_node,
pathToNode
pathToNode,
'CallExpression'
)
const tagArg = callExpression.arguments?.[2]
if (tagArg) {
// Tag is always 3rd expression now, using arg index feels brittle
// but we can come up with a better way to identify tag later.
const thirdArg = primaryCallExp.arguments?.[2]
const tagLiteral =
thirdArg || (createLiteral(findUniqueName(_node, 'seg', 2)) as Literal)
const isTagExisting = !!thirdArg
if (!isTagExisting) {
primaryCallExp.arguments[2] = tagLiteral
}
if ('value' in tagLiteral) {
// Now TypeScript knows tagLiteral has a value property
return {
modifiedAst: _node,
tag: String(tagArg),
tag: String(tagLiteral.value),
}
} else {
callExpression.arguments[2] = createLiteral(tagName)
return {
modifiedAst: _node,
tag: tagName,
}
throw new Error('Unable to assign tag without value')
}
}
}

View File

@ -1471,6 +1471,7 @@ dependencies = [
name = "grackle"
version = "0.1.0"
dependencies = [
"image",
"kcl-lib",
"kittycad",
"kittycad-execution-plan",
@ -1482,6 +1483,7 @@ dependencies = [
"serde_json",
"thiserror",
"tokio",
"twenty-twenty",
"uuid",
]
@ -2007,7 +2009,7 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan"
version = "0.1.0"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"bytes",
"insta",
@ -2027,7 +2029,7 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan-macros"
version = "0.1.9"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"proc-macro2",
"quote",
@ -2037,7 +2039,7 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan-traits"
version = "0.1.13"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"serde",
"thiserror",
@ -2046,8 +2048,8 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.1.30"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
version = "0.1.32"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"anyhow",
"chrono",
@ -2075,7 +2077,7 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds-macros"
version = "0.1.2"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"proc-macro2",
"quote",
@ -2085,11 +2087,12 @@ dependencies = [
[[package]]
name = "kittycad-modeling-session"
version = "0.1.1"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#4dfeb5c9ce2cc3fb853dd14cf948a922f3724ef4"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"futures",
"kittycad",
"kittycad-modeling-cmds",
"lsystem",
"reqwest",
"serde_json",
"thiserror",
@ -2187,6 +2190,12 @@ dependencies = [
"url",
]
[[package]]
name = "lsystem"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c47210f2a9f5ae2073e7b847026e3233bcb012aa6845201c69c26481762a81"
[[package]]
name = "matchit"
version = "0.7.3"

View File

@ -6,6 +6,7 @@ description = "A new executor for KCL which compiles to Execution Plans"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
image = { version = "0.24.7", default-features = false, features = ["png"] }
kcl-lib = { path = "../kcl" }
kittycad = { workspace = true }
kittycad-execution-plan = { workspace = true }
@ -15,6 +16,7 @@ kittycad-modeling-cmds = { workspace = true }
kittycad-modeling-session = { workspace = true }
thiserror = "1.0.57"
tokio = { version = "1.36.0", features = ["macros", "rt"] }
twenty-twenty = "0.7.0"
uuid = "1.7"
[dev-dependencies]

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -113,6 +113,26 @@ impl BindingScope {
"lineTo".into(),
EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)),
),
(
"line".into(),
EpBinding::from(KclFunction::Line(native_functions::sketch::Line)),
),
(
"xLineTo".into(),
EpBinding::from(KclFunction::XLineTo(native_functions::sketch::XLineTo)),
),
(
"xLine".into(),
EpBinding::from(KclFunction::XLine(native_functions::sketch::XLine)),
),
(
"yLineTo".into(),
EpBinding::from(KclFunction::YLineTo(native_functions::sketch::YLineTo)),
),
(
"yLine".into(),
EpBinding::from(KclFunction::YLine(native_functions::sketch::YLine)),
),
(
"extrude".into(),
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),

View File

@ -76,7 +76,7 @@ impl From<ExecutionFailed> for Error {
) -> Self {
Self::Execution {
error,
instruction,
instruction: instruction.expect("no instruction"),
instruction_index,
}
}

View File

@ -262,6 +262,11 @@ impl Planner {
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
KclFunction::Line(f) => f.call(&mut ctx, args)?,
KclFunction::XLineTo(f) => f.call(&mut ctx, args)?,
KclFunction::XLine(f) => f.call(&mut ctx, args)?,
KclFunction::YLineTo(f) => f.call(&mut ctx, args)?,
KclFunction::YLine(f) => f.call(&mut ctx, args)?,
KclFunction::Add(f) => f.call(&mut ctx, args)?,
KclFunction::Close(f) => f.call(&mut ctx, args)?,
KclFunction::UserDefined(f) => {
@ -631,6 +636,11 @@ enum KclFunction {
Id(native_functions::Id),
StartSketchAt(native_functions::sketch::StartSketchAt),
LineTo(native_functions::sketch::LineTo),
Line(native_functions::sketch::Line),
XLineTo(native_functions::sketch::XLineTo),
XLine(native_functions::sketch::XLine),
YLineTo(native_functions::sketch::YLineTo),
YLine(native_functions::sketch::YLine),
Add(native_functions::Add),
UserDefined(UserDefinedFunction),
Extrude(native_functions::sketch::Extrude),

View File

@ -3,4 +3,4 @@
pub mod helpers;
pub mod stdlib_functions;
pub use stdlib_functions::{Close, Extrude, LineTo, StartSketchAt};
pub use stdlib_functions::{Close, Extrude, Line, LineTo, StartSketchAt, XLine, XLineTo, YLine, YLineTo};

View File

@ -139,7 +139,7 @@ pub fn sequence_binding(
}
}
/// Extract a 2D point from an argument to a Cabble.
/// Extract a 2D point from an argument to a KCL Function.
pub fn arg_point2d(
arg: EpBinding,
fn_name: &'static str,
@ -148,7 +148,7 @@ pub fn arg_point2d(
arg_number: usize,
) -> Result<Address, CompileError> {
let expected = "2D point (array with length 2)";
let elements = sequence_binding(arg, "startSketchAt", "an array of length 2", arg_number)?;
let elements = sequence_binding(arg, fn_name, "an array of length 2", arg_number)?;
if elements.len() != 2 {
return Err(CompileError::ArgWrongType {
fn_name,
@ -165,12 +165,12 @@ pub fn arg_point2d(
let start_z = start + 2;
instructions.extend([
Instruction::Copy {
source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?,
source: single_binding(elements[0].clone(), fn_name, "number", arg_number)?,
destination: Destination::Address(start_x),
length: 1,
},
Instruction::Copy {
source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?,
source: single_binding(elements[1].clone(), fn_name, "number", arg_number)?,
destination: Destination::Address(start_y),
length: 1,
},

View File

@ -1,7 +1,7 @@
use kittycad_execution_plan::{
api_request::ApiRequest,
sketch_types::{self, Axes, BasePath, Plane, SketchGroup},
Destination, Instruction,
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand,
};
use kittycad_execution_plan_traits::{Address, InMemory, Primitive, Value};
use kittycad_modeling_cmds::{
@ -13,6 +13,22 @@ use uuid::Uuid;
use super::helpers::{arg_point2d, no_arg_api_call, sg_binding, single_binding, stack_api_call};
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
#[derive(PartialEq)]
pub enum At {
RelativeXY,
AbsoluteXY,
RelativeX,
AbsoluteX,
RelativeY,
AbsoluteY,
}
impl At {
pub fn is_relative(&self) -> bool {
*self == At::RelativeX || *self == At::RelativeY || *self == At::RelativeXY
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Close;
@ -140,25 +156,124 @@ impl Callable for LineTo {
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "lineTo", args, LineBareOptions { at: At::AbsoluteXY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Line;
impl Callable for Line {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "line", args, LineBareOptions { at: At::RelativeXY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct XLineTo;
impl Callable for XLineTo {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "xLineTo", args, LineBareOptions { at: At::AbsoluteX })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct XLine;
impl Callable for XLine {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "xLine", args, LineBareOptions { at: At::RelativeX })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct YLineTo;
impl Callable for YLineTo {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "yLineTo", args, LineBareOptions { at: At::AbsoluteY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct YLine;
impl Callable for YLine {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "yLine", args, LineBareOptions { at: At::RelativeY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
/// Exposes all the possible arguments the `line` modeling command can take.
/// Reduces code for the other line functions needed.
/// We do not expose this to the developer since it does not align with
/// the documentation (there is no "lineBare").
pub struct LineBare;
/// Used to configure the call to handle different line variants.
pub struct LineBareOptions {
/// Where to start coordinates at, ex: At::RelativeXY.
at: At,
}
impl LineBare {
fn call(
ctx: &mut crate::native_functions::Context<'_>,
fn_name: &'static str,
args: Vec<EpBinding>,
opts: LineBareOptions,
) -> Result<EvalPlan, CompileError> {
let mut instructions = Vec::new();
let fn_name = "lineTo";
// Get both required params.
let required = 2;
let mut args_iter = args.into_iter();
let Some(to) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required: 2,
actual: 0,
required,
actual: args_iter.count(),
});
};
let Some(sketch_group) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required: 2,
actual: 1,
required,
actual: args_iter.count(),
});
};
let tag = match args_iter.next() {
Some(a) => a,
None => {
@ -171,26 +286,90 @@ impl Callable for LineTo {
EpBinding::Single(empty_string_addr)
}
};
// Check the type of required params.
let to = arg_point2d(to, fn_name, &mut instructions, ctx, 0)?;
// We don't check `to` here because it can take on either a
// EpBinding::Sequence or EpBinding::Single.
let sg = sg_binding(sketch_group, fn_name, "sketch group", 1)?;
let tag = single_binding(tag, fn_name, "string tag", 2)?;
let id = Uuid::new_v4();
// Start of the path segment (which is a straight line).
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
let start_of_line = ctx.next_address.offset_by(1);
// Reserve space for the line's end, and the `relative: bool` field.
ctx.next_address.offset_by(length_of_3d_point + 1);
let new_sg_index = ctx.assign_sketch_group();
// Copy based on the options.
match opts {
LineBareOptions { at: At::AbsoluteXY, .. } | LineBareOptions { at: At::RelativeXY, .. } => {
// Push the `to` 2D point onto the stack.
let EpBinding::Sequence { elements, length_at: _ } = to.clone() else {
return Err(CompileError::InvalidOperand("Must pass a list of length 2"));
};
let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() else {
return Err(CompileError::InvalidOperand("Must pass a sequence here."));
};
instructions.extend([
Instruction::Copy {
// X
source: el0,
length: 1,
destination: Destination::StackPush,
},
Instruction::Copy {
// Y
source: el1,
length: 1,
destination: Destination::StackExtend,
},
Instruction::StackExtend { data: vec![0.0.into()] }, // Z
]);
}
LineBareOptions { at: At::AbsoluteX, .. } | LineBareOptions { at: At::RelativeX, .. } => {
let EpBinding::Single(addr) = to else {
return Err(CompileError::InvalidOperand("Must pass a single value here."));
};
instructions.extend([
Instruction::Copy {
// X
source: addr,
length: 1,
destination: Destination::StackPush,
},
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Y
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Z
]);
}
LineBareOptions { at: At::AbsoluteY, .. } | LineBareOptions { at: At::RelativeY, .. } => {
let EpBinding::Single(addr) = to else {
return Err(CompileError::InvalidOperand("Must pass a single value here."));
};
instructions.extend([
Instruction::StackPush {
data: vec![Primitive::from(0.0)],
}, // X
Instruction::Copy {
// Y
source: addr,
length: 1,
destination: Destination::StackExtend,
},
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Z
]);
}
}
instructions.extend([
// Push the `to` 2D point onto the stack.
Instruction::Copy {
source: to,
length: 2,
destination: Destination::StackPush,
},
// Make it a 3D point.
Instruction::StackExtend { data: vec![0.0.into()] },
// Append the new path segment to memory.
// First comes its tag.
Instruction::SetPrimitive {
@ -204,7 +383,7 @@ impl Callable for LineTo {
// Then its `relative` field.
Instruction::SetPrimitive {
address: start_of_line + 1 + length_of_3d_point,
value: false.into(),
value: opts.at.is_relative().into(),
},
// Push the path ID onto the stack.
Instruction::SketchGroupCopyFrom {
@ -231,16 +410,159 @@ impl Callable for LineTo {
data: vec![Primitive::from("ToPoint".to_owned())],
},
// `BasePath::from` point.
// Place them in the secondary stack to prepare ToPoint structure.
Instruction::SketchGroupGetLastPoint {
source: sg,
destination: Destination::StackExtend,
},
// `BasePath::to` point.
Instruction::Copy {
source: start_of_line + 1,
length: 2,
destination: Destination::StackExtend,
]);
// Reserve space for the segment last point
let to_point_from = ctx.next_address.offset_by(2);
instructions.extend([
// Copy to the primary stack as well to be worked with.
Instruction::SketchGroupGetLastPoint {
source: sg,
destination: Destination::Address(to_point_from),
},
]);
// `BasePath::to` point.
// The copy here depends on the incoming `to` data.
// Sometimes it's a list, sometimes it's single datum.
// And the relative/not relative matters. When relative, we need to
// copy coords from `from` into the new `to` coord that don't change.
// At least everything else can be built up from these "primitives".
if let EpBinding::Sequence { elements, length_at: _ } = to.clone() {
if let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() {
match opts {
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 + y2 } }
LineBareOptions { at: At::RelativeXY, .. } => {
instructions.extend([
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 0),
operand1: Operand::Reference(el0),
},
destination: Destination::StackExtend,
},
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 1),
operand1: Operand::Reference(el1),
},
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x2, y2 } }
LineBareOptions { at: At::AbsoluteXY, .. } => {
// Otherwise just directly copy the new points.
instructions.extend([
Instruction::Copy {
source: el0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: el1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
_ => {
return Err(CompileError::InvalidOperand(
"A Sequence with At::...X or At::...Y is not valid here. Must be At::...XY.",
));
}
}
}
} else if let EpBinding::Single(addr) = to {
match opts {
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 } }
LineBareOptions { at: At::RelativeX } => {
instructions.extend([
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 0),
operand1: Operand::Reference(addr),
},
destination: Destination::StackExtend,
},
Instruction::Copy {
source: to_point_from + 1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x2, y1 } }
LineBareOptions { at: At::AbsoluteX } => {
instructions.extend([
Instruction::Copy {
source: addr,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: to_point_from + 1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x1, y1 + y2 } }
LineBareOptions { at: At::RelativeY } => {
instructions.extend([
Instruction::Copy {
source: to_point_from + 0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 1),
operand1: Operand::Reference(addr),
},
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x1, y2 } }
LineBareOptions { at: At::AbsoluteY } => {
instructions.extend([
Instruction::Copy {
source: to_point_from + 0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: addr,
length: 1,
destination: Destination::StackExtend,
},
]);
}
_ => {
return Err(CompileError::InvalidOperand(
"A Single binding with At::...XY is not valid here.",
));
}
}
} else {
return Err(CompileError::InvalidOperand(
"Must be a sequence or single value binding.",
));
}
instructions.extend([
// `BasePath::name` string.
Instruction::Copy {
source: tag,

View File

@ -1048,14 +1048,10 @@ fn store_object_with_array_property() {
/// Write the program's plan to the KCVM debugger's normal input file.
#[allow(unused)]
fn kcvm_dbg(kcl_program: &str) {
fn kcvm_dbg(kcl_program: &str, path: &str) {
let (plan, _scope, _) = must_plan(kcl_program);
let plan_json = serde_json::to_string_pretty(&plan).unwrap();
std::fs::write(
"/Users/adamchalmers/kc-repos/modeling-api/execution-plan-debugger/test_input.json",
plan_json,
)
.unwrap();
std::fs::write(path, plan_json).unwrap();
}
#[tokio::test]
@ -1069,8 +1065,6 @@ async fn stdlib_cube_partial() {
|> close(%)
|> extrude(100.0, %)
"#;
let (_plan, _scope, last_address) = must_plan(program);
assert_eq!(last_address, Address::ZERO + 66);
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
@ -1113,23 +1107,115 @@ async fn stdlib_cube_partial() {
},
]
);
// use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
// let out = client
// .unwrap()
// .run_command(
// uuid::Uuid::new_v4().into(),
// each_cmd::TakeSnapshot {
// format: ImageFormat::Png,
// },
// )
// .await
// .unwrap();
// let out = match out {
// OkModelingCmdResponse::TakeSnapshot(b) => b,
// other => panic!("wrong output: {other:?}"),
// };
// let out: Vec<u8> = out.contents.into();
// std::fs::write("image.png", out).unwrap();
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
let out = client
.unwrap()
.run_command(
uuid::Uuid::new_v4().into(),
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
format: ImageFormat::Png,
}),
)
.await
.unwrap();
let out = match out {
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
other => panic!("wrong output: {other:?}"),
};
use image::io::Reader as ImageReader;
let img = ImageReader::new(std::io::Cursor::new(out))
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image("fixtures/cube_lineTo.png", &img, 0.9999);
}
#[tokio::test]
async fn stdlib_cube_xline_yline() {
let program = r#"
let cube = startSketchAt([0.0, 0.0], "adam")
|> xLine(210.0, %, "side0")
|> yLine(210.0, %, "side1")
|> xLine(-210.0, %, "side2")
|> yLine(-210.0, %, "side3")
|> close(%)
|> extrude(100.0, %)
"#;
kcvm_dbg(
program,
"/home/lee/Code/Zoo/modeling-api/execution-plan-debugger/cube_xyline.json",
);
let (_plan, _scope, _last_address) = must_plan(program);
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let mut client = Some(test_client().await);
let mem = match crate::execute(ast, &mut client).await {
Ok(mem) => mem,
Err(e) => panic!("{e}"),
};
let sg = &mem.sketch_groups.last().unwrap();
assert_eq!(
sg.path_rest,
vec![
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 0.0, y: 0.0 },
to: Point2d { x: 210.0, y: 0.0 },
name: "side0".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 210.0, y: 0.0 },
to: Point2d { x: 210.0, y: 210.0 },
name: "side1".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 210.0, y: 210.0 },
to: Point2d { x: 0.0, y: 210.0 },
name: "side2".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 0.0, y: 210.0 },
to: Point2d { x: 0.0, y: 0.0 },
name: "side3".into(),
}
},
]
);
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
let out = client
.unwrap()
.run_command(
uuid::Uuid::new_v4().into(),
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
format: ImageFormat::Png,
}),
)
.await
.unwrap();
let out = match out {
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
other => panic!("wrong output: {other:?}"),
};
use image::io::Reader as ImageReader;
let img = ImageReader::new(std::io::Cursor::new(out))
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image("fixtures/cube_xyLine.png", &img, 0.9999);
}
async fn test_client() -> Session {

View File

@ -7,6 +7,10 @@ name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "addr2line"
@ -45,10 +49,25 @@ dependencies = [
]
[[package]]
name = "anyhow"
version = "1.0.80"
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
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.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
dependencies = [
"backtrace",
]
@ -92,7 +111,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -103,7 +122,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -268,12 +287,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.29"
version = "0.4.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87d9d13be47a5b7c3907137f1290b0459a7f80efb26be8c52afb11963bccb02"
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.0",
]
[[package]]
@ -320,6 +344,21 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -368,7 +407,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
"synstructure",
]
@ -380,10 +419,11 @@ checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]]
name = "derive-docs"
version = "0.1.6"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "834580a8bd697658876ed8c9f7727e49f01d34f5b859ca921ac5b99ffc6adf77"
checksum = "b4e18a04fe569a325dd50743821f2605057ff5c4b48e60512270b4406907825b"
dependencies = [
"Inflector",
"convert_case",
"once_cell",
"proc-macro2",
@ -391,7 +431,7 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -521,7 +561,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -592,7 +632,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -739,6 +779,29 @@ dependencies = [
"tokio-rustls 0.24.1",
]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
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.4.0"
@ -807,22 +870,23 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kcl-lib"
version = "0.1.42"
version = "0.1.46"
dependencies = [
"anyhow",
"approx",
"async-recursion",
"async-trait",
"bson",
"chrono",
"dashmap",
"databake",
"derive-docs",
@ -833,12 +897,14 @@ dependencies = [
"kittycad-execution-plan-macros",
"kittycad-execution-plan-traits",
"lazy_static",
"mime_guess",
"parse-display 0.9.0",
"reqwest",
"ropey",
"schemars",
"serde",
"serde_json",
"sha2",
"thiserror",
"tokio",
"tokio-tungstenite",
@ -849,6 +915,7 @@ dependencies = [
"wasm-bindgen-futures",
"web-sys",
"winnow",
"zip",
]
[[package]]
@ -861,9 +928,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.54"
version = "0.2.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13958174d876353f429ea8230dc92fe86f164819cea2e51bbf22e01a4c2a496e"
checksum = "f8aa5906d0730bd90f6b3331fe57c04951d00743169a29ee96408767b4060605"
dependencies = [
"anyhow",
"async-trait",
@ -877,6 +944,7 @@ dependencies = [
"http 0.2.9",
"itertools",
"log",
"mime_guess",
"parse-display 0.8.2",
"phonenumber",
"rand",
@ -899,14 +967,13 @@ source = "git+https://github.com/KittyCAD/modeling-api?branch=main#1e6ef9601686c
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
name = "kittycad-execution-plan-traits"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
version = "0.1.13"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
dependencies = [
"serde",
"thiserror",
@ -1028,9 +1095,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi",
@ -1171,7 +1238,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1185,7 +1252,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1232,7 +1299,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1404,9 +1471,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwest"
version = "0.11.24"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.3",
"bytes",
@ -1692,7 +1759,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1726,7 +1793,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1738,7 +1805,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1776,6 +1843,17 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -1847,7 +1925,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1859,7 +1937,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1870,7 +1948,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1881,7 +1959,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -1925,9 +2003,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.50"
version = "2.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032"
dependencies = [
"proc-macro2",
"quote",
@ -1948,7 +2026,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -2004,7 +2082,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -2077,7 +2155,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -2183,7 +2261,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -2212,7 +2290,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
]
[[package]]
@ -2250,7 +2328,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
"termcolor",
]
@ -2382,9 +2460,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -2392,24 +2470,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.41"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
@ -2419,9 +2497,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2429,22 +2507,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.53",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wasm-streams"
@ -2461,9 +2539,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -2506,6 +2584,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -2680,3 +2767,14 @@ name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
]