Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
f97bdaf8b7 | |||
3f3693e12d | |||
73660d1db8 | |||
c373f33507 | |||
2dc76a71cc | |||
ce42966f2b | |||
b47b9c9613 | |||
2af2144f89 | |||
bd37c488ee | |||
a3551e4b2f | |||
d3979edb41 | |||
095a7a575b | |||
b5c8ca05a5 | |||
33f7badf41 | |||
569935c21f | |||
7680605085 | |||
2bb6c74f42 | |||
faf4d42b6a | |||
8dd2a86191 | |||
13c4de77c3 | |||
e29ee9d1ca | |||
b7437e949a | |||
08781ff010 | |||
eb79b1f746 | |||
8df81b2753 | |||
e75a604c64 | |||
0624e42822 | |||
1c07e8af5b | |||
5fccaad0e7 | |||
a506f7f698 | |||
1bb96cd878 |
@ -14,7 +14,7 @@ close(
|
||||
): Sketch
|
||||
```
|
||||
|
||||
|
||||
If you want to perform some 3-dimensional operation on a sketch, like extrude or sweep, you must `close` it first. `close` must be called even if the end point of the last segment is coincident with the sketch starting point.
|
||||
|
||||
### Arguments
|
||||
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Extract the 'x' axis value of the last line segment in the provided 2-d sketch.
|
||||
|
||||
```kcl
|
||||
lastSegX(@sketch: Sketch): number
|
||||
lastSegX(@sketch: Sketch): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ lastSegX(@sketch: Sketch): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | The sketch whose line segment is being queried | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | The sketch whose line segment is being queried. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Extract the 'y' axis value of the last line segment in the provided 2-d sketch.
|
||||
|
||||
```kcl
|
||||
lastSegY(@sketch: Sketch): number
|
||||
lastSegY(@sketch: Sketch): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ lastSegY(@sketch: Sketch): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | The sketch whose line segment is being queried | Yes |
|
||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | The sketch whose line segment is being queried. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the angle (in degrees) of the provided line segment.
|
||||
|
||||
```kcl
|
||||
segAng(@tag: TagIdentifier): number
|
||||
segAng(@tag: tag): number(Angle)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segAng(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Angle)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the ending point of the provided line segment.
|
||||
|
||||
```kcl
|
||||
segEnd(@tag: TagIdentifier): Point2d
|
||||
segEnd(@tag: tag): Point2d
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ segEnd(@tag: TagIdentifier): Point2d
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -39,9 +39,9 @@ cube = startSketchOn(XY)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchOn(XY)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> circle(radius = radius, center = segEnd(tag))
|
||||
|> extrude(length = radius)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> circle(radius = radius, center = segEnd(tag) )
|
||||
|> extrude(length = radius)
|
||||
}
|
||||
|
||||
cylinder(radius = 1, tag = line1)
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the ending point of the provided line segment along the 'x' axis.
|
||||
|
||||
```kcl
|
||||
segEndX(@tag: TagIdentifier): number
|
||||
segEndX(@tag: tag): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segEndX(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the ending point of the provided line segment along the 'y' axis.
|
||||
|
||||
```kcl
|
||||
segEndY(@tag: TagIdentifier): number
|
||||
segEndY(@tag: tag): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segEndY(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the length of the provided line segment.
|
||||
|
||||
```kcl
|
||||
segLen(@tag: TagIdentifier): number
|
||||
segLen(@tag: tag): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segLen(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -29,9 +29,16 @@ segLen(@tag: TagIdentifier): number
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 60, length = 10, tag = $thing)
|
||||
|> angledLine(
|
||||
angle = 60,
|
||||
length = 10,
|
||||
tag = $thing,
|
||||
)
|
||||
|> tangentialArc(angle = -120, radius = 5)
|
||||
|> angledLine(angle = -60, length = segLen(thing))
|
||||
|> angledLine(
|
||||
angle = -60,
|
||||
length = segLen(thing),
|
||||
)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the starting point of the provided line segment.
|
||||
|
||||
```kcl
|
||||
segStart(@tag: TagIdentifier): Point2d
|
||||
segStart(@tag: tag): Point2d
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ segStart(@tag: TagIdentifier): Point2d
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -39,9 +39,9 @@ cube = startSketchOn(XY)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchOn(XY)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> circle(radius = radius, center = segStart(tag))
|
||||
|> extrude(length = radius)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> circle( radius = radius, center = segStart(tag) )
|
||||
|> extrude(length = radius)
|
||||
}
|
||||
|
||||
cylinder(radius = 1, tag = line1)
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the starting point of the provided line segment along the 'x' axis.
|
||||
|
||||
```kcl
|
||||
segStartX(@tag: TagIdentifier): number
|
||||
segStartX(@tag: tag): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segStartX(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the starting point of the provided line segment along the 'y' axis.
|
||||
|
||||
```kcl
|
||||
segStartY(@tag: TagIdentifier): number
|
||||
segStartY(@tag: tag): number(Length)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ segStartY(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Length)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -32,7 +32,7 @@ exampleSketch = startSketchOn(XZ)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 3], tag = $thing)
|
||||
|> line(end = [-10, 0])
|
||||
|> line(end = [0, 20 - segStartY(thing)])
|
||||
|> line(end = [0, 20-segStartY(thing)])
|
||||
|> line(end = [-10, 0])
|
||||
|> close()
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Returns the angle coming out of the end of the segment in degrees.
|
||||
|
||||
```kcl
|
||||
tangentToEnd(@tag: TagIdentifier): number
|
||||
tangentToEnd(@tag: tag): number(Angle)
|
||||
```
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ tangentToEnd(@tag: TagIdentifier): number
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) | The line segment being queried by its tag | Yes |
|
||||
| [`tag`](/docs/kcl-std/types/std-types-tag) | [`tag`](/docs/kcl-std/types/std-types-tag) | The line segment being queried by its tag. | Yes |
|
||||
|
||||
### Returns
|
||||
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number(Angle)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -32,7 +32,10 @@ pillSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [20, 0])
|
||||
|> tangentialArc(end = [0, 10], tag = $arc1)
|
||||
|> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
|> angledLine(
|
||||
angle = tangentToEnd(arc1),
|
||||
length = 20,
|
||||
)
|
||||
|> tangentialArc(end = [0, -10])
|
||||
|> close()
|
||||
|
||||
@ -47,7 +50,10 @@ pillSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> tangentialArc(endAbsolute = [10, 20], tag = $arc1)
|
||||
|> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
|> angledLine(
|
||||
angle = tangentToEnd(arc1),
|
||||
length = 20,
|
||||
)
|
||||
|> tangentialArc(end = [-10, 0])
|
||||
|> close()
|
||||
|
||||
@ -60,7 +66,10 @@ pillExtrude = extrude(pillSketch, length = 10)
|
||||
rectangleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0], tag = $seg1)
|
||||
|> angledLine(angle = tangentToEnd(seg1), length = 10)
|
||||
|> angledLine(
|
||||
angle = tangentToEnd(seg1),
|
||||
length = 10,
|
||||
)
|
||||
|> line(end = [0, 10])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()
|
||||
@ -73,7 +82,11 @@ rectangleExtrude = extrude(rectangleSketch, length = 10)
|
||||
```kcl
|
||||
bottom = startSketchOn(XY)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> arc(endAbsolute = [10, 10], interiorAbsolute = [5, 1], tag = $arc1)
|
||||
|> arc(
|
||||
endAbsolute = [10, 10],
|
||||
interiorAbsolute = [5, 1],
|
||||
tag = $arc1,
|
||||
)
|
||||
|> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
|> close()
|
||||
```
|
||||
@ -82,7 +95,7 @@ bottom = startSketchOn(XY)
|
||||
|
||||
```kcl
|
||||
circSketch = startSketchOn(XY)
|
||||
|> circle(center = [0, 0], radius = 3, tag = $circ)
|
||||
|> circle(center = [0, 0], radius= 3, tag = $circ)
|
||||
|
||||
triangleSketch = startSketchOn(XY)
|
||||
|> startProfile(at = [-5, 0])
|
@ -60,8 +60,8 @@ layout: manual
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
* [`getPreviousAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getPreviousAdjacentEdge)
|
||||
* [`involuteCircular`](/docs/kcl-std/involuteCircular)
|
||||
* [`lastSegX`](/docs/kcl-std/lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/lastSegY)
|
||||
* [`lastSegX`](/docs/kcl-std/functions/std-sketch-lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/functions/std-sketch-lastSegY)
|
||||
* [`line`](/docs/kcl-std/line)
|
||||
* [`loft`](/docs/kcl-std/functions/std-sketch-loft)
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
@ -72,19 +72,19 @@ layout: manual
|
||||
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
||||
* [`profileStartY`](/docs/kcl-std/profileStartY)
|
||||
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
|
||||
* [`segAng`](/docs/kcl-std/segAng)
|
||||
* [`segEnd`](/docs/kcl-std/segEnd)
|
||||
* [`segEndX`](/docs/kcl-std/segEndX)
|
||||
* [`segEndY`](/docs/kcl-std/segEndY)
|
||||
* [`segLen`](/docs/kcl-std/segLen)
|
||||
* [`segStart`](/docs/kcl-std/segStart)
|
||||
* [`segStartX`](/docs/kcl-std/segStartX)
|
||||
* [`segStartY`](/docs/kcl-std/segStartY)
|
||||
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
|
||||
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)
|
||||
* [`segEndX`](/docs/kcl-std/functions/std-sketch-segEndX)
|
||||
* [`segEndY`](/docs/kcl-std/functions/std-sketch-segEndY)
|
||||
* [`segLen`](/docs/kcl-std/functions/std-sketch-segLen)
|
||||
* [`segStart`](/docs/kcl-std/functions/std-sketch-segStart)
|
||||
* [`segStartX`](/docs/kcl-std/functions/std-sketch-segStartX)
|
||||
* [`segStartY`](/docs/kcl-std/functions/std-sketch-segStartY)
|
||||
* [`startProfile`](/docs/kcl-std/startProfile)
|
||||
* [`startSketchOn`](/docs/kcl-std/startSketchOn)
|
||||
* [`subtract2d`](/docs/kcl-std/subtract2d)
|
||||
* [`sweep`](/docs/kcl-std/functions/std-sketch-sweep)
|
||||
* [`tangentToEnd`](/docs/kcl-std/tangentToEnd)
|
||||
* [`tangentToEnd`](/docs/kcl-std/functions/std-sketch-tangentToEnd)
|
||||
* [`tangentialArc`](/docs/kcl-std/tangentialArc)
|
||||
* [`xLine`](/docs/kcl-std/xLine)
|
||||
* [`yLine`](/docs/kcl-std/yLine)
|
||||
|
@ -25,8 +25,8 @@ This module contains functions for creating and manipulating sketches, and makin
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
* [`getPreviousAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getPreviousAdjacentEdge)
|
||||
* [`involuteCircular`](/docs/kcl-std/involuteCircular)
|
||||
* [`lastSegX`](/docs/kcl-std/lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/lastSegY)
|
||||
* [`lastSegX`](/docs/kcl-std/functions/std-sketch-lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/functions/std-sketch-lastSegY)
|
||||
* [`line`](/docs/kcl-std/line)
|
||||
* [`loft`](/docs/kcl-std/functions/std-sketch-loft)
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
@ -37,19 +37,19 @@ This module contains functions for creating and manipulating sketches, and makin
|
||||
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
||||
* [`profileStartY`](/docs/kcl-std/profileStartY)
|
||||
* [`revolve`](/docs/kcl-std/functions/std-sketch-revolve)
|
||||
* [`segAng`](/docs/kcl-std/segAng)
|
||||
* [`segEnd`](/docs/kcl-std/segEnd)
|
||||
* [`segEndX`](/docs/kcl-std/segEndX)
|
||||
* [`segEndY`](/docs/kcl-std/segEndY)
|
||||
* [`segLen`](/docs/kcl-std/segLen)
|
||||
* [`segStart`](/docs/kcl-std/segStart)
|
||||
* [`segStartX`](/docs/kcl-std/segStartX)
|
||||
* [`segStartY`](/docs/kcl-std/segStartY)
|
||||
* [`segAng`](/docs/kcl-std/functions/std-sketch-segAng)
|
||||
* [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd)
|
||||
* [`segEndX`](/docs/kcl-std/functions/std-sketch-segEndX)
|
||||
* [`segEndY`](/docs/kcl-std/functions/std-sketch-segEndY)
|
||||
* [`segLen`](/docs/kcl-std/functions/std-sketch-segLen)
|
||||
* [`segStart`](/docs/kcl-std/functions/std-sketch-segStart)
|
||||
* [`segStartX`](/docs/kcl-std/functions/std-sketch-segStartX)
|
||||
* [`segStartY`](/docs/kcl-std/functions/std-sketch-segStartY)
|
||||
* [`startProfile`](/docs/kcl-std/startProfile)
|
||||
* [`startSketchOn`](/docs/kcl-std/startSketchOn)
|
||||
* [`subtract2d`](/docs/kcl-std/subtract2d)
|
||||
* [`sweep`](/docs/kcl-std/functions/std-sketch-sweep)
|
||||
* [`tangentToEnd`](/docs/kcl-std/tangentToEnd)
|
||||
* [`tangentToEnd`](/docs/kcl-std/functions/std-sketch-tangentToEnd)
|
||||
* [`tangentialArc`](/docs/kcl-std/tangentialArc)
|
||||
* [`xLine`](/docs/kcl-std/xLine)
|
||||
* [`yLine`](/docs/kcl-std/yLine)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1766,7 +1766,7 @@ loft001 = loft([sketch001, sketch002])
|
||||
sketch001 = startSketchOn(YZ)
|
||||
profile001 = circle(sketch001, center = [0, 0], radius = 500)
|
||||
sketch002 = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
profile002 = startProfile(sketch002, at = [0, 0])
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArc(endAbsolute = [-2000, 500])`,
|
||||
},
|
||||
@ -1782,7 +1782,7 @@ profile001 = startProfile(sketch001, at = [-400, -400])
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
sketch002 = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
profile002 = startProfile(sketch002, at = [0, 0])
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArc(endAbsolute = [-2000, 500])`,
|
||||
},
|
||||
@ -1810,9 +1810,9 @@ sketch002 = startSketchOn(XZ)
|
||||
testPoint.x - 50,
|
||||
testPoint.y
|
||||
)
|
||||
const sweepDeclaration = 'sweep001 = sweep(profile001, path = sketch002)'
|
||||
const sweepDeclaration = 'sweep001 = sweep(profile001, path = profile002)'
|
||||
const editedSweepDeclaration =
|
||||
'sweep001 = sweep(profile001, path = sketch002, sectional = true)'
|
||||
'sweep001 = sweep(profile001, path = profile002, sectional = true)'
|
||||
|
||||
await test.step(`Look for sketch001`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
|
@ -2116,3 +2116,74 @@ test(
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'segment position changes persist after dragging and reopening project',
|
||||
{ tag: '@desktop' },
|
||||
async ({ scene, cmdBar, context, page, editor, toolbar }, testInfo) => {
|
||||
const projectName = 'segment-drag-test'
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const projectDir = path.join(dir, projectName)
|
||||
await fsp.mkdir(projectDir, { recursive: true })
|
||||
await fsp.writeFile(
|
||||
path.join(projectDir, 'main.kcl'),
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [0, 0])
|
||||
|> line(end = [0, 6])
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [-8, -5])
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1200, height: 600 })
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step('Opening the project and entering sketch mode', async () => {
|
||||
await expect(page.getByText(projectName)).toBeVisible()
|
||||
await page.getByText(projectName).click()
|
||||
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// go to sketch mode
|
||||
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||
|
||||
// Without this, "add axis n grid" action runs after editing the sketch and invokes codeManager.writeToFile()
|
||||
// so we wait for that action to run first before we start editing the sketch and making sure it's saving
|
||||
// because of those edits.
|
||||
await page.waitForTimeout(2000)
|
||||
})
|
||||
|
||||
const changedLine = 'line(end = [-6.54, -4.99])'
|
||||
|
||||
await test.step('Dragging the line endpoint to modify it', async () => {
|
||||
// Get the last line's endpoint position
|
||||
const lineEnd = await u.getBoundingBox('[data-overlay-index="3"]')
|
||||
|
||||
await page.mouse.move(lineEnd.x, lineEnd.y - 5)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(lineEnd.x + 80, lineEnd.y)
|
||||
await page.mouse.up()
|
||||
|
||||
await editor.expectEditor.toContain(changedLine)
|
||||
|
||||
// Exit sketch mode
|
||||
await page.keyboard.press('Escape')
|
||||
await page.waitForTimeout(100)
|
||||
})
|
||||
|
||||
await test.step('Going back to dashboard', async () => {
|
||||
await page.getByTestId('app-logo').click()
|
||||
await page.waitForTimeout(1000)
|
||||
})
|
||||
|
||||
await test.step('Reopening the project and verifying changes are saved', async () => {
|
||||
await page.getByText(projectName).click()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Check if new line coordinates were saved
|
||||
await editor.expectEditor.toContain(changedLine)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
@ -2362,7 +2362,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
||||
|
||||
await test.step('add new profile', async () => {
|
||||
await toolbar.rectangleBtn.click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.waitForTimeout(200)
|
||||
await rectStart()
|
||||
await editor.expectEditor.toContain(
|
||||
`profile005 = startProfile(sketch001, at = [15.68, -3.84])`
|
||||
|
@ -823,7 +823,7 @@ test('theme persists', async ({ page, context, homePage }) => {
|
||||
uploadThroughput: -1,
|
||||
})
|
||||
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
await expect(networkToggle).toContainText('Network health (Strong)')
|
||||
|
||||
await expect(page.getByText('building scene')).not.toBeVisible()
|
||||
|
||||
|
@ -14,8 +14,10 @@ test.describe('Test network related behaviors', () => {
|
||||
'simulate network down and network little widget',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage }) => {
|
||||
const networkToggleConnectedText = page.getByText('Connected')
|
||||
const networkToggleWeakText = page.getByText('Network health (Weak)')
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
const networkToggleWeakText = page.getByText('Network health (Ok)')
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
@ -98,8 +100,10 @@ test.describe('Test network related behaviors', () => {
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, toolbar, scene, cmdBar }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggleConnectedText = page.getByText('Connected')
|
||||
const networkToggleWeakText = page.getByText('Network health (Weak)')
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
const networkToggleWeakText = page.getByText('Network health (Ok)')
|
||||
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
@ -282,8 +286,10 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34])
|
||||
{ tag: ['@desktop', '@skipLocalEngine'] },
|
||||
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggleConnectedText = page.getByText('Connected')
|
||||
const networkToggleWeakText = page.getByText('Network health (Weak)')
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
const networkToggleWeakText = page.getByText('Network health (Ok)')
|
||||
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
|
@ -3,187 +3,250 @@ import { uuidv4 } from '@src/lib/utils'
|
||||
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
|
||||
test.describe('Testing Camera Movement', () => {
|
||||
test('Can move camera reliably', async ({
|
||||
/**
|
||||
* hack that we're implemented our own retry instead of using retries built into playwright.
|
||||
* however each of these camera drags can be flaky, because of udp
|
||||
* and so putting them together means only one needs to fail to make this test extra flaky.
|
||||
* this way we can retry within the test
|
||||
* We could break them out into separate tests, but the longest past of the test is waiting
|
||||
* for the stream to start, so it can be good to bundle related things together.
|
||||
*/
|
||||
const bakeInRetries = async ({
|
||||
mouseActions,
|
||||
afterPosition,
|
||||
beforePosition,
|
||||
retryCount = 0,
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
scene,
|
||||
}: {
|
||||
mouseActions: () => Promise<void>
|
||||
beforePosition: [number, number, number]
|
||||
afterPosition: [number, number, number]
|
||||
retryCount?: number
|
||||
page: Page
|
||||
scene: SceneFixture
|
||||
}) => {
|
||||
const acceptableCamError = 5
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.connectionEstablished()
|
||||
await test.step('Set up initial camera position', async () =>
|
||||
await scene.moveCameraTo({
|
||||
x: beforePosition[0],
|
||||
y: beforePosition[1],
|
||||
z: beforePosition[2],
|
||||
}))
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
const camPos: [number, number, number] = [0, 85, 85]
|
||||
const bakeInRetries = async (
|
||||
mouseActions: any,
|
||||
xyz: [number, number, number],
|
||||
cnt = 0
|
||||
) => {
|
||||
// hack that we're implemented our own retry instead of using retries built into playwright.
|
||||
// however each of these camera drags can be flaky, because of udp
|
||||
// and so putting them together means only one needs to fail to make this test extra flaky.
|
||||
// this way we can retry within the test
|
||||
// We could break them out into separate tests, but the longest past of the test is waiting
|
||||
// for the stream to start, so it can be good to bundle related things together.
|
||||
|
||||
const camCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
}
|
||||
const updateCamCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
}
|
||||
await u.sendCustomCmd(camCommand)
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd(updateCamCommand)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// rotate
|
||||
await u.closeDebugPanel()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
// const yo = page.getByTestId('cam-x-position').inputValue()
|
||||
|
||||
await u.doAndWaitForImageDiff(async () => {
|
||||
await test.step('Do actions and watch for changes', async () =>
|
||||
u.doAndWaitForImageDiff(async () => {
|
||||
await mouseActions()
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.waitForTimeout(100)
|
||||
}, 300)
|
||||
}, 300))
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await expect(page.getByTestId('cam-x-position')).toBeAttached()
|
||||
|
||||
const vals = await Promise.all([
|
||||
page.getByTestId('cam-x-position').inputValue(),
|
||||
page.getByTestId('cam-y-position').inputValue(),
|
||||
page.getByTestId('cam-z-position').inputValue(),
|
||||
])
|
||||
const errors = vals.map((v, i) => Math.abs(Number(v) - afterPosition[i]))
|
||||
let shouldRetry = false
|
||||
|
||||
if (errors.some((e) => e > acceptableCamError)) {
|
||||
if (retryCount > 2) {
|
||||
console.log('xVal', vals[0], 'xError', errors[0])
|
||||
console.log('yVal', vals[1], 'yError', errors[1])
|
||||
console.log('zVal', vals[2], 'zError', errors[2])
|
||||
|
||||
throw new Error('Camera position not as expected', {
|
||||
cause: {
|
||||
vals,
|
||||
errors,
|
||||
},
|
||||
})
|
||||
}
|
||||
shouldRetry = true
|
||||
}
|
||||
if (shouldRetry) {
|
||||
await bakeInRetries({
|
||||
mouseActions,
|
||||
afterPosition: afterPosition,
|
||||
beforePosition: beforePosition,
|
||||
retryCount: retryCount + 1,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
test(
|
||||
'Can pan and zoom camera reliably',
|
||||
{
|
||||
tag: '@web',
|
||||
},
|
||||
async ({ page, homePage, scene, cmdBar }) => {
|
||||
const u = await getUtils(page)
|
||||
const camInitialPosition: [number, number, number] = [0, 85, 85]
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByTestId('cam-x-position').isVisible()
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
const vals = await Promise.all([
|
||||
page.getByTestId('cam-x-position').inputValue(),
|
||||
page.getByTestId('cam-y-position').inputValue(),
|
||||
page.getByTestId('cam-z-position').inputValue(),
|
||||
])
|
||||
const xError = Math.abs(Number(vals[0]) + xyz[0])
|
||||
const yError = Math.abs(Number(vals[1]) + xyz[1])
|
||||
const zError = Math.abs(Number(vals[2]) + xyz[2])
|
||||
await test.step('Pan', async () => {
|
||||
await bakeInRetries({
|
||||
mouseActions: async () => {
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.move(600, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
// Gotcha: remove steps:2 from this 700,200 mouse move. This bricked the test on local host engine.
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(200)
|
||||
},
|
||||
afterPosition: [19, 85, 85],
|
||||
beforePosition: camInitialPosition,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
})
|
||||
|
||||
let shouldRetry = false
|
||||
await test.step('Zoom with click and drag', async () => {
|
||||
await bakeInRetries({
|
||||
mouseActions: async () => {
|
||||
await page.keyboard.down('Control')
|
||||
await page.mouse.move(700, 400)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(700, 300)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Control')
|
||||
},
|
||||
afterPosition: [0, 118, 118],
|
||||
beforePosition: camInitialPosition,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
})
|
||||
|
||||
if (xError > 5 || yError > 5 || zError > 5) {
|
||||
if (cnt > 2) {
|
||||
console.log('xVal', vals[0], 'xError', xError)
|
||||
console.log('yVal', vals[1], 'yError', yError)
|
||||
console.log('zVal', vals[2], 'zError', zError)
|
||||
|
||||
throw new Error('Camera position not as expected')
|
||||
await test.step('Zoom with scrollwheel', async () => {
|
||||
const refreshCamValuesCmd: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
}
|
||||
shouldRetry = true
|
||||
}
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
if (shouldRetry) await bakeInRetries(mouseActions, xyz, cnt + 1)
|
||||
await bakeInRetries({
|
||||
mouseActions: async () => {
|
||||
await page.mouse.move(700, 400)
|
||||
await page.mouse.wheel(0, -150)
|
||||
|
||||
// Scroll zooming doesn't update the debug pane's cam position values,
|
||||
// so we have to force a refresh.
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd(refreshCamValuesCmd)
|
||||
await u.waitForCmdReceive('default_camera_get_settings')
|
||||
await u.closeDebugPanel()
|
||||
},
|
||||
afterPosition: [0, 42.5, 42.5],
|
||||
beforePosition: camInitialPosition,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
})
|
||||
}
|
||||
await bakeInRetries(async () => {
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.waitForTimeout(100)
|
||||
)
|
||||
|
||||
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
|
||||
expect(appLogoBBox).not.toBeNull()
|
||||
if (!appLogoBBox) throw new Error('app logo not found')
|
||||
await page.mouse.move(
|
||||
appLogoBBox.x + appLogoBBox.width / 2,
|
||||
appLogoBBox.y + appLogoBBox.height / 2
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(600, 303)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
}, [4, -10.5, -120])
|
||||
test(
|
||||
'Can orbit camera reliably',
|
||||
{
|
||||
tag: '@web',
|
||||
},
|
||||
async ({ page, homePage, scene, cmdBar }) => {
|
||||
const u = await getUtils(page)
|
||||
const initialCamPosition: [number, number, number] = [0, 85, 85]
|
||||
|
||||
await bakeInRetries(async () => {
|
||||
await page.keyboard.down('Shift')
|
||||
await page.mouse.move(600, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
// Gotcha: remove steps:2 from this 700,200 mouse move. This bricked the test on local host engine.
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Shift')
|
||||
}, [-19, -85, -85])
|
||||
await homePage.goToModelingScene()
|
||||
// this turns on the debug pane setting as well
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
const camCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
vantage: { x: camPos[0], y: camPos[1], z: camPos[2] },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.closeKclCodePanel()
|
||||
|
||||
await test.step('Test orbit with spherical mode', async () => {
|
||||
await bakeInRetries({
|
||||
mouseActions: async () => {
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
|
||||
expect(appLogoBBox).not.toBeNull()
|
||||
if (!appLogoBBox) throw new Error('app logo not found')
|
||||
await page.mouse.move(
|
||||
appLogoBBox.x + appLogoBBox.width / 2,
|
||||
appLogoBBox.y + appLogoBBox.height / 2
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(600, 303)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
},
|
||||
afterPosition: [-4, 10.5, 120],
|
||||
beforePosition: initialCamPosition,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Test orbit with trackball mode', async () => {
|
||||
await test.step('Set orbitMode to trackball', async () => {
|
||||
await cmdBar.openCmdBar()
|
||||
await cmdBar.selectOption({ name: 'camera orbit' }).click()
|
||||
await cmdBar.selectOption({ name: 'trackball' }).click()
|
||||
await expect(
|
||||
page.getByText(`camera orbit to "trackball"`)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
await bakeInRetries({
|
||||
mouseActions: async () => {
|
||||
await page.mouse.move(700, 200)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const appLogoBBox = await page.getByTestId('app-logo').boundingBox()
|
||||
expect(appLogoBBox).not.toBeNull()
|
||||
if (!appLogoBBox) {
|
||||
throw new Error('app logo not found')
|
||||
}
|
||||
await page.mouse.move(
|
||||
appLogoBBox.x + appLogoBBox.width / 2,
|
||||
appLogoBBox.y + appLogoBBox.height / 2
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(600, 303)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
},
|
||||
afterPosition: [18.06, -42.79, 110.87],
|
||||
beforePosition: initialCamPosition,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
})
|
||||
}
|
||||
const updateCamCommand: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
}
|
||||
await u.sendCustomCmd(camCommand)
|
||||
await page.waitForTimeout(100)
|
||||
await u.sendCustomCmd(updateCamCommand)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
// zoom
|
||||
await u.doAndWaitForImageDiff(async () => {
|
||||
await page.keyboard.down('Control')
|
||||
await page.mouse.move(700, 400)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(700, 300)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await u.openDebugPanel()
|
||||
await page.waitForTimeout(300)
|
||||
await u.clearCommandLogs()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
}, 300)
|
||||
|
||||
// zoom with scroll
|
||||
await u.openAndClearDebugPanel()
|
||||
// TODO, it appears we don't get the cam setting back from the engine when the interaction is zoom into `backInRetries` once the information is sent back on zoom
|
||||
// await expect(Math.abs(Number(await page.getByTestId('cam-x-position').inputValue()) + 12)).toBeLessThan(1.5)
|
||||
// await expect(Math.abs(Number(await page.getByTestId('cam-y-position').inputValue()) - 85)).toBeLessThan(1.5)
|
||||
// await expect(Math.abs(Number(await page.getByTestId('cam-z-position').inputValue()) - 85)).toBeLessThan(1.5)
|
||||
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
|
||||
await bakeInRetries(async () => {
|
||||
await page.mouse.move(700, 400)
|
||||
await page.mouse.wheel(0, -100)
|
||||
}, [0, -85, -85])
|
||||
})
|
||||
)
|
||||
|
||||
// TODO: fix after electron migration is merged
|
||||
test('Zoom should be consistent when exiting or entering sketches', async ({
|
||||
|
@ -27,7 +27,7 @@ if len(modified_release_body) > max_length:
|
||||
# Message to send to Discord
|
||||
data = {
|
||||
"content": textwrap.dedent(f'''
|
||||
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/design-studio>
|
||||
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/design-studio/download#added>
|
||||
|
||||
{modified_release_body}
|
||||
'''),
|
||||
|
@ -11,24 +11,20 @@ filletRadius = 0.5
|
||||
plateThickness = .5
|
||||
centerHoleDiameter = 2
|
||||
|
||||
// Create a function that defines the body width and length of the mounting plate. Tag the corners so they can be passed through the fillet function.
|
||||
fn rectShape(pos, w, l) {
|
||||
rr = startSketchOn(XY)
|
||||
|> startProfile(at = [pos[0] - (w / 2), pos[1] - (l / 2)])
|
||||
|> line(endAbsolute = [pos[0] + w / 2, pos[1] - (l / 2)], tag = $edge1)
|
||||
|> line(endAbsolute = [pos[0] + w / 2, pos[1] + l / 2], tag = $edge2)
|
||||
|> line(endAbsolute = [pos[0] - (w / 2), pos[1] + l / 2], tag = $edge3)
|
||||
|> close(tag = $edge4)
|
||||
return rr
|
||||
}
|
||||
|
||||
// Define the hole radius and x, y location constants
|
||||
holeRadius = .25
|
||||
holeIndex = .75
|
||||
|
||||
sketch001 = startSketchOn(XY)
|
||||
rectShape = startProfile(sketch001, at = [-plateWidth / 2, plateLength / 2])
|
||||
|> angledLine(angle = 0, length = plateWidth, tag = $basePlateEdge1)
|
||||
|> angledLine(angle = segAng(basePlateEdge1) - 90, length = plateLength, tag = $basePlateEdge2)
|
||||
|> angledLine(angle = segAng(basePlateEdge1), length = -segLen(basePlateEdge1), tag = $basePlateEdge3)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $basePlateEdge4)
|
||||
|> close()
|
||||
|
||||
// Create the mounting plate extrusion, holes, and fillets
|
||||
rs = rectShape(pos = [0, 0], w = plateWidth, l = plateLength)
|
||||
part = rs
|
||||
part = rectShape
|
||||
|> subtract2d(tool = circle(
|
||||
center = [
|
||||
-plateWidth / 2 + holeIndex,
|
||||
@ -62,9 +58,9 @@ part = rs
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [
|
||||
getPreviousAdjacentEdge(rs.tags.edge1),
|
||||
getPreviousAdjacentEdge(rs.tags.edge2),
|
||||
getPreviousAdjacentEdge(rs.tags.edge3),
|
||||
getPreviousAdjacentEdge(rs.tags.edge4)
|
||||
getCommonEdge(faces = [basePlateEdge3, basePlateEdge2]),
|
||||
getCommonEdge(faces = [basePlateEdge4, basePlateEdge3]),
|
||||
getCommonEdge(faces = [basePlateEdge4, basePlateEdge1]),
|
||||
getCommonEdge(faces = [basePlateEdge2, basePlateEdge1])
|
||||
],
|
||||
)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
47
rust/Cargo.lock
generated
47
rust/Cargo.lock
generated
@ -627,26 +627,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||
checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"futures",
|
||||
"is-terminal",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.13.0",
|
||||
"num-traits 0.2.19",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"tokio",
|
||||
@ -715,9 +711,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "csscolorparser"
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f9a16a848a7fb95dd47ce387ac1ee9a6df879ba784b815537fcd388a1a8288"
|
||||
checksum = "5fda6aace1fbef3aa217b27f4c8d7d071ef2a70a5ca51050b1f17d40299d3f16"
|
||||
dependencies = [
|
||||
"phf",
|
||||
]
|
||||
@ -1815,7 +1811,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1826,7 +1822,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-derive-docs"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -1845,7 +1841,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-directory-test-macro"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
@ -1855,7 +1851,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server"
|
||||
version = "0.2.77"
|
||||
version = "0.2.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1876,7 +1872,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1896,7 +1892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.77"
|
||||
version = "0.2.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1973,7 +1969,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-python-bindings"
|
||||
version = "0.3.77"
|
||||
version = "0.3.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kcl-lib",
|
||||
@ -1988,7 +1984,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
@ -2001,7 +1997,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-to-core"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2015,7 +2011,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-wasm-lib"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bson",
|
||||
@ -4200,9 +4196,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.24.0"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
|
||||
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
@ -4456,21 +4452,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http 1.3.1",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"rand 0.9.0",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.12",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
[package]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-api"
|
||||
rust-version = "1.76"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -134,6 +134,21 @@ pub const TEST_NAMES: &[&str] = &[
|
||||
"std-sketch-patternLinear2d-1",
|
||||
"std-sketch-patternCircular2d-0",
|
||||
"std-sketch-circleThreePoint-0",
|
||||
"std-sketch-segStart-0",
|
||||
"std-sketch-segStartX-0",
|
||||
"std-sketch-segStartY-0",
|
||||
"std-sketch-segEnd-0",
|
||||
"std-sketch-segEndX-0",
|
||||
"std-sketch-segEndY-0",
|
||||
"std-sketch-lastSegX-0",
|
||||
"std-sketch-lastSegY-0",
|
||||
"std-sketch-segAng-0",
|
||||
"std-sketch-segLen-0",
|
||||
"std-sketch-tangentToEnd-0",
|
||||
"std-sketch-tangentToEnd-1",
|
||||
"std-sketch-tangentToEnd-2",
|
||||
"std-sketch-tangentToEnd-3",
|
||||
"std-sketch-tangentToEnd-4",
|
||||
"std-solid-appearance-0",
|
||||
"std-solid-appearance-1",
|
||||
"std-solid-appearance-2",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-directory-test-macro"
|
||||
description = "A tool for generating tests from a directory of kcl files"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.77"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
publish = false
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "kcl-language-server"
|
||||
description = "A language server for KCL."
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
version = "0.2.77"
|
||||
version = "0.2.79"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.77"
|
||||
version = "0.2.79"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -31,7 +31,7 @@ clap = { version = "4.5.36", default-features = false, optional = true, features
|
||||
"derive",
|
||||
] }
|
||||
convert_case = "0.8.0"
|
||||
csscolorparser = "0.7.0"
|
||||
csscolorparser = "0.7.2"
|
||||
dashmap = { workspace = true }
|
||||
dhat = { version = "0.3", optional = true }
|
||||
fnv = "1.0.7"
|
||||
@ -107,7 +107,7 @@ web-sys = { version = "0.3.76", features = ["console"] }
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
instant = "0.1.13"
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.24.0", features = [
|
||||
tokio-tungstenite = { version = "0.26.2", features = [
|
||||
"rustls-tls-native-roots",
|
||||
] }
|
||||
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||
@ -130,7 +130,7 @@ tabled = ["dep:tabled"]
|
||||
[dev-dependencies]
|
||||
approx = "0.5"
|
||||
base64 = "0.22.1"
|
||||
criterion = { version = "0.5.1", features = ["async_tokio"] }
|
||||
criterion = { version = "0.6.0", features = ["async_tokio"] }
|
||||
expectorate = "1.1.0"
|
||||
handlebars = "6.3.2"
|
||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::{
|
||||
fs,
|
||||
hint::black_box,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
const IGNORE_DIRS: [&str; 2] = ["step", "screenshots"];
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use std::hint::black_box;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
pub fn bench_parse(c: &mut Criterion) {
|
||||
for (name, file) in [
|
||||
|
@ -1,4 +1,6 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use std::hint::black_box;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use kcl_lib::kcl_lsp_server;
|
||||
use tokio::runtime::Runtime;
|
||||
use tower_lsp::LanguageServer;
|
||||
|
@ -67,6 +67,7 @@ pub struct TcpRead {
|
||||
|
||||
/// Occurs when client couldn't read from the WebSocket to the engine.
|
||||
// #[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum WebSocketReadError {
|
||||
/// Could not read a message due to WebSocket errors.
|
||||
Read(tokio_tungstenite::tungstenite::Error),
|
||||
@ -206,7 +207,7 @@ impl EngineConnection {
|
||||
async fn inner_send_to_engine(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
||||
let msg = serde_json::to_string(&request).map_err(|e| anyhow!("could not serialize json: {e}"))?;
|
||||
tcp_write
|
||||
.send(WsMsg::Text(msg))
|
||||
.send(WsMsg::Text(msg.into()))
|
||||
.await
|
||||
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
||||
Ok(())
|
||||
@ -216,19 +217,17 @@ impl EngineConnection {
|
||||
async fn inner_send_to_engine_binary(request: WebSocketRequest, tcp_write: &mut WebSocketTcpWrite) -> Result<()> {
|
||||
let msg = bson::to_vec(&request).map_err(|e| anyhow!("could not serialize bson: {e}"))?;
|
||||
tcp_write
|
||||
.send(WsMsg::Binary(msg))
|
||||
.send(WsMsg::Binary(msg.into()))
|
||||
.await
|
||||
.map_err(|e| anyhow!("could not send json over websocket: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn new(ws: reqwest::Upgraded) -> Result<EngineConnection> {
|
||||
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig {
|
||||
let wsconfig = tokio_tungstenite::tungstenite::protocol::WebSocketConfig::default()
|
||||
// 4294967296 bytes, which is around 4.2 GB.
|
||||
max_message_size: Some(usize::MAX),
|
||||
max_frame_size: Some(usize::MAX),
|
||||
..Default::default()
|
||||
};
|
||||
.max_message_size(Some(usize::MAX))
|
||||
.max_frame_size(Some(usize::MAX));
|
||||
|
||||
let ws_stream = tokio_tungstenite::WebSocketStream::from_raw_socket(
|
||||
ws,
|
||||
@ -439,7 +438,7 @@ impl EngineManager for EngineConnection {
|
||||
request_sent: tx,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(format!("Failed to send debug: {}", e), vec![])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(format!("Failed to send debug: {}", e), vec![])))?;
|
||||
|
||||
let _ = rx.await;
|
||||
Ok(())
|
||||
@ -474,7 +473,7 @@ impl EngineManager for EngineConnection {
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to send modeling command: {}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -483,13 +482,13 @@ impl EngineManager for EngineConnection {
|
||||
// Wait for the request to be sent.
|
||||
rx.await
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("could not send request to the engine actor: {e}"),
|
||||
vec![source_range],
|
||||
))
|
||||
})?
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("could not send request to the engine: {e}"),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -516,12 +515,12 @@ impl EngineManager for EngineConnection {
|
||||
// Check if we have any pending errors.
|
||||
let pe = self.pending_errors.read().await;
|
||||
if !pe.is_empty() {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
pe.join(", ").to_string(),
|
||||
vec![source_range],
|
||||
)));
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
"Modeling command failed: websocket closed early".to_string(),
|
||||
vec![source_range],
|
||||
)));
|
||||
@ -543,7 +542,7 @@ impl EngineManager for EngineConnection {
|
||||
}
|
||||
}
|
||||
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Modeling command timed out `{}`", id),
|
||||
vec![source_range],
|
||||
)))
|
||||
|
@ -147,19 +147,19 @@ impl EngineConnection {
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize id to source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -167,7 +167,7 @@ impl EngineConnection {
|
||||
|
||||
self.manager
|
||||
.fire_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -180,19 +180,19 @@ impl EngineConnection {
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to serialize id to source range: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -201,7 +201,7 @@ impl EngineConnection {
|
||||
let promise = self
|
||||
.manager
|
||||
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
// Try to parse the error as an engine error.
|
||||
@ -209,7 +209,7 @@ impl EngineConnection {
|
||||
if let Ok(kittycad_modeling_cmds::websocket::FailureWebSocketResponse { errors, .. }) =
|
||||
serde_json::from_str(&err_str)
|
||||
{
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -218,7 +218,7 @@ impl EngineConnection {
|
||||
{
|
||||
if let Some(data) = data.first() {
|
||||
// It could also be an array of responses.
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
data.errors
|
||||
.iter()
|
||||
.map(|e| e.message.clone())
|
||||
@ -227,13 +227,13 @@ impl EngineConnection {
|
||||
vec![source_range],
|
||||
))
|
||||
} else {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
"Received empty response from engine".into(),
|
||||
vec![source_range],
|
||||
))
|
||||
}
|
||||
} else {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from send modeling command: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -241,7 +241,7 @@ impl EngineConnection {
|
||||
})?;
|
||||
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
"Received null or undefined response from engine".into(),
|
||||
vec![source_range],
|
||||
)));
|
||||
@ -251,7 +251,7 @@ impl EngineConnection {
|
||||
let data = js_sys::Uint8Array::from(value);
|
||||
|
||||
let ws_result: WebSocketResponse = bson::from_slice(&data.to_vec()).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to deserialize bson response from engine: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -308,10 +308,10 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
let promise = self
|
||||
.manager
|
||||
.start_new_session()
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from start new session: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
|
@ -276,7 +276,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
{
|
||||
let duration = instant::Duration::from_millis(1);
|
||||
wasm_timer::Delay::new(duration).await.map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to sleep: {:?}", err),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -293,7 +293,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
Err(KclError::new_engine(KclErrorDetails::new(
|
||||
"async command timed out".to_string(),
|
||||
vec![source_range],
|
||||
)))
|
||||
@ -547,7 +547,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
id_to_source_range.insert(Uuid::from(*cmd_id), *range);
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("The request is not a modeling command: {:?}", req),
|
||||
vec![*range],
|
||||
)));
|
||||
@ -595,7 +595,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
self.parse_batch_responses(last_id.into(), id_to_source_range, responses)
|
||||
} else {
|
||||
// We should never get here.
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to get batch response: {:?}", response),
|
||||
vec![source_range],
|
||||
)))
|
||||
@ -610,7 +610,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
// request so we need the original request source range in case the engine returns
|
||||
// an error.
|
||||
let source_range = id_to_source_range.get(cmd_id.as_ref()).cloned().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
vec![],
|
||||
))
|
||||
@ -620,7 +620,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
.await?;
|
||||
self.parse_websocket_response(ws_resp, source_range)
|
||||
}
|
||||
_ => Err(KclError::Engine(KclErrorDetails::new(
|
||||
_ => Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("The final request is not a modeling command: {:?}", final_req),
|
||||
vec![source_range],
|
||||
))),
|
||||
@ -729,7 +729,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
for (name, plane_id, color) in plane_settings {
|
||||
let info = DEFAULT_PLANE_INFO.get(&name).ok_or_else(|| {
|
||||
// We should never get here.
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to get default plane info for: {:?}", name),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -763,7 +763,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
WebSocketResponse::Success(success) => Ok(success.resp),
|
||||
WebSocketResponse::Failure(fail) => {
|
||||
let _request_id = fail.request_id;
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
Err(KclError::new_engine(KclErrorDetails::new(
|
||||
fail.errors
|
||||
.iter()
|
||||
.map(|e| e.message.clone())
|
||||
@ -805,12 +805,12 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
BatchResponse::Failure { errors } => {
|
||||
// Get the source range for the command.
|
||||
let source_range = id_to_source_range.get(cmd_id).cloned().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to get source range for command ID: {:?}", cmd_id),
|
||||
vec![],
|
||||
))
|
||||
})?;
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
|
||||
vec![source_range],
|
||||
)));
|
||||
@ -820,7 +820,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
|
||||
// Return an error that we did not get an error or the response we wanted.
|
||||
// This should never happen but who knows.
|
||||
Err(KclError::Engine(KclErrorDetails::new(
|
||||
Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to find response for command ID: {:?}", id),
|
||||
vec![],
|
||||
)))
|
||||
|
@ -91,30 +91,33 @@ pub enum ConnectionError {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum KclError {
|
||||
#[error("lexical: {0:?}")]
|
||||
Lexical(KclErrorDetails),
|
||||
#[error("syntax: {0:?}")]
|
||||
Syntax(KclErrorDetails),
|
||||
#[error("semantic: {0:?}")]
|
||||
Semantic(KclErrorDetails),
|
||||
#[error("import cycle: {0:?}")]
|
||||
ImportCycle(KclErrorDetails),
|
||||
#[error("type: {0:?}")]
|
||||
Type(KclErrorDetails),
|
||||
#[error("i/o: {0:?}")]
|
||||
Io(KclErrorDetails),
|
||||
#[error("unexpected: {0:?}")]
|
||||
Unexpected(KclErrorDetails),
|
||||
#[error("value already defined: {0:?}")]
|
||||
ValueAlreadyDefined(KclErrorDetails),
|
||||
#[error("undefined value: {0:?}")]
|
||||
UndefinedValue(KclErrorDetails),
|
||||
#[error("invalid expression: {0:?}")]
|
||||
InvalidExpression(KclErrorDetails),
|
||||
#[error("engine: {0:?}")]
|
||||
Engine(KclErrorDetails),
|
||||
#[error("internal error, please report to KittyCAD team: {0:?}")]
|
||||
Internal(KclErrorDetails),
|
||||
#[error("lexical: {details:?}")]
|
||||
Lexical { details: KclErrorDetails },
|
||||
#[error("syntax: {details:?}")]
|
||||
Syntax { details: KclErrorDetails },
|
||||
#[error("semantic: {details:?}")]
|
||||
Semantic { details: KclErrorDetails },
|
||||
#[error("import cycle: {details:?}")]
|
||||
ImportCycle { details: KclErrorDetails },
|
||||
#[error("type: {details:?}")]
|
||||
Type { details: KclErrorDetails },
|
||||
#[error("i/o: {details:?}")]
|
||||
Io { details: KclErrorDetails },
|
||||
#[error("unexpected: {details:?}")]
|
||||
Unexpected { details: KclErrorDetails },
|
||||
#[error("value already defined: {details:?}")]
|
||||
ValueAlreadyDefined { details: KclErrorDetails },
|
||||
#[error("undefined value: {details:?}")]
|
||||
UndefinedValue {
|
||||
details: KclErrorDetails,
|
||||
name: Option<String>,
|
||||
},
|
||||
#[error("invalid expression: {details:?}")]
|
||||
InvalidExpression { details: KclErrorDetails },
|
||||
#[error("engine: {details:?}")]
|
||||
Engine { details: KclErrorDetails },
|
||||
#[error("internal error, please report to KittyCAD team: {details:?}")]
|
||||
Internal { details: KclErrorDetails },
|
||||
}
|
||||
|
||||
impl From<KclErrorWithOutputs> for KclError {
|
||||
@ -296,18 +299,18 @@ pub struct ReportWithOutputs {
|
||||
impl miette::Diagnostic for ReportWithOutputs {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
|
||||
let family = match self.error.error {
|
||||
KclError::Lexical(_) => "Lexical",
|
||||
KclError::Syntax(_) => "Syntax",
|
||||
KclError::Semantic(_) => "Semantic",
|
||||
KclError::ImportCycle(_) => "ImportCycle",
|
||||
KclError::Type(_) => "Type",
|
||||
KclError::Io(_) => "I/O",
|
||||
KclError::Unexpected(_) => "Unexpected",
|
||||
KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
|
||||
KclError::UndefinedValue(_) => "UndefinedValue",
|
||||
KclError::InvalidExpression(_) => "InvalidExpression",
|
||||
KclError::Engine(_) => "Engine",
|
||||
KclError::Internal(_) => "Internal",
|
||||
KclError::Lexical { .. } => "Lexical",
|
||||
KclError::Syntax { .. } => "Syntax",
|
||||
KclError::Semantic { .. } => "Semantic",
|
||||
KclError::ImportCycle { .. } => "ImportCycle",
|
||||
KclError::Type { .. } => "Type",
|
||||
KclError::Io { .. } => "I/O",
|
||||
KclError::Unexpected { .. } => "Unexpected",
|
||||
KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
|
||||
KclError::UndefinedValue { .. } => "UndefinedValue",
|
||||
KclError::InvalidExpression { .. } => "InvalidExpression",
|
||||
KclError::Engine { .. } => "Engine",
|
||||
KclError::Internal { .. } => "Internal",
|
||||
};
|
||||
let error_string = format!("KCL {family} error");
|
||||
Some(Box::new(error_string))
|
||||
@ -346,18 +349,18 @@ pub struct Report {
|
||||
impl miette::Diagnostic for Report {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
|
||||
let family = match self.error {
|
||||
KclError::Lexical(_) => "Lexical",
|
||||
KclError::Syntax(_) => "Syntax",
|
||||
KclError::Semantic(_) => "Semantic",
|
||||
KclError::ImportCycle(_) => "ImportCycle",
|
||||
KclError::Type(_) => "Type",
|
||||
KclError::Io(_) => "I/O",
|
||||
KclError::Unexpected(_) => "Unexpected",
|
||||
KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
|
||||
KclError::UndefinedValue(_) => "UndefinedValue",
|
||||
KclError::InvalidExpression(_) => "InvalidExpression",
|
||||
KclError::Engine(_) => "Engine",
|
||||
KclError::Internal(_) => "Internal",
|
||||
KclError::Lexical { .. } => "Lexical",
|
||||
KclError::Syntax { .. } => "Syntax",
|
||||
KclError::Semantic { .. } => "Semantic",
|
||||
KclError::ImportCycle { .. } => "ImportCycle",
|
||||
KclError::Type { .. } => "Type",
|
||||
KclError::Io { .. } => "I/O",
|
||||
KclError::Unexpected { .. } => "Unexpected",
|
||||
KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
|
||||
KclError::UndefinedValue { .. } => "UndefinedValue",
|
||||
KclError::InvalidExpression { .. } => "InvalidExpression",
|
||||
KclError::Engine { .. } => "Engine",
|
||||
KclError::Internal { .. } => "Internal",
|
||||
};
|
||||
let error_string = format!("KCL {family} error");
|
||||
Some(Box::new(error_string))
|
||||
@ -410,11 +413,53 @@ impl KclErrorDetails {
|
||||
|
||||
impl KclError {
|
||||
pub fn internal(message: String) -> KclError {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
source_ranges: Default::default(),
|
||||
backtrace: Default::default(),
|
||||
message,
|
||||
})
|
||||
KclError::Internal {
|
||||
details: KclErrorDetails {
|
||||
source_ranges: Default::default(),
|
||||
backtrace: Default::default(),
|
||||
message,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_internal(details: KclErrorDetails) -> KclError {
|
||||
KclError::Internal { details }
|
||||
}
|
||||
|
||||
pub fn new_import_cycle(details: KclErrorDetails) -> KclError {
|
||||
KclError::ImportCycle { details }
|
||||
}
|
||||
|
||||
pub fn new_semantic(details: KclErrorDetails) -> KclError {
|
||||
KclError::Semantic { details }
|
||||
}
|
||||
|
||||
pub fn new_value_already_defined(details: KclErrorDetails) -> KclError {
|
||||
KclError::ValueAlreadyDefined { details }
|
||||
}
|
||||
|
||||
pub fn new_syntax(details: KclErrorDetails) -> KclError {
|
||||
KclError::Syntax { details }
|
||||
}
|
||||
|
||||
pub fn new_io(details: KclErrorDetails) -> KclError {
|
||||
KclError::Io { details }
|
||||
}
|
||||
|
||||
pub fn new_engine(details: KclErrorDetails) -> KclError {
|
||||
KclError::Engine { details }
|
||||
}
|
||||
|
||||
pub fn new_lexical(details: KclErrorDetails) -> KclError {
|
||||
KclError::Lexical { details }
|
||||
}
|
||||
|
||||
pub fn new_undefined_value(details: KclErrorDetails, name: Option<String>) -> KclError {
|
||||
KclError::UndefinedValue { details, name }
|
||||
}
|
||||
|
||||
pub fn new_type(details: KclErrorDetails) -> KclError {
|
||||
KclError::Type { details }
|
||||
}
|
||||
|
||||
/// Get the error message.
|
||||
@ -424,88 +469,88 @@ impl KclError {
|
||||
|
||||
pub fn error_type(&self) -> &'static str {
|
||||
match self {
|
||||
KclError::Lexical(_) => "lexical",
|
||||
KclError::Syntax(_) => "syntax",
|
||||
KclError::Semantic(_) => "semantic",
|
||||
KclError::ImportCycle(_) => "import cycle",
|
||||
KclError::Type(_) => "type",
|
||||
KclError::Io(_) => "i/o",
|
||||
KclError::Unexpected(_) => "unexpected",
|
||||
KclError::ValueAlreadyDefined(_) => "value already defined",
|
||||
KclError::UndefinedValue(_) => "undefined value",
|
||||
KclError::InvalidExpression(_) => "invalid expression",
|
||||
KclError::Engine(_) => "engine",
|
||||
KclError::Internal(_) => "internal",
|
||||
KclError::Lexical { .. } => "lexical",
|
||||
KclError::Syntax { .. } => "syntax",
|
||||
KclError::Semantic { .. } => "semantic",
|
||||
KclError::ImportCycle { .. } => "import cycle",
|
||||
KclError::Type { .. } => "type",
|
||||
KclError::Io { .. } => "i/o",
|
||||
KclError::Unexpected { .. } => "unexpected",
|
||||
KclError::ValueAlreadyDefined { .. } => "value already defined",
|
||||
KclError::UndefinedValue { .. } => "undefined value",
|
||||
KclError::InvalidExpression { .. } => "invalid expression",
|
||||
KclError::Engine { .. } => "engine",
|
||||
KclError::Internal { .. } => "internal",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn source_ranges(&self) -> Vec<SourceRange> {
|
||||
match &self {
|
||||
KclError::Lexical(e) => e.source_ranges.clone(),
|
||||
KclError::Syntax(e) => e.source_ranges.clone(),
|
||||
KclError::Semantic(e) => e.source_ranges.clone(),
|
||||
KclError::ImportCycle(e) => e.source_ranges.clone(),
|
||||
KclError::Type(e) => e.source_ranges.clone(),
|
||||
KclError::Io(e) => e.source_ranges.clone(),
|
||||
KclError::Unexpected(e) => e.source_ranges.clone(),
|
||||
KclError::ValueAlreadyDefined(e) => e.source_ranges.clone(),
|
||||
KclError::UndefinedValue(e) => e.source_ranges.clone(),
|
||||
KclError::InvalidExpression(e) => e.source_ranges.clone(),
|
||||
KclError::Engine(e) => e.source_ranges.clone(),
|
||||
KclError::Internal(e) => e.source_ranges.clone(),
|
||||
KclError::Lexical { details: e } => e.source_ranges.clone(),
|
||||
KclError::Syntax { details: e } => e.source_ranges.clone(),
|
||||
KclError::Semantic { details: e } => e.source_ranges.clone(),
|
||||
KclError::ImportCycle { details: e } => e.source_ranges.clone(),
|
||||
KclError::Type { details: e } => e.source_ranges.clone(),
|
||||
KclError::Io { details: e } => e.source_ranges.clone(),
|
||||
KclError::Unexpected { details: e } => e.source_ranges.clone(),
|
||||
KclError::ValueAlreadyDefined { details: e } => e.source_ranges.clone(),
|
||||
KclError::UndefinedValue { details: e, .. } => e.source_ranges.clone(),
|
||||
KclError::InvalidExpression { details: e } => e.source_ranges.clone(),
|
||||
KclError::Engine { details: e } => e.source_ranges.clone(),
|
||||
KclError::Internal { details: e } => e.source_ranges.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the inner error message.
|
||||
pub fn message(&self) -> &str {
|
||||
match &self {
|
||||
KclError::Lexical(e) => &e.message,
|
||||
KclError::Syntax(e) => &e.message,
|
||||
KclError::Semantic(e) => &e.message,
|
||||
KclError::ImportCycle(e) => &e.message,
|
||||
KclError::Type(e) => &e.message,
|
||||
KclError::Io(e) => &e.message,
|
||||
KclError::Unexpected(e) => &e.message,
|
||||
KclError::ValueAlreadyDefined(e) => &e.message,
|
||||
KclError::UndefinedValue(e) => &e.message,
|
||||
KclError::InvalidExpression(e) => &e.message,
|
||||
KclError::Engine(e) => &e.message,
|
||||
KclError::Internal(e) => &e.message,
|
||||
KclError::Lexical { details: e } => &e.message,
|
||||
KclError::Syntax { details: e } => &e.message,
|
||||
KclError::Semantic { details: e } => &e.message,
|
||||
KclError::ImportCycle { details: e } => &e.message,
|
||||
KclError::Type { details: e } => &e.message,
|
||||
KclError::Io { details: e } => &e.message,
|
||||
KclError::Unexpected { details: e } => &e.message,
|
||||
KclError::ValueAlreadyDefined { details: e } => &e.message,
|
||||
KclError::UndefinedValue { details: e, .. } => &e.message,
|
||||
KclError::InvalidExpression { details: e } => &e.message,
|
||||
KclError::Engine { details: e } => &e.message,
|
||||
KclError::Internal { details: e } => &e.message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backtrace(&self) -> Vec<BacktraceItem> {
|
||||
match self {
|
||||
KclError::Lexical(e)
|
||||
| KclError::Syntax(e)
|
||||
| KclError::Semantic(e)
|
||||
| KclError::ImportCycle(e)
|
||||
| KclError::Type(e)
|
||||
| KclError::Io(e)
|
||||
| KclError::Unexpected(e)
|
||||
| KclError::ValueAlreadyDefined(e)
|
||||
| KclError::UndefinedValue(e)
|
||||
| KclError::InvalidExpression(e)
|
||||
| KclError::Engine(e)
|
||||
| KclError::Internal(e) => e.backtrace.clone(),
|
||||
KclError::Lexical { details: e }
|
||||
| KclError::Syntax { details: e }
|
||||
| KclError::Semantic { details: e }
|
||||
| KclError::ImportCycle { details: e }
|
||||
| KclError::Type { details: e }
|
||||
| KclError::Io { details: e }
|
||||
| KclError::Unexpected { details: e }
|
||||
| KclError::ValueAlreadyDefined { details: e }
|
||||
| KclError::UndefinedValue { details: e, .. }
|
||||
| KclError::InvalidExpression { details: e }
|
||||
| KclError::Engine { details: e }
|
||||
| KclError::Internal { details: e } => e.backtrace.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn override_source_ranges(&self, source_ranges: Vec<SourceRange>) -> Self {
|
||||
let mut new = self.clone();
|
||||
match &mut new {
|
||||
KclError::Lexical(e)
|
||||
| KclError::Syntax(e)
|
||||
| KclError::Semantic(e)
|
||||
| KclError::ImportCycle(e)
|
||||
| KclError::Type(e)
|
||||
| KclError::Io(e)
|
||||
| KclError::Unexpected(e)
|
||||
| KclError::ValueAlreadyDefined(e)
|
||||
| KclError::UndefinedValue(e)
|
||||
| KclError::InvalidExpression(e)
|
||||
| KclError::Engine(e)
|
||||
| KclError::Internal(e) => {
|
||||
KclError::Lexical { details: e }
|
||||
| KclError::Syntax { details: e }
|
||||
| KclError::Semantic { details: e }
|
||||
| KclError::ImportCycle { details: e }
|
||||
| KclError::Type { details: e }
|
||||
| KclError::Io { details: e }
|
||||
| KclError::Unexpected { details: e }
|
||||
| KclError::ValueAlreadyDefined { details: e }
|
||||
| KclError::UndefinedValue { details: e, .. }
|
||||
| KclError::InvalidExpression { details: e }
|
||||
| KclError::Engine { details: e }
|
||||
| KclError::Internal { details: e } => {
|
||||
e.backtrace = source_ranges
|
||||
.iter()
|
||||
.map(|s| BacktraceItem {
|
||||
@ -523,18 +568,18 @@ impl KclError {
|
||||
pub(crate) fn set_last_backtrace_fn_name(&self, last_fn_name: Option<String>) -> Self {
|
||||
let mut new = self.clone();
|
||||
match &mut new {
|
||||
KclError::Lexical(e)
|
||||
| KclError::Syntax(e)
|
||||
| KclError::Semantic(e)
|
||||
| KclError::ImportCycle(e)
|
||||
| KclError::Type(e)
|
||||
| KclError::Io(e)
|
||||
| KclError::Unexpected(e)
|
||||
| KclError::ValueAlreadyDefined(e)
|
||||
| KclError::UndefinedValue(e)
|
||||
| KclError::InvalidExpression(e)
|
||||
| KclError::Engine(e)
|
||||
| KclError::Internal(e) => {
|
||||
KclError::Lexical { details: e }
|
||||
| KclError::Syntax { details: e }
|
||||
| KclError::Semantic { details: e }
|
||||
| KclError::ImportCycle { details: e }
|
||||
| KclError::Type { details: e }
|
||||
| KclError::Io { details: e }
|
||||
| KclError::Unexpected { details: e }
|
||||
| KclError::ValueAlreadyDefined { details: e }
|
||||
| KclError::UndefinedValue { details: e, .. }
|
||||
| KclError::InvalidExpression { details: e }
|
||||
| KclError::Engine { details: e }
|
||||
| KclError::Internal { details: e } => {
|
||||
if let Some(item) = e.backtrace.last_mut() {
|
||||
item.fn_name = last_fn_name;
|
||||
}
|
||||
@ -547,18 +592,18 @@ impl KclError {
|
||||
pub(crate) fn add_unwind_location(&self, last_fn_name: Option<String>, source_range: SourceRange) -> Self {
|
||||
let mut new = self.clone();
|
||||
match &mut new {
|
||||
KclError::Lexical(e)
|
||||
| KclError::Syntax(e)
|
||||
| KclError::Semantic(e)
|
||||
| KclError::ImportCycle(e)
|
||||
| KclError::Type(e)
|
||||
| KclError::Io(e)
|
||||
| KclError::Unexpected(e)
|
||||
| KclError::ValueAlreadyDefined(e)
|
||||
| KclError::UndefinedValue(e)
|
||||
| KclError::InvalidExpression(e)
|
||||
| KclError::Engine(e)
|
||||
| KclError::Internal(e) => {
|
||||
KclError::Lexical { details: e }
|
||||
| KclError::Syntax { details: e }
|
||||
| KclError::Semantic { details: e }
|
||||
| KclError::ImportCycle { details: e }
|
||||
| KclError::Type { details: e }
|
||||
| KclError::Io { details: e }
|
||||
| KclError::Unexpected { details: e }
|
||||
| KclError::ValueAlreadyDefined { details: e }
|
||||
| KclError::UndefinedValue { details: e, .. }
|
||||
| KclError::InvalidExpression { details: e }
|
||||
| KclError::Engine { details: e }
|
||||
| KclError::Internal { details: e } => {
|
||||
if let Some(item) = e.backtrace.last_mut() {
|
||||
item.fn_name = last_fn_name;
|
||||
}
|
||||
@ -645,7 +690,7 @@ impl From<String> for KclError {
|
||||
#[cfg(feature = "pyo3")]
|
||||
impl From<pyo3::PyErr> for KclError {
|
||||
fn from(error: pyo3::PyErr) -> Self {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
KclError::new_internal(KclErrorDetails {
|
||||
source_ranges: vec![],
|
||||
backtrace: Default::default(),
|
||||
message: error.to_string(),
|
||||
|
@ -70,7 +70,7 @@ pub(super) fn expect_properties<'a>(
|
||||
) -> Result<&'a [Node<ObjectProperty>], KclError> {
|
||||
assert_eq!(annotation.name().unwrap(), for_key);
|
||||
Ok(&**annotation.properties.as_ref().ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Empty `{for_key}` annotation"),
|
||||
vec![annotation.as_source_range()],
|
||||
))
|
||||
@ -84,7 +84,7 @@ pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {
|
||||
}
|
||||
}
|
||||
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Unexpected settings value, expected a simple name, e.g., `mm`".to_owned(),
|
||||
vec![expr.into()],
|
||||
)))
|
||||
@ -98,7 +98,7 @@ pub(super) fn expect_number(expr: &Expr) -> Result<String, KclError> {
|
||||
}
|
||||
}
|
||||
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Unexpected settings value, expected a number, e.g., `1.0`".to_owned(),
|
||||
vec![expr.into()],
|
||||
)))
|
||||
@ -113,7 +113,7 @@ pub(super) fn get_impl(annotations: &[Node<Annotation>], source_range: SourceRan
|
||||
if &*p.key.name == IMPL {
|
||||
if let Some(s) = p.value.ident_name() {
|
||||
return Impl::from_str(s).map(Some).map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Invalid value for {} attribute, expected one of: {}",
|
||||
IMPL,
|
||||
@ -139,7 +139,7 @@ impl UnitLen {
|
||||
"inch" | "in" => Ok(UnitLen::Inches),
|
||||
"ft" => Ok(UnitLen::Feet),
|
||||
"yd" => Ok(UnitLen::Yards),
|
||||
value => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
value => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Unexpected value for length units: `{value}`; expected one of `mm`, `cm`, `m`, `in`, `ft`, `yd`"
|
||||
),
|
||||
@ -154,7 +154,7 @@ impl UnitAngle {
|
||||
match s {
|
||||
"deg" => Ok(UnitAngle::Degrees),
|
||||
"rad" => Ok(UnitAngle::Radians),
|
||||
value => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
value => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Unexpected value for angle units: `{value}`; expected one of `deg`, `rad`"),
|
||||
vec![source_range],
|
||||
))),
|
||||
|
@ -24,7 +24,7 @@ macro_rules! internal_error {
|
||||
($range:expr, $($rest:tt)*) => {{
|
||||
let message = format!($($rest)*);
|
||||
debug_assert!(false, "{}", &message);
|
||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![$range])));
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(message, vec![$range])));
|
||||
}};
|
||||
}
|
||||
|
||||
@ -949,7 +949,7 @@ fn artifacts_to_update(
|
||||
ModelingCmd::StartPath(_) => {
|
||||
let mut return_arr = Vec::new();
|
||||
let current_plane_id = path_to_plane_id_map.get(&artifact_command.cmd_id).ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Expected a current plane ID when processing StartPath command, but we have none: {id:?}"),
|
||||
vec![range],
|
||||
))
|
||||
@ -1137,7 +1137,7 @@ fn artifacts_to_update(
|
||||
// TODO: Using the first one. Make sure to revisit this
|
||||
// choice, don't think it matters for now.
|
||||
path_id: ArtifactId::new(*loft_cmd.section_ids.first().ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Expected at least one section ID in Loft command: {id:?}; cmd={cmd:?}"),
|
||||
vec![range],
|
||||
))
|
||||
@ -1180,7 +1180,7 @@ fn artifacts_to_update(
|
||||
};
|
||||
last_path = Some(path);
|
||||
let path_sweep_id = path.sweep_id.ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
|
||||
),
|
||||
@ -1234,7 +1234,7 @@ fn artifacts_to_update(
|
||||
continue;
|
||||
};
|
||||
let path_sweep_id = path.sweep_id.ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected a sweep ID on the path when processing last path's Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
|
||||
),
|
||||
|
@ -131,7 +131,7 @@ impl ExecutorContext {
|
||||
match statement {
|
||||
BodyItem::ImportStatement(import_stmt) => {
|
||||
if !matches!(body_type, BodyType::Root) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Imports are only supported at the top-level of a file.".to_owned(),
|
||||
vec![import_stmt.into()],
|
||||
)));
|
||||
@ -164,15 +164,18 @@ impl ExecutorContext {
|
||||
let mut mod_value = mem.get_from(&mod_name, env_ref, import_item.into(), 0).cloned();
|
||||
|
||||
if value.is_err() && ty.is_err() && mod_value.is_err() {
|
||||
return Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("{} is not defined in module", import_item.name.name),
|
||||
vec![SourceRange::from(&import_item.name)],
|
||||
)));
|
||||
return Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(
|
||||
format!("{} is not defined in module", import_item.name.name),
|
||||
vec![SourceRange::from(&import_item.name)],
|
||||
),
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
// Check that the item is allowed to be imported (in at least one namespace).
|
||||
if value.is_ok() && !module_exports.contains(&import_item.name.name) {
|
||||
value = Err(KclError::Semantic(KclErrorDetails::new(
|
||||
value = Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
|
||||
import_item.name.name
|
||||
@ -182,7 +185,7 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
if ty.is_ok() && !module_exports.contains(&ty_name) {
|
||||
ty = Err(KclError::Semantic(KclErrorDetails::new(format!(
|
||||
ty = Err(KclError::new_semantic(KclErrorDetails::new(format!(
|
||||
"Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
|
||||
import_item.name.name
|
||||
),
|
||||
@ -190,7 +193,7 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
if mod_value.is_ok() && !module_exports.contains(&mod_name) {
|
||||
mod_value = Err(KclError::Semantic(KclErrorDetails::new(format!(
|
||||
mod_value = Err(KclError::new_semantic(KclErrorDetails::new(format!(
|
||||
"Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
|
||||
import_item.name.name
|
||||
),
|
||||
@ -253,7 +256,7 @@ impl ExecutorContext {
|
||||
.memory
|
||||
.get_from(name, env_ref, source_range, 0)
|
||||
.map_err(|_err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("{} is not defined in module (but was exported?)", name),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -301,7 +304,11 @@ impl ExecutorContext {
|
||||
|
||||
let annotations = &variable_declaration.outer_attrs;
|
||||
|
||||
let value = self
|
||||
// During the evaluation of the variable's RHS, set context that this is all happening inside a variable
|
||||
// declaration, for the given name. This helps improve user-facing error messages.
|
||||
let lhs = variable_declaration.inner.name().to_owned();
|
||||
exec_state.mod_local.being_declared = Some(lhs);
|
||||
let rhs_result = self
|
||||
.execute_expr(
|
||||
&variable_declaration.declaration.init,
|
||||
exec_state,
|
||||
@ -309,10 +316,14 @@ impl ExecutorContext {
|
||||
annotations,
|
||||
StatementKind::Declaration { name: &var_name },
|
||||
)
|
||||
.await?;
|
||||
.await;
|
||||
// Declaration over, so unset this context.
|
||||
exec_state.mod_local.being_declared = None;
|
||||
let rhs = rhs_result?;
|
||||
|
||||
exec_state
|
||||
.mut_stack()
|
||||
.add(var_name.clone(), value.clone(), source_range)?;
|
||||
.add(var_name.clone(), rhs.clone(), source_range)?;
|
||||
|
||||
// Track exports.
|
||||
if let ItemVisibility::Export = variable_declaration.visibility {
|
||||
@ -326,7 +337,7 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
// Variable declaration can be the return value of a module.
|
||||
last_expr = matches!(body_type, BodyType::Root).then_some(value);
|
||||
last_expr = matches!(body_type, BodyType::Root).then_some(rhs);
|
||||
}
|
||||
BodyItem::TypeDeclaration(ty) => {
|
||||
let metadata = Metadata::from(&**ty);
|
||||
@ -336,7 +347,7 @@ impl ExecutorContext {
|
||||
let std_path = match &exec_state.mod_local.path {
|
||||
ModulePath::Std { value } => value,
|
||||
ModulePath::Local { .. } | ModulePath::Main => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"User-defined types are not yet supported.".to_owned(),
|
||||
vec![metadata.source_range],
|
||||
)));
|
||||
@ -352,7 +363,7 @@ impl ExecutorContext {
|
||||
.mut_stack()
|
||||
.add(name_in_mem.clone(), value, metadata.source_range)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Redefinition of type {}.", ty.name.name),
|
||||
vec![metadata.source_range],
|
||||
))
|
||||
@ -373,7 +384,7 @@ impl ExecutorContext {
|
||||
exec_state,
|
||||
metadata.source_range,
|
||||
)
|
||||
.map_err(|e| KclError::Semantic(e.into()))?,
|
||||
.map_err(|e| KclError::new_semantic(e.into()))?,
|
||||
),
|
||||
meta: vec![metadata],
|
||||
};
|
||||
@ -382,7 +393,7 @@ impl ExecutorContext {
|
||||
.mut_stack()
|
||||
.add(name_in_mem.clone(), value, metadata.source_range)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Redefinition of type {}.", ty.name.name),
|
||||
vec![metadata.source_range],
|
||||
))
|
||||
@ -393,7 +404,7 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"User-defined types are not yet supported.".to_owned(),
|
||||
vec![metadata.source_range],
|
||||
)))
|
||||
@ -407,7 +418,7 @@ impl ExecutorContext {
|
||||
let metadata = Metadata::from(return_statement);
|
||||
|
||||
if matches!(body_type, BodyType::Root) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Cannot return from outside a function.".to_owned(),
|
||||
vec![metadata.source_range],
|
||||
)));
|
||||
@ -426,7 +437,7 @@ impl ExecutorContext {
|
||||
.mut_stack()
|
||||
.add(memory::RETURN_NAME.to_owned(), value, metadata.source_range)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"Multiple returns from a single function.".to_owned(),
|
||||
vec![metadata.source_range],
|
||||
))
|
||||
@ -531,7 +542,7 @@ impl ExecutorContext {
|
||||
*cache = Some((val, er, items.clone()));
|
||||
(er, items)
|
||||
}),
|
||||
ModuleRepr::Foreign(geom, _) => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
ModuleRepr::Foreign(geom, _) => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Cannot import items from foreign modules".to_owned(),
|
||||
vec![geom.source_range],
|
||||
))),
|
||||
@ -605,12 +616,12 @@ impl ExecutorContext {
|
||||
exec_state.global.mod_loader.leave_module(path);
|
||||
|
||||
result.map_err(|err| {
|
||||
if let KclError::ImportCycle(_) = err {
|
||||
if let KclError::ImportCycle { .. } = err {
|
||||
// It was an import cycle. Keep the original message.
|
||||
err.override_source_ranges(vec![source_range])
|
||||
} else {
|
||||
// TODO would be great to have line/column for the underlying error here
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Error loading imported file ({path}). Open it to view more details.\n {}",
|
||||
err.message()
|
||||
@ -635,7 +646,12 @@ impl ExecutorContext {
|
||||
Expr::Literal(literal) => KclValue::from_literal((**literal).clone(), exec_state),
|
||||
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
|
||||
Expr::Name(name) => {
|
||||
let value = name.get_result(exec_state, self).await?.clone();
|
||||
let being_declared = exec_state.mod_local.being_declared.clone();
|
||||
let value = name
|
||||
.get_result(exec_state, self)
|
||||
.await
|
||||
.map_err(|e| var_in_own_ref_err(e, &being_declared))?
|
||||
.clone();
|
||||
if let KclValue::Module { value: module_id, meta } = value {
|
||||
self.exec_module_for_result(
|
||||
module_id,
|
||||
@ -677,7 +693,7 @@ impl ExecutorContext {
|
||||
meta: vec![metadata.to_owned()],
|
||||
}
|
||||
} else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Rust implementation of functions is restricted to the standard library".to_owned(),
|
||||
vec![metadata.source_range],
|
||||
)));
|
||||
@ -704,7 +720,7 @@ impl ExecutorContext {
|
||||
"you cannot declare variable {name} as %, because % can only be used in function calls"
|
||||
);
|
||||
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
message,
|
||||
vec![pipe_substitution.into()],
|
||||
)));
|
||||
@ -712,7 +728,7 @@ impl ExecutorContext {
|
||||
StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"cannot use % outside a pipe expression".to_owned(),
|
||||
vec![pipe_substitution.into()],
|
||||
)));
|
||||
@ -741,6 +757,24 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// If the error is about an undefined name, and that name matches the name being defined,
|
||||
/// make the error message more specific.
|
||||
fn var_in_own_ref_err(e: KclError, being_declared: &Option<String>) -> KclError {
|
||||
let KclError::UndefinedValue { name, mut details } = e else {
|
||||
return e;
|
||||
};
|
||||
// TODO after June 26th: replace this with a let-chain,
|
||||
// which will be available in Rust 1.88
|
||||
// https://rust-lang.github.io/rfcs/2497-if-let-chains.html
|
||||
match (&being_declared, &name) {
|
||||
(Some(name0), Some(name1)) if name0 == name1 => {
|
||||
details.message = format!("You can't use `{name0}` because you're currently trying to define it. Use a different variable here instead.");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
KclError::UndefinedValue { details, name }
|
||||
}
|
||||
|
||||
impl Node<AscribedExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
@ -761,7 +795,7 @@ fn apply_ascription(
|
||||
source_range: SourceRange,
|
||||
) -> Result<KclValue, KclError> {
|
||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||
.map_err(|e| KclError::new_semantic(e.into()))?;
|
||||
|
||||
value.coerce(&ty, false, exec_state).map_err(|_| {
|
||||
let suggestion = if ty == RuntimeType::length() {
|
||||
@ -771,7 +805,7 @@ fn apply_ascription(
|
||||
} else {
|
||||
""
|
||||
};
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"could not coerce value of type {} to type {ty}{suggestion}",
|
||||
value.human_friendly_type()
|
||||
@ -804,7 +838,7 @@ impl Node<Name> {
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<&'a KclValue, KclError> {
|
||||
if self.abs_path {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Absolute paths (names beginning with `::` are not yet supported)".to_owned(),
|
||||
self.as_source_ranges(),
|
||||
)));
|
||||
@ -825,7 +859,7 @@ impl Node<Name> {
|
||||
let value = match mem_spec {
|
||||
Some((env, exports)) => {
|
||||
if !exports.contains(&p.name) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Item {} not found in module's exported items", p.name),
|
||||
p.as_source_ranges(),
|
||||
)));
|
||||
@ -842,7 +876,7 @@ impl Node<Name> {
|
||||
};
|
||||
|
||||
let KclValue::Module { value: module_id, .. } = value else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Identifier in path must refer to a module, found {}",
|
||||
value.human_friendly_type()
|
||||
@ -888,7 +922,7 @@ impl Node<Name> {
|
||||
|
||||
// Either item or module is defined, but not exported.
|
||||
debug_assert!((item_value.is_ok() && !item_exported) || (mod_value.is_ok() && !mod_exported));
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Item {} not found in module's exported items", self.name.name),
|
||||
self.name.as_source_ranges(),
|
||||
)))
|
||||
@ -913,14 +947,17 @@ impl Node<MemberExpression> {
|
||||
if let Some(value) = map.get(&property) {
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("Property '{property}' not found in object"),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(
|
||||
format!("Property '{property}' not found in object"),
|
||||
vec![self.clone().into()],
|
||||
),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
(KclValue::Object { .. }, Property::String(property), true) => {
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Cannot index object with string; use dot notation instead, e.g. `obj.{property}`"),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
@ -928,7 +965,7 @@ impl Node<MemberExpression> {
|
||||
(KclValue::Object { .. }, p, _) => {
|
||||
let t = p.type_name();
|
||||
let article = article_for(t);
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Only strings can be used as the property of an object, but you're using {article} {t}",),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
@ -938,10 +975,13 @@ impl Node<MemberExpression> {
|
||||
if let Some(value) = value_of_arr {
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("The array doesn't have any item at index {index}"),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(
|
||||
format!("The array doesn't have any item at index {index}"),
|
||||
vec![self.clone().into()],
|
||||
),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
// Singletons and single-element arrays should be interchangeable, but only indexing by 0 should work.
|
||||
@ -950,7 +990,7 @@ impl Node<MemberExpression> {
|
||||
(KclValue::HomArray { .. }, p, _) => {
|
||||
let t = p.type_name();
|
||||
let article = article_for(t);
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Only integers >= 0 can be used as the index of an array, but you're using {article} {t}",),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
@ -971,7 +1011,7 @@ impl Node<MemberExpression> {
|
||||
(being_indexed, _, _) => {
|
||||
let t = being_indexed.human_friendly_type();
|
||||
let article = article_for(&t);
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Only arrays can be indexed, but you're trying to index {article} {t}"),
|
||||
vec![self.clone().into()],
|
||||
)))
|
||||
@ -1049,7 +1089,7 @@ impl Node<BinaryExpression> {
|
||||
meta: _,
|
||||
} = left_value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Cannot apply logical operator to non-boolean value: {}",
|
||||
left_value.human_friendly_type()
|
||||
@ -1062,7 +1102,7 @@ impl Node<BinaryExpression> {
|
||||
meta: _,
|
||||
} = right_value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Cannot apply logical operator to non-boolean value: {}",
|
||||
right_value.human_friendly_type()
|
||||
@ -1168,7 +1208,7 @@ impl Node<UnaryExpression> {
|
||||
meta: _,
|
||||
} = value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Cannot apply unary operator ! to non-boolean value: {}",
|
||||
value.human_friendly_type()
|
||||
@ -1189,7 +1229,7 @@ impl Node<UnaryExpression> {
|
||||
|
||||
let value = &self.argument.get_result(exec_state, ctx).await?;
|
||||
let err = || {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"You can only negate numbers, planes, or lines, but this is a {}",
|
||||
value.human_friendly_type()
|
||||
@ -1292,7 +1332,7 @@ pub(crate) async fn execute_pipe_body(
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<KclValue, KclError> {
|
||||
let Some((first, body)) = body.split_first() else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Pipe expressions cannot be empty".to_owned(),
|
||||
vec![source_range],
|
||||
)));
|
||||
@ -1311,7 +1351,7 @@ pub(crate) async fn execute_pipe_body(
|
||||
// Now that we've evaluated the first child expression in the pipeline, following child expressions
|
||||
// should use the previous child expression for %.
|
||||
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
|
||||
let previous_pipe_value = std::mem::replace(&mut exec_state.mod_local.pipe_value, Some(output));
|
||||
let previous_pipe_value = exec_state.mod_local.pipe_value.replace(output);
|
||||
// Evaluate remaining elements.
|
||||
let result = inner_execute_pipe_body(exec_state, body, ctx).await;
|
||||
// Restore the previous pipe value.
|
||||
@ -1330,7 +1370,7 @@ async fn inner_execute_pipe_body(
|
||||
) -> Result<KclValue, KclError> {
|
||||
for expression in body {
|
||||
if let Expr::TagDeclarator(_) = expression {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("This cannot be in a PipeExpression: {:?}", expression),
|
||||
vec![expression.into()],
|
||||
)));
|
||||
@ -1404,7 +1444,7 @@ impl Node<ArrayRangeExpression> {
|
||||
.await?;
|
||||
let (start, start_ty) = start_val
|
||||
.as_int_with_ty()
|
||||
.ok_or(KclError::Semantic(KclErrorDetails::new(
|
||||
.ok_or(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected int but found {}", start_val.human_friendly_type()),
|
||||
vec![self.into()],
|
||||
)))?;
|
||||
@ -1412,24 +1452,26 @@ impl Node<ArrayRangeExpression> {
|
||||
let end_val = ctx
|
||||
.execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
|
||||
.await?;
|
||||
let (end, end_ty) = end_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails::new(
|
||||
format!("Expected int but found {}", end_val.human_friendly_type()),
|
||||
vec![self.into()],
|
||||
)))?;
|
||||
let (end, end_ty) = end_val
|
||||
.as_int_with_ty()
|
||||
.ok_or(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected int but found {}", end_val.human_friendly_type()),
|
||||
vec![self.into()],
|
||||
)))?;
|
||||
|
||||
if start_ty != end_ty {
|
||||
let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
|
||||
let start = fmt::human_display_number(start.n, start.ty);
|
||||
let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
|
||||
let end = fmt::human_display_number(end.n, end.ty);
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Range start and end must be of the same type, but found {start} and {end}"),
|
||||
vec![self.into()],
|
||||
)));
|
||||
}
|
||||
|
||||
if end < start {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Range start is greater than range end: {start} .. {end}"),
|
||||
vec![self.into()],
|
||||
)));
|
||||
@ -1493,7 +1535,7 @@ fn article_for<S: AsRef<str>>(s: S) -> &'static str {
|
||||
fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
|
||||
v.as_ty_f64().ok_or_else(|| {
|
||||
let actual_type = v.human_friendly_type();
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected a number, but found {actual_type}",),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -1585,13 +1627,13 @@ impl Property {
|
||||
if let Some(x) = crate::try_f64_to_usize(value) {
|
||||
Ok(Property::UInt(x))
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("{value} is not a valid index, indices must be whole numbers >= 0"),
|
||||
property_sr,
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
_ => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Only numbers (>= 0) can be indexes".to_owned(),
|
||||
vec![sr],
|
||||
))),
|
||||
@ -1602,7 +1644,8 @@ impl Property {
|
||||
}
|
||||
|
||||
fn jvalue_to_prop(value: &KclValue, property_sr: Vec<SourceRange>, name: &str) -> Result<Property, KclError> {
|
||||
let make_err = |message: String| Err::<Property, _>(KclError::Semantic(KclErrorDetails::new(message, property_sr)));
|
||||
let make_err =
|
||||
|message: String| Err::<Property, _>(KclError::new_semantic(KclErrorDetails::new(message, property_sr)));
|
||||
match value {
|
||||
KclValue::Number{value: num, .. } => {
|
||||
let num = *num;
|
||||
@ -1846,7 +1889,7 @@ d = b + c
|
||||
crate::engine::conn_mock::EngineConnection::new()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to create mock engine connection: {}", err),
|
||||
vec![SourceRange::default()],
|
||||
))
|
||||
|
@ -295,7 +295,7 @@ impl Node<CallExpressionKw> {
|
||||
let func = fn_name.get_result(exec_state, ctx).await?.clone();
|
||||
|
||||
let Some(fn_src) = func.as_function() else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"cannot call this because it isn't a function".to_string(),
|
||||
vec![callsite],
|
||||
)));
|
||||
@ -318,10 +318,13 @@ impl Node<CallExpressionKw> {
|
||||
if let KclValue::Function { meta, .. } = func {
|
||||
source_ranges = meta.iter().map(|m| m.source_range).collect();
|
||||
};
|
||||
KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("Result of user-defined function {} is undefined", fn_name),
|
||||
source_ranges,
|
||||
))
|
||||
KclError::new_undefined_value(
|
||||
KclErrorDetails::new(
|
||||
format!("Result of user-defined function {} is undefined", fn_name),
|
||||
source_ranges,
|
||||
),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(result)
|
||||
@ -500,7 +503,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
|
||||
let mut t = t.clone();
|
||||
let Some(info) = t.get_cur_info() else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Tag {} does not have path info", tag.name),
|
||||
vec![tag.into()],
|
||||
)));
|
||||
@ -605,7 +608,7 @@ fn type_check_params_kw(
|
||||
arg.value = arg
|
||||
.value
|
||||
.coerce(
|
||||
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
|
||||
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.source_range).map_err(|e| KclError::new_semantic(e.into()))?,
|
||||
true,
|
||||
exec_state,
|
||||
)
|
||||
@ -619,7 +622,7 @@ fn type_check_params_kw(
|
||||
// TODO if we have access to the AST for the argument we could choose which example to suggest.
|
||||
message = format!("{message}\n\nYou may need to add information about the type of the argument, for example:\n using a numeric suffix: `42{ty}`\n or using type ascription: `foo(): number({ty})`");
|
||||
}
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
message,
|
||||
vec![arg.source_range],
|
||||
))
|
||||
@ -670,7 +673,7 @@ fn type_check_params_kw(
|
||||
let first = errors.next().unwrap();
|
||||
errors.for_each(|e| exec_state.err(e));
|
||||
|
||||
return Err(KclError::Semantic(first.into()));
|
||||
return Err(KclError::new_semantic(first.into()));
|
||||
}
|
||||
|
||||
if let Some(arg) = &mut args.unlabeled {
|
||||
@ -680,12 +683,12 @@ fn type_check_params_kw(
|
||||
.value
|
||||
.coerce(
|
||||
&RuntimeType::from_parsed(ty.clone(), exec_state, arg.1.source_range)
|
||||
.map_err(|e| KclError::Semantic(e.into()))?,
|
||||
.map_err(|e| KclError::new_semantic(e.into()))?,
|
||||
true,
|
||||
exec_state,
|
||||
)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"The input argument of {} requires a value with type `{}`, but found {}",
|
||||
fn_name
|
||||
@ -742,7 +745,7 @@ fn assign_args_to_params_kw(
|
||||
.add(name.clone(), value, default_val.source_range())?;
|
||||
}
|
||||
None => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"This function requires a parameter {}, but you haven't passed it one.",
|
||||
name
|
||||
@ -759,12 +762,12 @@ fn assign_args_to_params_kw(
|
||||
|
||||
let Some(unlabeled) = unlabelled else {
|
||||
return Err(if args.kw_args.labeled.contains_key(param_name) {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("The function does declare a parameter named '{param_name}', but this parameter doesn't use a label. Try removing the `{param_name}:`"),
|
||||
source_ranges,
|
||||
))
|
||||
} else {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"This function expects an unlabeled first parameter, but you haven't passed it one.".to_owned(),
|
||||
source_ranges,
|
||||
))
|
||||
@ -788,9 +791,9 @@ fn coerce_result_type(
|
||||
if let Ok(Some(val)) = result {
|
||||
if let Some(ret_ty) = &fn_def.return_type {
|
||||
let ty = RuntimeType::from_parsed(ret_ty.inner.clone(), exec_state, ret_ty.as_source_range())
|
||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||
.map_err(|e| KclError::new_semantic(e.into()))?;
|
||||
let val = val.coerce(&ty, true, exec_state).map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"This function requires its result to be of type `{}`, but found {}",
|
||||
ty.human_friendly_type(),
|
||||
@ -874,7 +877,7 @@ mod test {
|
||||
"all params required, none given, should error",
|
||||
vec![req_param("x")],
|
||||
vec![],
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"This function requires a parameter x, but you haven't passed it one.".to_owned(),
|
||||
vec![SourceRange::default()],
|
||||
))),
|
||||
@ -889,7 +892,7 @@ mod test {
|
||||
"mixed params, too few given",
|
||||
vec![req_param("x"), opt_param("y")],
|
||||
vec![],
|
||||
Err(KclError::Semantic(KclErrorDetails::new(
|
||||
Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"This function requires a parameter x, but you haven't passed it one.".to_owned(),
|
||||
vec![SourceRange::default()],
|
||||
))),
|
||||
|
@ -24,6 +24,7 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Geometry {
|
||||
Sketch(Sketch),
|
||||
Solid(Solid),
|
||||
@ -52,6 +53,7 @@ impl Geometry {
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum GeometryWithImportedGeometry {
|
||||
Sketch(Sketch),
|
||||
Solid(Solid),
|
||||
@ -469,7 +471,7 @@ impl TryFrom<PlaneData> for PlaneInfo {
|
||||
PlaneData::NegYZ => PlaneName::NegYz,
|
||||
PlaneData::Plane(_) => {
|
||||
// We will never get here since we already checked for PlaneData::Plane.
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
format!("PlaneData {:?} not found", value),
|
||||
Default::default(),
|
||||
)));
|
||||
@ -477,7 +479,7 @@ impl TryFrom<PlaneData> for PlaneInfo {
|
||||
};
|
||||
|
||||
let info = DEFAULT_PLANE_INFO.get(&name).ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Plane {} not found", name),
|
||||
Default::default(),
|
||||
))
|
||||
|
@ -37,25 +37,25 @@ pub async fn import_foreign(
|
||||
) -> Result<PreImportedGeometry, KclError> {
|
||||
// Make sure the file exists.
|
||||
if !ctxt.fs.exists(file_path, source_range).await? {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("File `{}` does not exist.", file_path.display()),
|
||||
vec![source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
let ext_format = get_import_format_from_extension(file_path.extension().ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("No file extension found for `{}`", file_path.display()),
|
||||
vec![source_range],
|
||||
))
|
||||
})?)
|
||||
.map_err(|e| KclError::Semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
|
||||
// Get the format type from the extension of the file.
|
||||
let format = if let Some(format) = format {
|
||||
// Validate the given format with the extension format.
|
||||
validate_extension_format(ext_format, format.clone())
|
||||
.map_err(|e| KclError::Semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
format
|
||||
} else {
|
||||
ext_format
|
||||
@ -66,11 +66,11 @@ pub async fn import_foreign(
|
||||
.fs
|
||||
.read(file_path, source_range)
|
||||
.await
|
||||
.map_err(|e| KclError::Semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
|
||||
// We want the file_path to be without the parent.
|
||||
let file_name = file_path.file_name().map(|p| p.to_string()).ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Could not get the file name from the path `{}`", file_path.display()),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -87,7 +87,7 @@ pub async fn import_foreign(
|
||||
// file.
|
||||
if !file_contents.starts_with(b"glTF") {
|
||||
let json = gltf_json::Root::from_slice(&file_contents)
|
||||
.map_err(|e| KclError::Semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_semantic(KclErrorDetails::new(e.to_string(), vec![source_range])))?;
|
||||
|
||||
// Read the gltf file and check if there is a bin file.
|
||||
for buffer in json.buffers.iter() {
|
||||
@ -95,16 +95,15 @@ pub async fn import_foreign(
|
||||
if !uri.starts_with("data:") {
|
||||
// We want this path relative to the file_path given.
|
||||
let bin_path = file_path.parent().map(|p| p.join(uri)).ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Could not get the parent path of the file `{}`", file_path.display()),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
let bin_contents =
|
||||
ctxt.fs.read(&bin_path, source_range).await.map_err(|e| {
|
||||
KclError::Semantic(KclErrorDetails::new(e.to_string(), vec![source_range]))
|
||||
})?;
|
||||
let bin_contents = ctxt.fs.read(&bin_path, source_range).await.map_err(|e| {
|
||||
KclError::new_semantic(KclErrorDetails::new(e.to_string(), vec![source_range]))
|
||||
})?;
|
||||
|
||||
import_files.push(ImportFile {
|
||||
path: uri.to_string(),
|
||||
@ -141,7 +140,7 @@ pub(super) fn format_from_annotations(
|
||||
if p.key.name == annotations::IMPORT_FORMAT {
|
||||
result = Some(
|
||||
get_import_format_from_extension(annotations::expect_ident(&p.value)?).map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Unknown format for import, expected one of: {}",
|
||||
crate::IMPORT_FILE_EXTENSIONS.join(", ")
|
||||
@ -159,7 +158,7 @@ pub(super) fn format_from_annotations(
|
||||
path.extension()
|
||||
.and_then(|ext| get_import_format_from_extension(ext).ok())
|
||||
})
|
||||
.ok_or(KclError::Semantic(KclErrorDetails::new(
|
||||
.ok_or(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Unknown or missing extension, and no specified format for imported file".to_owned(),
|
||||
vec![import_source_range],
|
||||
)))?;
|
||||
@ -174,7 +173,7 @@ pub(super) fn format_from_annotations(
|
||||
}
|
||||
annotations::IMPORT_FORMAT => {}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Unexpected annotation for import, expected one of: {}, {}, {}",
|
||||
annotations::IMPORT_FORMAT,
|
||||
@ -199,7 +198,7 @@ fn set_coords(fmt: &mut InputFormat3d, coords_str: &str, source_range: SourceRan
|
||||
}
|
||||
|
||||
let Some(coords) = coords else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Unknown coordinate system: {coords_str}, expected one of: {}",
|
||||
annotations::IMPORT_COORDS_VALUES
|
||||
@ -217,7 +216,7 @@ fn set_coords(fmt: &mut InputFormat3d, coords_str: &str, source_range: SourceRan
|
||||
InputFormat3d::Ply(opts) => opts.coords = coords,
|
||||
InputFormat3d::Stl(opts) => opts.coords = coords,
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"`{}` option cannot be applied to the specified format",
|
||||
annotations::IMPORT_COORDS
|
||||
@ -238,7 +237,7 @@ fn set_length_unit(fmt: &mut InputFormat3d, units_str: &str, source_range: Sourc
|
||||
InputFormat3d::Ply(opts) => opts.units = units.into(),
|
||||
InputFormat3d::Stl(opts) => opts.units = units.into(),
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"`{}` option cannot be applied to the specified format",
|
||||
annotations::IMPORT_LENGTH_UNIT
|
||||
|
@ -574,7 +574,7 @@ impl KclValue {
|
||||
pub fn get_tag_identifier(&self) -> Result<TagIdentifier, KclError> {
|
||||
match self {
|
||||
KclValue::TagIdentifier(t) => Ok(*t.clone()),
|
||||
_ => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
_ => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Not a tag identifier: {:?}", self),
|
||||
self.clone().into(),
|
||||
))),
|
||||
@ -585,7 +585,7 @@ impl KclValue {
|
||||
pub fn get_tag_declarator(&self) -> Result<TagNode, KclError> {
|
||||
match self {
|
||||
KclValue::TagDeclarator(t) => Ok((**t).clone()),
|
||||
_ => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
_ => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Not a tag declarator: {:?}", self),
|
||||
self.clone().into(),
|
||||
))),
|
||||
@ -595,7 +595,7 @@ impl KclValue {
|
||||
/// If this KCL value is a bool, retrieve it.
|
||||
pub fn get_bool(&self) -> Result<bool, KclError> {
|
||||
self.as_bool().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected bool, found {}", self.human_friendly_type()),
|
||||
self.into(),
|
||||
))
|
||||
|
@ -367,10 +367,10 @@ impl ProgramMemory {
|
||||
|
||||
let name = var.trim_start_matches(TYPE_PREFIX).trim_start_matches(MODULE_PREFIX);
|
||||
|
||||
Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("`{name}` is not defined"),
|
||||
vec![source_range],
|
||||
)))
|
||||
Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(format!("`{name}` is not defined"), vec![source_range]),
|
||||
Some(name.to_owned()),
|
||||
))
|
||||
}
|
||||
|
||||
/// Iterate over all key/value pairs in the specified environment which satisfy the provided
|
||||
@ -488,10 +488,10 @@ impl ProgramMemory {
|
||||
};
|
||||
}
|
||||
|
||||
Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
format!("`{}` is not defined", var),
|
||||
vec![],
|
||||
)))
|
||||
Err(KclError::new_undefined_value(
|
||||
KclErrorDetails::new(format!("`{}` is not defined", var), vec![]),
|
||||
Some(var.to_owned()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,7 +646,7 @@ impl Stack {
|
||||
pub fn add(&mut self, key: String, value: KclValue, source_range: SourceRange) -> Result<(), KclError> {
|
||||
let env = self.memory.get_env(self.current_env.index());
|
||||
if env.contains_key(&key) {
|
||||
return Err(KclError::ValueAlreadyDefined(KclErrorDetails::new(
|
||||
return Err(KclError::new_value_already_defined(KclErrorDetails::new(
|
||||
format!("Cannot redefine `{}`", key),
|
||||
vec![source_range],
|
||||
)));
|
||||
|
@ -858,7 +858,7 @@ impl ExecutorContext {
|
||||
|
||||
for module in modules {
|
||||
let Some((import_stmt, module_id, module_path, repr)) = universe.get(&module) else {
|
||||
return Err(KclErrorWithOutputs::no_outputs(KclError::Internal(
|
||||
return Err(KclErrorWithOutputs::no_outputs(KclError::new_internal(
|
||||
KclErrorDetails::new(format!("Module {module} not found in universe"), Default::default()),
|
||||
)));
|
||||
};
|
||||
@ -920,7 +920,7 @@ impl ExecutorContext {
|
||||
|
||||
result.map(|val| ModuleRepr::Foreign(geom.clone(), val))
|
||||
}
|
||||
ModuleRepr::Dummy | ModuleRepr::Root => Err(KclError::Internal(KclErrorDetails::new(
|
||||
ModuleRepr::Dummy | ModuleRepr::Root => Err(KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Module {module_path} not found in universe"),
|
||||
vec![source_range],
|
||||
))),
|
||||
@ -1283,7 +1283,7 @@ impl ExecutorContext {
|
||||
.await?;
|
||||
|
||||
let kittycad_modeling_cmds::websocket::OkWebSocketResponseData::Export { files } = resp else {
|
||||
return Err(KclError::Internal(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(crate::errors::KclErrorDetails::new(
|
||||
format!("Expected Export response, got {resp:?}",),
|
||||
vec![SourceRange::default()],
|
||||
)));
|
||||
@ -1303,7 +1303,7 @@ impl ExecutorContext {
|
||||
coords: *kittycad_modeling_cmds::coord::KITTYCAD,
|
||||
created: if deterministic_time {
|
||||
Some("2021-01-01T00:00:00Z".parse().map_err(|e| {
|
||||
KclError::Internal(crate::errors::KclErrorDetails::new(
|
||||
KclError::new_internal(crate::errors::KclErrorDetails::new(
|
||||
format!("Failed to parse date: {}", e),
|
||||
vec![SourceRange::default()],
|
||||
))
|
||||
@ -1383,7 +1383,7 @@ pub(crate) async fn parse_execute_with_project_dir(
|
||||
let exec_ctxt = ExecutorContext {
|
||||
engine: Arc::new(Box::new(
|
||||
crate::engine::conn_mock::EngineConnection::new().await.map_err(|err| {
|
||||
KclError::Internal(crate::errors::KclErrorDetails::new(
|
||||
KclError::new_internal(crate::errors::KclErrorDetails::new(
|
||||
format!("Failed to create mock engine connection: {}", err),
|
||||
vec![SourceRange::default()],
|
||||
))
|
||||
@ -1799,7 +1799,7 @@ foo
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
KclError::Syntax(KclErrorDetails::new(
|
||||
KclError::new_syntax(KclErrorDetails::new(
|
||||
"Unexpected token: #".to_owned(),
|
||||
vec![SourceRange::new(14, 15, ModuleId::default())],
|
||||
)),
|
||||
@ -2058,7 +2058,7 @@ notTagIdentifier = !myTag";
|
||||
// TODO: We don't currently parse this, but we should. It should be
|
||||
// a runtime error instead.
|
||||
parse_execute(code10).await.unwrap_err(),
|
||||
KclError::Syntax(KclErrorDetails::new(
|
||||
KclError::new_syntax(KclErrorDetails::new(
|
||||
"Unexpected token: !".to_owned(),
|
||||
vec![SourceRange::new(10, 11, ModuleId::default())],
|
||||
))
|
||||
@ -2071,9 +2071,9 @@ notPipeSub = 1 |> identity(!%))";
|
||||
// TODO: We don't currently parse this, but we should. It should be
|
||||
// a runtime error instead.
|
||||
parse_execute(code11).await.unwrap_err(),
|
||||
KclError::Syntax(KclErrorDetails::new(
|
||||
"Unexpected token: |>".to_owned(),
|
||||
vec![SourceRange::new(44, 46, ModuleId::default())],
|
||||
KclError::new_syntax(KclErrorDetails::new(
|
||||
"There was an unexpected !. Try removing it.".to_owned(),
|
||||
vec![SourceRange::new(56, 57, ModuleId::default())],
|
||||
))
|
||||
);
|
||||
|
||||
|
@ -80,6 +80,11 @@ pub(super) struct ModuleState {
|
||||
/// The current value of the pipe operator returned from the previous
|
||||
/// expression. If we're not currently in a pipeline, this will be None.
|
||||
pub pipe_value: Option<KclValue>,
|
||||
/// The closest variable declaration being executed in any parent node in the AST.
|
||||
/// This is used to provide better error messages, e.g. noticing when the user is trying
|
||||
/// to use the variable `length` inside the RHS of its own definition, like `length = tan(length)`.
|
||||
/// TODO: Make this a reference.
|
||||
pub being_declared: Option<String>,
|
||||
/// Identifiers that have been exported from the current module.
|
||||
pub module_exports: Vec<String>,
|
||||
/// Settings specified from annotations.
|
||||
@ -276,7 +281,7 @@ impl ExecState {
|
||||
}
|
||||
|
||||
pub(super) fn circular_import_error(&self, path: &ModulePath, source_range: SourceRange) -> KclError {
|
||||
KclError::ImportCycle(KclErrorDetails::new(
|
||||
KclError::new_import_cycle(KclErrorDetails::new(
|
||||
format!(
|
||||
"circular import of modules is not allowed: {} -> {}",
|
||||
self.global
|
||||
@ -342,6 +347,7 @@ impl ModuleState {
|
||||
id_generator: IdGenerator::new(module_id),
|
||||
stack: memory.new_stack(),
|
||||
pipe_value: Default::default(),
|
||||
being_declared: Default::default(),
|
||||
module_exports: Default::default(),
|
||||
explicit_length_units: false,
|
||||
path,
|
||||
@ -389,7 +395,7 @@ impl MetaSettings {
|
||||
self.kcl_version = value;
|
||||
}
|
||||
name => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Unexpected settings key: `{name}`; expected one of `{}`, `{}`",
|
||||
annotations::SETTINGS_UNIT_LENGTH,
|
||||
|
@ -187,7 +187,7 @@ impl RuntimeType {
|
||||
};
|
||||
RuntimeType::Primitive(PrimitiveType::Number(ty))
|
||||
}
|
||||
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
|
||||
AstPrimitiveType::Named { id } => Self::from_alias(&id.name, exec_state, source_range)?,
|
||||
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
||||
AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||
AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
|
||||
|
@ -28,7 +28,7 @@ impl Default for FileManager {
|
||||
impl FileSystem for FileManager {
|
||||
async fn read(&self, path: &TypedPath, source_range: SourceRange) -> Result<Vec<u8>, KclError> {
|
||||
tokio::fs::read(&path.0).await.map_err(|e| {
|
||||
KclError::Io(KclErrorDetails::new(
|
||||
KclError::new_io(KclErrorDetails::new(
|
||||
format!("Failed to read file `{}`: {}", path.display(), e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -37,7 +37,7 @@ impl FileSystem for FileManager {
|
||||
|
||||
async fn read_to_string(&self, path: &TypedPath, source_range: SourceRange) -> Result<String, KclError> {
|
||||
tokio::fs::read_to_string(&path.0).await.map_err(|e| {
|
||||
KclError::Io(KclErrorDetails::new(
|
||||
KclError::new_io(KclErrorDetails::new(
|
||||
format!("Failed to read file `{}`: {}", path.display(), e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -49,7 +49,7 @@ impl FileSystem for FileManager {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(KclError::Io(KclErrorDetails::new(
|
||||
Err(KclError::new_io(KclErrorDetails::new(
|
||||
format!("Failed to check if file `{}` exists: {}", path.display(), e),
|
||||
vec![source_range],
|
||||
)))
|
||||
@ -71,7 +71,7 @@ impl FileSystem for FileManager {
|
||||
}
|
||||
|
||||
let mut read_dir = tokio::fs::read_dir(&path).await.map_err(|e| {
|
||||
KclError::Io(KclErrorDetails::new(
|
||||
KclError::new_io(KclErrorDetails::new(
|
||||
format!("Failed to read directory `{}`: {}", path.display(), e),
|
||||
vec![source_range],
|
||||
))
|
||||
|
@ -49,10 +49,10 @@ impl FileSystem for FileManager {
|
||||
let promise = self
|
||||
.manager
|
||||
.read_file(path.to_string_lossy())
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
let value = JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from engine: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -67,7 +67,7 @@ impl FileSystem for FileManager {
|
||||
async fn read_to_string(&self, path: &TypedPath, source_range: SourceRange) -> Result<String, KclError> {
|
||||
let bytes = self.read(path, source_range).await?;
|
||||
let string = String::from_utf8(bytes).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to convert bytes to string: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -80,17 +80,17 @@ impl FileSystem for FileManager {
|
||||
let promise = self
|
||||
.manager
|
||||
.exists(path.to_string_lossy())
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
let value = JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from engine: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
let it_exists = value.as_bool().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
"Failed to convert value to bool".to_string(),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -107,24 +107,24 @@ impl FileSystem for FileManager {
|
||||
let promise = self
|
||||
.manager
|
||||
.get_all_files(path.to_string_lossy())
|
||||
.map_err(|e| KclError::Engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
.map_err(|e| KclError::new_engine(KclErrorDetails::new(e.to_string().into(), vec![source_range])))?;
|
||||
|
||||
let value = JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to wait for promise from javascript: {:?}", e),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
let s = value.as_string().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to get string from response from javascript: `{:?}`", value),
|
||||
vec![source_range],
|
||||
))
|
||||
})?;
|
||||
|
||||
let files: Vec<String> = serde_json::from_str(&s).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails::new(
|
||||
KclError::new_engine(KclErrorDetails::new(
|
||||
format!("Failed to parse json from javascript: `{}` `{:?}`", s, e),
|
||||
vec![source_range],
|
||||
))
|
||||
|
@ -1189,7 +1189,6 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
|
||||
// ADAM: This is the entrypoint.
|
||||
let mut completions = vec![CompletionItem {
|
||||
label: PIPE_OPERATOR.to_string(),
|
||||
label_details: None,
|
||||
|
@ -58,7 +58,7 @@ impl ModuleLoader {
|
||||
}
|
||||
|
||||
pub(crate) fn import_cycle_error(&self, path: &ModulePath, source_range: SourceRange) -> KclError {
|
||||
KclError::ImportCycle(KclErrorDetails::new(
|
||||
KclError::new_import_cycle(KclErrorDetails::new(
|
||||
format!(
|
||||
"circular import of modules is not allowed: {} -> {}",
|
||||
self.import_stack
|
||||
@ -163,7 +163,7 @@ impl ModulePath {
|
||||
ModulePath::Std { value: name } => Ok(ModuleSource {
|
||||
source: read_std(name)
|
||||
.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Cannot find standard library module to import: std::{name}."),
|
||||
vec![source_range],
|
||||
))
|
||||
@ -202,7 +202,7 @@ impl ModulePath {
|
||||
ModulePath::Std { .. } => {
|
||||
let message = format!("Cannot import a non-std KCL file from std: {path}.");
|
||||
debug_assert!(false, "{}", &message);
|
||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![])));
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(message, vec![])));
|
||||
}
|
||||
};
|
||||
|
||||
@ -217,7 +217,7 @@ impl ModulePath {
|
||||
if path.len() != 2 || path[0] != "std" {
|
||||
let message = format!("Invalid std import path: {path:?}.");
|
||||
debug_assert!(false, "{}", &message);
|
||||
return Err(KclError::Internal(KclErrorDetails::new(message, vec![])));
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(message, vec![])));
|
||||
}
|
||||
|
||||
Ok(ModulePath::Std { value: path[1].clone() })
|
||||
|
@ -228,7 +228,7 @@ impl PrimitiveType {
|
||||
let mut hasher = Sha256::new();
|
||||
match self {
|
||||
PrimitiveType::Any => hasher.update(b"any"),
|
||||
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
|
||||
PrimitiveType::Named { id } => hasher.update(id.compute_digest()),
|
||||
PrimitiveType::String => hasher.update(b"string"),
|
||||
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
||||
PrimitiveType::Boolean => hasher.update(b"bool"),
|
||||
|
@ -454,7 +454,7 @@ impl Node<Program> {
|
||||
alpha: c.a,
|
||||
},
|
||||
};
|
||||
if colors.borrow().iter().any(|c| *c == color) {
|
||||
if colors.borrow().contains(&color) {
|
||||
return;
|
||||
}
|
||||
colors.borrow_mut().push(color);
|
||||
@ -529,7 +529,7 @@ impl Node<Program> {
|
||||
let new_color = csscolorparser::Color::new(color.red, color.green, color.blue, color.alpha);
|
||||
Ok(Some(ColorPresentation {
|
||||
// The label will be what they replace the color with.
|
||||
label: new_color.to_hex_string(),
|
||||
label: new_color.to_css_hex(),
|
||||
text_edit: None,
|
||||
additional_text_edits: None,
|
||||
}))
|
||||
@ -3230,7 +3230,7 @@ pub enum PrimitiveType {
|
||||
/// `fn`, type of functions.
|
||||
Function(FunctionType),
|
||||
/// An identifier used as a type (not really a primitive type, but whatever).
|
||||
Named(Node<Identifier>),
|
||||
Named { id: Node<Identifier> },
|
||||
}
|
||||
|
||||
impl PrimitiveType {
|
||||
@ -3286,7 +3286,7 @@ impl fmt::Display for PrimitiveType {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
PrimitiveType::Named(n) => write!(f, "{}", n.name),
|
||||
PrimitiveType::Named { id: n } => write!(f, "{}", n.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ pub fn parse_tokens(mut tokens: TokenStream) -> ParseResult {
|
||||
} else {
|
||||
format!("found unknown tokens [{}]", token_list.join(", "))
|
||||
};
|
||||
return KclError::Lexical(KclErrorDetails::new(message, source_ranges)).into();
|
||||
return KclError::new_lexical(KclErrorDetails::new(message, source_ranges)).into();
|
||||
}
|
||||
|
||||
// Important, to not call this before the unknown tokens check.
|
||||
@ -110,7 +110,7 @@ impl ParseResult {
|
||||
let (p, errs) = self.0?;
|
||||
|
||||
if let Some(err) = errs.iter().find(|e| e.severity.is_err()) {
|
||||
return Err(KclError::Syntax(err.clone().into()));
|
||||
return Err(KclError::new_syntax(err.clone().into()));
|
||||
}
|
||||
match p {
|
||||
Some(p) => Ok(p),
|
||||
|
@ -979,12 +979,18 @@ fn property_separator(i: &mut TokenSlice) -> ModalResult<()> {
|
||||
}
|
||||
|
||||
/// Match something that separates the labeled arguments of a fn call.
|
||||
fn labeled_arg_separator(i: &mut TokenSlice) -> ModalResult<()> {
|
||||
/// Returns the source range of the erroneous separator, if any was found.
|
||||
fn labeled_arg_separator(i: &mut TokenSlice) -> ModalResult<Option<SourceRange>> {
|
||||
alt((
|
||||
// Normally you need a comma.
|
||||
comma_sep,
|
||||
comma_sep.map(|_| None),
|
||||
// But, if the argument list is ending, no need for a comma.
|
||||
peek(preceded(opt(whitespace), close_paren)).void(),
|
||||
peek(preceded(opt(whitespace), close_paren)).void().map(|_| None),
|
||||
whitespace.map(|mut tokens| {
|
||||
// Safe to unwrap here because `whitespace` is guaranteed to return at least 1 whitespace.
|
||||
let first_token = tokens.pop().unwrap();
|
||||
Some(SourceRange::from(&first_token))
|
||||
}),
|
||||
))
|
||||
.parse_next(i)
|
||||
}
|
||||
@ -2932,7 +2938,7 @@ fn primitive_type(i: &mut TokenSlice) -> ModalResult<Node<PrimitiveType>> {
|
||||
(identifier, opt(delimited(open_paren, uom_for_type, close_paren))).map(|(ident, suffix)| {
|
||||
let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id);
|
||||
result.inner =
|
||||
PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named(ident));
|
||||
PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named { id: ident });
|
||||
result
|
||||
}),
|
||||
))
|
||||
@ -3135,7 +3141,7 @@ fn binding_name(i: &mut TokenSlice) -> ModalResult<Node<Identifier>> {
|
||||
|
||||
/// Either a positional or keyword function call.
|
||||
fn fn_call_pos_or_kw(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
alt((fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),)).parse_next(i)
|
||||
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw).parse_next(i)
|
||||
}
|
||||
|
||||
fn labelled_fn_call(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
@ -3198,7 +3204,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum ArgPlace {
|
||||
NonCode(Node<NonCodeNode>),
|
||||
LabeledArg(LabeledArg),
|
||||
LabeledArg((LabeledArg, Option<SourceRange>)),
|
||||
UnlabeledArg(Expr),
|
||||
Keyword(Token),
|
||||
}
|
||||
@ -3208,7 +3214,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
|
||||
alt((
|
||||
terminated(non_code_node.map(ArgPlace::NonCode), whitespace),
|
||||
terminated(any_keyword.map(ArgPlace::Keyword), whitespace),
|
||||
terminated(labeled_argument, labeled_arg_separator).map(ArgPlace::LabeledArg),
|
||||
(labeled_argument, labeled_arg_separator).map(ArgPlace::LabeledArg),
|
||||
expression.map(ArgPlace::UnlabeledArg),
|
||||
)),
|
||||
)
|
||||
@ -3220,7 +3226,16 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
|
||||
ArgPlace::NonCode(x) => {
|
||||
non_code_nodes.insert(index, vec![x]);
|
||||
}
|
||||
ArgPlace::LabeledArg(x) => {
|
||||
ArgPlace::LabeledArg((x, bad_token_source_range)) => {
|
||||
if let Some(bad_token_source_range) = bad_token_source_range {
|
||||
return Err(ErrMode::Cut(
|
||||
CompilationError::fatal(
|
||||
bad_token_source_range,
|
||||
"Missing comma between arguments, try adding a comma in",
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
args.push(x);
|
||||
}
|
||||
ArgPlace::Keyword(kw) => {
|
||||
@ -3255,7 +3270,22 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
|
||||
)?;
|
||||
ignore_whitespace(i);
|
||||
opt(comma_sep).parse_next(i)?;
|
||||
let end = close_paren.parse_next(i)?.end;
|
||||
let end = match close_paren.parse_next(i) {
|
||||
Ok(tok) => tok.end,
|
||||
Err(e) => {
|
||||
if let Some(tok) = i.next_token() {
|
||||
return Err(ErrMode::Cut(
|
||||
CompilationError::fatal(
|
||||
SourceRange::from(&tok),
|
||||
format!("There was an unexpected {}. Try removing it.", tok.value),
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Validate there aren't any duplicate labels.
|
||||
let mut counted_labels = IndexMap::with_capacity(args.len());
|
||||
@ -3376,8 +3406,7 @@ mod tests {
|
||||
fn kw_call_as_operand() {
|
||||
let tokens = crate::parsing::token::lex("f(x = 1)", ModuleId::default()).unwrap();
|
||||
let tokens = tokens.as_slice();
|
||||
let op = operand.parse(tokens).unwrap();
|
||||
println!("{op:#?}");
|
||||
operand.parse(tokens).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -4383,7 +4412,7 @@ secondExtrude = startSketchOn(XY)
|
||||
#[test]
|
||||
fn test_parse_parens_unicode() {
|
||||
let result = crate::parsing::top_level_parse("(ޜ");
|
||||
let KclError::Lexical(details) = result.0.unwrap_err() else {
|
||||
let KclError::Lexical { details } = result.0.unwrap_err() else {
|
||||
panic!();
|
||||
};
|
||||
// TODO: Better errors when program cannot tokenize.
|
||||
@ -4417,8 +4446,8 @@ z(-[["#,
|
||||
assert_err(
|
||||
r#"z
|
||||
(--#"#,
|
||||
"Unexpected token: (",
|
||||
[2, 3],
|
||||
"There was an unexpected -. Try removing it.",
|
||||
[3, 4],
|
||||
);
|
||||
}
|
||||
|
||||
@ -5133,6 +5162,27 @@ bar = 1
|
||||
assert_eq!(actual.operator, UnaryOperator::Not);
|
||||
crate::parsing::top_level_parse(some_program_string).unwrap(); // Updated import path
|
||||
}
|
||||
#[test]
|
||||
fn test_sensible_error_when_missing_comma_between_fn_args() {
|
||||
let program_source = "startSketchOn(XY)
|
||||
|> arc(
|
||||
endAbsolute = [0, 50]
|
||||
interiorAbsolute = [-50, 0]
|
||||
)";
|
||||
let expected_src_start = program_source.find("]").unwrap();
|
||||
let tokens = crate::parsing::token::lex(program_source, ModuleId::default()).unwrap();
|
||||
ParseContext::init();
|
||||
let err = program
|
||||
.parse(tokens.as_slice())
|
||||
.expect_err("Program succeeded, but it should have failed");
|
||||
let cause = err
|
||||
.inner()
|
||||
.cause
|
||||
.as_ref()
|
||||
.expect("Found an error, but there was no cause. Add a cause.");
|
||||
assert_eq!(cause.message, "Missing comma between arguments, try adding a comma in",);
|
||||
assert_eq!(cause.source_range.start() - 1, expected_src_start);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sensible_error_when_missing_rhs_of_kw_arg() {
|
||||
@ -5152,14 +5202,32 @@ bar = 1
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sensible_error_when_unexpected_token_in_fn_call() {
|
||||
let program_source = "1
|
||||
|> extrude(
|
||||
length=depth,
|
||||
})";
|
||||
let expected_src_start = program_source.find("}").expect("Program should have an extraneous }");
|
||||
let tokens = crate::parsing::token::lex(program_source, ModuleId::default()).unwrap();
|
||||
ParseContext::init();
|
||||
let err = program.parse(tokens.as_slice()).unwrap_err();
|
||||
let cause = err
|
||||
.inner()
|
||||
.cause
|
||||
.as_ref()
|
||||
.expect("Found an error, but there was no cause. Add a cause.");
|
||||
assert_eq!(cause.message, "There was an unexpected }. Try removing it.",);
|
||||
assert_eq!(cause.source_range.start(), expected_src_start);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sensible_error_when_using_keyword_as_arg_label() {
|
||||
for (i, program) in ["pow(2, fn = 8)"].into_iter().enumerate() {
|
||||
let tokens = crate::parsing::token::lex(program, ModuleId::default()).unwrap();
|
||||
let err = match fn_call_kw.parse(tokens.as_slice()) {
|
||||
Err(e) => e,
|
||||
Ok(ast) => {
|
||||
eprintln!("{ast:#?}");
|
||||
Ok(_ast) => {
|
||||
panic!("Expected this to error but it didn't");
|
||||
}
|
||||
};
|
||||
|
@ -597,7 +597,7 @@ impl From<ParseError<Input<'_>, winnow::error::ContextError>> for KclError {
|
||||
// This is an offset, not an index, and may point to
|
||||
// the end of input (input.len()) on eof errors.
|
||||
|
||||
return KclError::Lexical(crate::errors::KclErrorDetails::new(
|
||||
return KclError::new_lexical(crate::errors::KclErrorDetails::new(
|
||||
"unexpected EOF while parsing".to_owned(),
|
||||
vec![SourceRange::new(offset, offset, module_id)],
|
||||
));
|
||||
@ -608,7 +608,7 @@ impl From<ParseError<Input<'_>, winnow::error::ContextError>> for KclError {
|
||||
let bad_token = &input[offset];
|
||||
// TODO: Add the Winnow parser context to the error.
|
||||
// See https://github.com/KittyCAD/modeling-app/issues/784
|
||||
KclError::Lexical(crate::errors::KclErrorDetails::new(
|
||||
KclError::new_lexical(crate::errors::KclErrorDetails::new(
|
||||
format!("found unknown token '{}'", bad_token),
|
||||
vec![SourceRange::new(offset, offset + 1, module_id)],
|
||||
))
|
||||
|
@ -3483,3 +3483,45 @@ mod spheres {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod var_ref_in_own_def {
|
||||
const TEST_NAME: &str = "var_ref_in_own_def";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod ascription_unknown_type {
|
||||
const TEST_NAME: &str = "ascription_unknown_type";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub async fn hex_string(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
|
||||
// Make sure the color if set is valid.
|
||||
if let Some(component) = rgb.iter().find(|component| component.n < 0.0 || component.n > 255.0) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Colors are given between 0 and 255, so {} is invalid", component.n),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -62,7 +62,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
|
||||
// Make sure the color if set is valid.
|
||||
if !HEX_REGEX.is_match(&color) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Invalid hex color (`{}`), try something like `#fff000`", color),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -93,7 +93,7 @@ async fn inner_appearance(
|
||||
for solid_id in solids.ids(&args.ctx).await? {
|
||||
// Set the material properties.
|
||||
let rgb = rgba_simple::RGB::<f32>::from_hex(&color).map_err(|err| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Invalid hex color (`{color}`): {err}"),
|
||||
vec![args.source_range],
|
||||
))
|
||||
|
@ -123,7 +123,7 @@ impl Args {
|
||||
}
|
||||
|
||||
T::from_kcl_val(&arg.value).map(Some).ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!(
|
||||
"The arg {label} was given, but it was the wrong type. It should be type {} but it was {}",
|
||||
tynm::type_name::<T>(),
|
||||
@ -156,7 +156,7 @@ impl Args {
|
||||
T: FromKclValue<'a>,
|
||||
{
|
||||
self.get_kw_arg_opt(label)?.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("This function requires a keyword argument '{label}'"),
|
||||
vec![self.source_range],
|
||||
))
|
||||
@ -173,7 +173,7 @@ impl Args {
|
||||
T: for<'a> FromKclValue<'a>,
|
||||
{
|
||||
let Some(arg) = self.kw_args.labeled.get(label) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("This function requires a keyword argument '{label}'"),
|
||||
vec![self.source_range],
|
||||
)));
|
||||
@ -207,7 +207,7 @@ impl Args {
|
||||
if message.contains("one or more Solids or imported geometry but it's actually of type Sketch") {
|
||||
message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
|
||||
}
|
||||
KclError::Semantic(KclErrorDetails::new(message, arg.source_ranges()))
|
||||
KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
|
||||
})?;
|
||||
|
||||
// TODO unnecessary cloning
|
||||
@ -221,7 +221,7 @@ impl Args {
|
||||
label: &str,
|
||||
) -> Result<Vec<(EdgeReference, SourceRange)>, KclError> {
|
||||
let Some(arg) = self.kw_args.labeled.get(label) else {
|
||||
let err = KclError::Semantic(KclErrorDetails::new(
|
||||
let err = KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("This function requires a keyword argument '{label}'"),
|
||||
vec![self.source_range],
|
||||
));
|
||||
@ -234,7 +234,7 @@ impl Args {
|
||||
.map(|item| {
|
||||
let source = SourceRange::from(item);
|
||||
let val = FromKclValue::from_kcl_val(item).ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected an Edge but found {}", arg.value.human_friendly_type()),
|
||||
arg.source_ranges(),
|
||||
))
|
||||
@ -270,7 +270,7 @@ impl Args {
|
||||
{
|
||||
let arg = self
|
||||
.unlabeled_kw_arg_unconverted()
|
||||
.ok_or(KclError::Semantic(KclErrorDetails::new(
|
||||
.ok_or(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
||||
vec![self.source_range],
|
||||
)))?;
|
||||
@ -304,11 +304,11 @@ impl Args {
|
||||
if message.contains("one or more Solids or imported geometry but it's actually of type Sketch") {
|
||||
message = format!("{message}. {ERROR_STRING_SKETCH_TO_SOLID_HELPER}");
|
||||
}
|
||||
KclError::Semantic(KclErrorDetails::new(message, arg.source_ranges()))
|
||||
KclError::new_semantic(KclErrorDetails::new(message, arg.source_ranges()))
|
||||
})?;
|
||||
|
||||
T::from_kcl_val(&arg).ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}"),
|
||||
vec![self.source_range],
|
||||
))
|
||||
@ -354,14 +354,14 @@ impl Args {
|
||||
exec_state.stack().get_from_call_stack(&tag.value, self.source_range)?
|
||||
{
|
||||
let info = t.get_info(epoch).ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Tag `{}` does not have engine info", tag.value),
|
||||
vec![self.source_range],
|
||||
))
|
||||
})?;
|
||||
Ok(info)
|
||||
} else {
|
||||
Err(KclError::Type(KclErrorDetails::new(
|
||||
Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!("Tag `{}` does not exist", tag.value),
|
||||
vec![self.source_range],
|
||||
)))
|
||||
@ -493,7 +493,7 @@ impl Args {
|
||||
must_be_planar: bool,
|
||||
) -> Result<uuid::Uuid, KclError> {
|
||||
if tag.value.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Expected a non-empty tag for the face".to_string(),
|
||||
vec![self.source_range],
|
||||
)));
|
||||
@ -502,7 +502,7 @@ impl Args {
|
||||
let engine_info = self.get_tag_engine_info_check_surface(exec_state, tag)?;
|
||||
|
||||
let surface = engine_info.surface.as_ref().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Tag `{}` does not have a surface", tag.value),
|
||||
vec![self.source_range],
|
||||
))
|
||||
@ -521,7 +521,7 @@ impl Args {
|
||||
}
|
||||
}
|
||||
// The must be planar check must be called before the arc check.
|
||||
ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails::new(
|
||||
ExtrudeSurface::ExtrudeArc(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!("Tag `{}` is a non-planar surface", tag.value),
|
||||
vec![self.source_range],
|
||||
)))),
|
||||
@ -548,7 +548,7 @@ impl Args {
|
||||
}
|
||||
}
|
||||
// The must be planar check must be called before the fillet check.
|
||||
ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::Type(KclErrorDetails::new(
|
||||
ExtrudeSurface::Fillet(_) if must_be_planar => Some(Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!("Tag `{}` is a non-planar surface", tag.value),
|
||||
vec![self.source_range],
|
||||
)))),
|
||||
@ -568,7 +568,7 @@ impl Args {
|
||||
}
|
||||
|
||||
// If we still haven't found the face, return an error.
|
||||
Err(KclError::Type(KclErrorDetails::new(
|
||||
Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a face with the tag `{}`", tag.value),
|
||||
vec![self.source_range],
|
||||
)))
|
||||
@ -593,13 +593,13 @@ where
|
||||
{
|
||||
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
|
||||
let Some(arg) = args.args.get(i) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected an argument at index {i}"),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
};
|
||||
let Some(val) = T::from_kcl_val(&arg.value) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Argument at index {i} was supposed to be type {} but found {}",
|
||||
tynm::type_name::<T>(),
|
||||
@ -622,7 +622,7 @@ where
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(val) = T::from_kcl_val(&arg.value) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Argument at index {i} was supposed to be type Option<{}> but found {}",
|
||||
tynm::type_name::<T>(),
|
||||
|
@ -58,7 +58,7 @@ async fn call_map_closure(
|
||||
let output = map_fn.call_kw(None, exec_state, ctxt, args, source_range).await?;
|
||||
let source_ranges = vec![source_range];
|
||||
let output = output.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"Map function must return a value".to_owned(),
|
||||
source_ranges,
|
||||
))
|
||||
@ -118,7 +118,7 @@ async fn call_reduce_closure(
|
||||
// Unpack the returned transform object.
|
||||
let source_ranges = vec![source_range];
|
||||
let out = transform_fn_return.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"Reducer function must return a value".to_string(),
|
||||
source_ranges.clone(),
|
||||
))
|
||||
@ -138,7 +138,7 @@ pub async fn push(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
pub async fn pop(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (mut array, ty) = args.get_unlabeled_kw_arg_array_and_type("array", exec_state)?;
|
||||
if array.is_empty() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Cannot pop from an empty array".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
|
||||
async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError> {
|
||||
if !value {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!("assert failed: {}", message),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -72,14 +72,14 @@ async fn inner_assert(
|
||||
.iter()
|
||||
.all(|cond| cond.is_none());
|
||||
if no_condition_given {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"You must provide at least one condition in this assert (for example, isEqualTo)".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if tolerance.is_some() && is_equal_to.is_none() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"The `tolerance` arg is only used with `isEqualTo`. Either remove `tolerance` or add an `isEqualTo` arg."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
|
@ -41,7 +41,7 @@ async fn inner_chamfer(
|
||||
// If you try and tag multiple edges with a tagged chamfer, we want to return an
|
||||
// error to the user that they can only tag one edge at a time.
|
||||
if tag.is_some() && tags.len() > 1 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -84,7 +84,7 @@ async fn inner_clone(
|
||||
fix_tags_and_references(&mut new_geometry, old_id, exec_state, &args)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("failed to fix tags and references: {:?}", e),
|
||||
vec![args.source_range],
|
||||
))
|
||||
|
@ -23,7 +23,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
||||
|
||||
if solids.len() < 2 {
|
||||
return Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"At least two solids are required for a union operation.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -66,7 +66,7 @@ pub(crate) async fn inner_union(
|
||||
modeling_response: OkModelingCmdResponse::BooleanUnion(BooleanUnion { extra_solid_ids }),
|
||||
} = result
|
||||
else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
"Failed to get the result of the union operation.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -88,7 +88,7 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
|
||||
|
||||
if solids.len() < 2 {
|
||||
return Err(KclError::UndefinedValue(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"At least two solids are required for an intersect operation.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -131,7 +131,7 @@ pub(crate) async fn inner_intersect(
|
||||
modeling_response: OkModelingCmdResponse::BooleanIntersection(BooleanIntersection { extra_solid_ids }),
|
||||
} = result
|
||||
else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
"Failed to get the result of the intersection operation.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -193,7 +193,7 @@ pub(crate) async fn inner_subtract(
|
||||
modeling_response: OkModelingCmdResponse::BooleanSubtract(BooleanSubtract { extra_solid_ids }),
|
||||
} = result
|
||||
else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
"Failed to get the result of the subtract operation.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -52,7 +52,7 @@ async fn inner_get_opposite_edge(
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
|
||||
} = &resp
|
||||
else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("mcmd::Solid3dGetOppositeEdge response was not as expected: {:?}", resp),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -100,7 +100,7 @@ async fn inner_get_next_adjacent_edge(
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(adjacent_edge),
|
||||
} = &resp
|
||||
else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!(
|
||||
"mcmd::Solid3dGetNextAdjacentEdge response was not as expected: {:?}",
|
||||
resp
|
||||
@ -110,7 +110,7 @@ async fn inner_get_next_adjacent_edge(
|
||||
};
|
||||
|
||||
adjacent_edge.edge.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("No edge found next adjacent to tag: `{}`", edge.value),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -155,7 +155,7 @@ async fn inner_get_previous_adjacent_edge(
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetPrevAdjacentEdge(adjacent_edge),
|
||||
} = &resp
|
||||
else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!(
|
||||
"mcmd::Solid3dGetPrevAdjacentEdge response was not as expected: {:?}",
|
||||
resp
|
||||
@ -165,7 +165,7 @@ async fn inner_get_previous_adjacent_edge(
|
||||
};
|
||||
|
||||
adjacent_edge.edge.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("No edge found previous adjacent to tag: `{}`", edge.value),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -198,7 +198,7 @@ async fn inner_get_common_edge(
|
||||
}
|
||||
|
||||
if faces.len() != 2 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"getCommonEdge requires exactly two tags for faces".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -210,7 +210,7 @@ async fn inner_get_common_edge(
|
||||
let second_tagged_path = args.get_tag_engine_info(exec_state, &faces[1])?;
|
||||
|
||||
if first_tagged_path.sketch != second_tagged_path.sketch {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"getCommonEdge requires the faces to be in the same original sketch".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -239,14 +239,14 @@ async fn inner_get_common_edge(
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetCommonEdge(common_edge),
|
||||
} = &resp
|
||||
else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("mcmd::Solid3dGetCommonEdge response was not as expected: {:?}", resp),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
};
|
||||
|
||||
common_edge.edge.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!(
|
||||
"No common edge was found between `{}` and `{}`",
|
||||
faces[0].value, faces[1].value
|
||||
|
@ -66,7 +66,7 @@ async fn inner_extrude(
|
||||
let mut solids = Vec::new();
|
||||
|
||||
if symmetric.unwrap_or(false) && bidirectional_length.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You cannot give both `symmetric` and `bidirectional` params, you have to choose one or the other"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -153,7 +153,7 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
// The "get extrusion face info" API call requires *any* edge on the sketch being extruded.
|
||||
// So, let's just use the first one.
|
||||
let Some(any_edge_id) = sketch.paths.first().map(|edge| edge.get_base().geo_meta.id) else {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Expected a non-empty sketch".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -278,7 +278,7 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
// Add the tags for the start or end caps.
|
||||
if let Some(tag_start) = named_cap_tags.start {
|
||||
let Some(start_cap_id) = start_cap_id else {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected a start cap ID for tag `{}` for extrusion of sketch {:?}",
|
||||
tag_start.name, sketch.id
|
||||
@ -298,7 +298,7 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
}
|
||||
if let Some(tag_end) = named_cap_tags.end {
|
||||
let Some(end_cap_id) = end_cap_id else {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected an end cap ID for tag `{}` for extrusion of sketch {:?}",
|
||||
tag_end.name, sketch.id
|
||||
|
@ -49,7 +49,7 @@ pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]
|
||||
}
|
||||
}
|
||||
if !duplicate_tags_source.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"The same edge ID is being referenced multiple times, which is not allowed. Please select a different edge"
|
||||
.to_string(),
|
||||
duplicate_tags_source,
|
||||
@ -85,14 +85,14 @@ async fn inner_fillet(
|
||||
// If you try and tag multiple edges with a tagged fillet, we want to return an
|
||||
// error to the user that they can only tag one edge at a time.
|
||||
if tag.is_some() && tags.len() > 1 {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
return Err(KclError::new_type(KclErrorDetails {
|
||||
message: "You can only tag one edge at a time with a tagged fillet. Either delete the tag for the fillet fn if you don't need it OR separate into individual fillet functions for each tag.".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
backtrace: Default::default(),
|
||||
}));
|
||||
}
|
||||
if tags.is_empty() {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
return Err(KclError::new_semantic(KclErrorDetails {
|
||||
source_ranges: vec![args.source_range],
|
||||
message: "You must fillet at least one tag".to_owned(),
|
||||
backtrace: Default::default(),
|
||||
|
@ -33,7 +33,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we have a radius if we don't have a cylinder.
|
||||
if radius.is_none() && cylinder.is_none() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Radius is required when creating a helix without a cylinder.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -41,7 +41,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we don't have a radius if we have a cylinder.
|
||||
if radius.is_some() && cylinder.is_some() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Radius is not allowed when creating a helix with a cylinder.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -49,7 +49,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we have an axis if we don't have a cylinder.
|
||||
if axis.is_none() && cylinder.is_none() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Axis is required when creating a helix without a cylinder.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -57,7 +57,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we don't have an axis if we have a cylinder.
|
||||
if axis.is_some() && cylinder.is_some() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Axis is not allowed when creating a helix with a cylinder.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -65,7 +65,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we have a radius if we have an axis.
|
||||
if radius.is_none() && axis.is_some() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Radius is required when creating a helix around an axis.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -73,7 +73,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Make sure we have an axis if we have a radius.
|
||||
if axis.is_none() && radius.is_some() {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(crate::errors::KclErrorDetails::new(
|
||||
"Axis is required when creating a helix around an axis.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -140,7 +140,7 @@ async fn inner_helix(
|
||||
Axis3dOrEdgeReference::Axis { direction, origin } => {
|
||||
// Make sure they gave us a length.
|
||||
let Some(length) = length else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Length is required when creating a helix around an axis.".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -68,7 +68,7 @@ async fn inner_loft(
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
// Make sure we have at least two sketches.
|
||||
if sketches.len() < 2 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Loft requires at least two sketches, but only {} were provided.",
|
||||
sketches.len()
|
||||
|
@ -56,7 +56,7 @@ pub async fn sqrt(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
||||
let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
|
||||
|
||||
if input.n < 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Attempt to take square root (`sqrt`) of a number less than zero ({})",
|
||||
input.n
|
||||
|
@ -101,7 +101,7 @@ async fn inner_mirror_2d(
|
||||
OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids { entity_ids: child_ids }),
|
||||
} = response
|
||||
else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
"Expected a successful response from EntityGetAllChildUuids".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -112,7 +112,7 @@ async fn inner_mirror_2d(
|
||||
let child_id = child_ids[1];
|
||||
sketch.mirror = Some(child_id);
|
||||
} else {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Expected child uuids to be >= 2".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -45,17 +45,6 @@ pub type StdFn = fn(
|
||||
|
||||
lazy_static! {
|
||||
static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
|
||||
Box::new(crate::std::segment::SegEnd),
|
||||
Box::new(crate::std::segment::SegEndX),
|
||||
Box::new(crate::std::segment::SegEndY),
|
||||
Box::new(crate::std::segment::SegStart),
|
||||
Box::new(crate::std::segment::SegStartX),
|
||||
Box::new(crate::std::segment::SegStartY),
|
||||
Box::new(crate::std::segment::LastSegX),
|
||||
Box::new(crate::std::segment::LastSegY),
|
||||
Box::new(crate::std::segment::SegLen),
|
||||
Box::new(crate::std::segment::SegAng),
|
||||
Box::new(crate::std::segment::TangentToEnd),
|
||||
Box::new(crate::std::sketch::InvoluteCircular),
|
||||
Box::new(crate::std::sketch::Line),
|
||||
Box::new(crate::std::sketch::XLine),
|
||||
@ -210,15 +199,15 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
||||
),
|
||||
("transform", "translate") => (
|
||||
|e, a| Box::pin(crate::std::transform::translate(e, a)),
|
||||
StdFnProps::default("std::transform::translate"),
|
||||
StdFnProps::default("std::transform::translate").include_in_feature_tree(),
|
||||
),
|
||||
("transform", "rotate") => (
|
||||
|e, a| Box::pin(crate::std::transform::rotate(e, a)),
|
||||
StdFnProps::default("std::transform::rotate"),
|
||||
StdFnProps::default("std::transform::rotate").include_in_feature_tree(),
|
||||
),
|
||||
("transform", "scale") => (
|
||||
|e, a| Box::pin(crate::std::transform::scale(e, a)),
|
||||
StdFnProps::default("std::transform::scale"),
|
||||
StdFnProps::default("std::transform::scale").include_in_feature_tree(),
|
||||
),
|
||||
("prelude", "offsetPlane") => (
|
||||
|e, a| Box::pin(crate::std::planes::offset_plane(e, a)),
|
||||
@ -352,6 +341,50 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
||||
|e, a| Box::pin(crate::std::patterns::pattern_circular_2d(e, a)),
|
||||
StdFnProps::default("std::sketch::patternCircular2d"),
|
||||
),
|
||||
("sketch", "segEnd") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_end(e, a)),
|
||||
StdFnProps::default("std::sketch::segEnd"),
|
||||
),
|
||||
("sketch", "segEndX") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_end_x(e, a)),
|
||||
StdFnProps::default("std::sketch::segEndX"),
|
||||
),
|
||||
("sketch", "segEndY") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_end_y(e, a)),
|
||||
StdFnProps::default("std::sketch::segEndY"),
|
||||
),
|
||||
("sketch", "segStart") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_start(e, a)),
|
||||
StdFnProps::default("std::sketch::segStart"),
|
||||
),
|
||||
("sketch", "segStartX") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_start_x(e, a)),
|
||||
StdFnProps::default("std::sketch::segStartX"),
|
||||
),
|
||||
("sketch", "segStartY") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_start_y(e, a)),
|
||||
StdFnProps::default("std::sketch::segStartY"),
|
||||
),
|
||||
("sketch", "lastSegX") => (
|
||||
|e, a| Box::pin(crate::std::segment::last_segment_x(e, a)),
|
||||
StdFnProps::default("std::sketch::lastSegX"),
|
||||
),
|
||||
("sketch", "lastSegY") => (
|
||||
|e, a| Box::pin(crate::std::segment::last_segment_y(e, a)),
|
||||
StdFnProps::default("std::sketch::lastSegY"),
|
||||
),
|
||||
("sketch", "segLen") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_length(e, a)),
|
||||
StdFnProps::default("std::sketch::segLen"),
|
||||
),
|
||||
("sketch", "segAng") => (
|
||||
|e, a| Box::pin(crate::std::segment::segment_angle(e, a)),
|
||||
StdFnProps::default("std::sketch::segAng"),
|
||||
),
|
||||
("sketch", "tangentToEnd") => (
|
||||
|e, a| Box::pin(crate::std::segment::tangent_to_end(e, a)),
|
||||
StdFnProps::default("std::sketch::tangentToEnd"),
|
||||
),
|
||||
("appearance", "hexString") => (
|
||||
|e, a| Box::pin(crate::std::appearance::hex_string(e, a)),
|
||||
StdFnProps::default("std::appearance::hexString"),
|
||||
|
@ -66,7 +66,7 @@ async fn inner_pattern_transform<'a>(
|
||||
// Build the vec of transforms, one for each repetition.
|
||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||
if instances < 1 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
MUST_HAVE_ONE_INSTANCE.to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -96,7 +96,7 @@ async fn inner_pattern_transform_2d<'a>(
|
||||
// Build the vec of transforms, one for each repetition.
|
||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||
if instances < 1 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
MUST_HAVE_ONE_INSTANCE.to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -176,7 +176,7 @@ async fn send_pattern_transform<T: GeometryTrait>(
|
||||
}
|
||||
&mock_ids
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("EntityLinearPattern response was not as expected: {:?}", resp),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -222,7 +222,7 @@ async fn make_transform<T: GeometryTrait>(
|
||||
// Unpack the returned transform object.
|
||||
let source_ranges = vec![source_range];
|
||||
let transform_fn_return = transform_fn_return.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"Transform function must return a value".to_string(),
|
||||
source_ranges.clone(),
|
||||
))
|
||||
@ -233,7 +233,7 @@ async fn make_transform<T: GeometryTrait>(
|
||||
let transforms: Vec<_> = value
|
||||
.into_iter()
|
||||
.map(|val| {
|
||||
val.into_object().ok_or(KclError::Semantic(KclErrorDetails::new(
|
||||
val.into_object().ok_or(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Transform function must return a transform object".to_string(),
|
||||
source_ranges.clone(),
|
||||
)))
|
||||
@ -242,7 +242,7 @@ async fn make_transform<T: GeometryTrait>(
|
||||
transforms
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Transform function must return a transform object".to_string(),
|
||||
source_ranges.clone(),
|
||||
)))
|
||||
@ -265,7 +265,7 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
Some(KclValue::Bool { value: true, .. }) => true,
|
||||
Some(KclValue::Bool { value: false, .. }) => false,
|
||||
Some(_) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"The 'replicate' key must be a bool".to_string(),
|
||||
source_ranges.clone(),
|
||||
)));
|
||||
@ -297,7 +297,7 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
let mut rotation = Rotation::default();
|
||||
if let Some(rot) = transform.get("rotation") {
|
||||
let KclValue::Object { value: rot, meta: _ } = rot else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"The 'rotation' key must be an object (with optional fields 'angle', 'axis' and 'origin')".to_owned(),
|
||||
source_ranges.clone(),
|
||||
)));
|
||||
@ -311,7 +311,7 @@ fn transform_from_obj_fields<T: GeometryTrait>(
|
||||
rotation.angle = Angle::from_degrees(*number);
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"The 'rotation.angle' key must be a number (of degrees)".to_owned(),
|
||||
source_ranges.clone(),
|
||||
)));
|
||||
@ -345,7 +345,7 @@ fn array_to_point3d(
|
||||
) -> Result<[TyF64; 3], KclError> {
|
||||
val.coerce(&RuntimeType::point3d(), true, exec_state)
|
||||
.map_err(|e| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected an array of 3 numbers (i.e., a 3D point), found {}",
|
||||
e.found
|
||||
@ -365,7 +365,7 @@ fn array_to_point2d(
|
||||
) -> Result<[TyF64; 2], KclError> {
|
||||
val.coerce(&RuntimeType::point2d(), true, exec_state)
|
||||
.map_err(|e| {
|
||||
KclError::Semantic(KclErrorDetails::new(
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected an array of 2 numbers (i.e., a 2D point), found {}",
|
||||
e.found
|
||||
@ -534,7 +534,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
|
||||
let axis = axis.to_point2d();
|
||||
if axis[0].n == 0.0 && axis[1].n == 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -594,7 +594,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
|
||||
let axis = axis.to_point3d();
|
||||
if axis[0].n == 0.0 && axis[1].n == 0.0 && axis[2].n == 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -803,7 +803,7 @@ async fn inner_pattern_circular_2d(
|
||||
.await?;
|
||||
|
||||
let Geometries::Sketches(new_sketches) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected a vec of sketches".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -901,7 +901,7 @@ async fn inner_pattern_circular_3d(
|
||||
.await?;
|
||||
|
||||
let Geometries::Solids(new_solids) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected a vec of solids".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -926,7 +926,7 @@ async fn pattern_circular(
|
||||
return Ok(Geometries::from(geometry));
|
||||
}
|
||||
RepetitionsNeeded::Invalid => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
MUST_HAVE_ONE_INSTANCE.to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -971,7 +971,7 @@ async fn pattern_circular(
|
||||
}
|
||||
&mock_ids
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails::new(
|
||||
return Err(KclError::new_engine(KclErrorDetails::new(
|
||||
format!("EntityCircularPattern response was not as expected: {:?}", resp),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -75,7 +75,7 @@ async fn inner_revolve(
|
||||
// We don't use validate() here because we want to return a specific error message that is
|
||||
// nice and we use the other data in the docs, so we still need use the derive above for the json schema.
|
||||
if !(-360.0..=360.0).contains(&angle) || angle == 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected angle to be between -360 and 360 and not 0, found `{}`", angle),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -87,7 +87,7 @@ async fn inner_revolve(
|
||||
// We don't use validate() here because we want to return a specific error message that is
|
||||
// nice and we use the other data in the docs, so we still need use the derive above for the json schema.
|
||||
if !(-360.0..=360.0).contains(&bidirectional_angle) || bidirectional_angle == 0.0 {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Expected bidirectional angle to be between -360 and 360 and not 0, found `{}`",
|
||||
bidirectional_angle
|
||||
@ -99,7 +99,7 @@ async fn inner_revolve(
|
||||
if let Some(angle) = angle {
|
||||
let ang = angle.signum() * bidirectional_angle + angle;
|
||||
if !(-360.0..=360.0).contains(&ang) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!(
|
||||
"Combined angle and bidirectional must be between -360 and 360, found '{}'",
|
||||
ang
|
||||
@ -111,7 +111,7 @@ async fn inner_revolve(
|
||||
}
|
||||
|
||||
if symmetric.unwrap_or(false) && bidirectional_angle.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You cannot give both `symmetric` and `bidirectional` params, you have to choose one or the other"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Functions related to line segments.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use kittycad_modeling_cmds::shared::Angle;
|
||||
|
||||
use super::utils::untype_point;
|
||||
@ -22,43 +21,10 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment.
|
||||
///
|
||||
/// ```no_run
|
||||
/// w = 15
|
||||
/// cube = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [w, 0], tag = $line1)
|
||||
/// |> line(end = [0, w], tag = $line2)
|
||||
/// |> line(end = [-w, 0], tag = $line3)
|
||||
/// |> line(end = [0, -w], tag = $line4)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> circle(radius = radius, center = segEnd(tag) )
|
||||
/// |> extrude(length = radius)
|
||||
/// }
|
||||
///
|
||||
/// cylinder(radius = 1, tag = line1)
|
||||
/// cylinder(radius = 2, tag = line2)
|
||||
/// cylinder(radius = 3, tag = line3)
|
||||
/// cylinder(radius = 4, tag = line4)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segEnd",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -78,31 +44,10 @@ pub async fn segment_end_x(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'x' axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0], tag = $thing)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [segEndX(thing), 0])
|
||||
/// |> line(end = [-20, 10])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segEndX",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -119,32 +64,10 @@ pub async fn segment_end_y(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'y' axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, 3], tag = $thing)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> line(end = [0, segEndY(thing)])
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segEndY",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -161,43 +84,10 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
args.make_kcl_val_from_point([pt[0].n, pt[1].n], pt[0].ty.clone())
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment.
|
||||
///
|
||||
/// ```no_run
|
||||
/// w = 15
|
||||
/// cube = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [w, 0], tag = $line1)
|
||||
/// |> line(end = [0, w], tag = $line2)
|
||||
/// |> line(end = [-w, 0], tag = $line3)
|
||||
/// |> line(end = [0, -w], tag = $line4)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> circle( radius = radius, center = segStart(tag) )
|
||||
/// |> extrude(length = radius)
|
||||
/// }
|
||||
///
|
||||
/// cylinder(radius = 1, tag = line1)
|
||||
/// cylinder(radius = 2, tag = line2)
|
||||
/// cylinder(radius = 3, tag = line3)
|
||||
/// cylinder(radius = 4, tag = line4)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segStart",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<[TyF64; 2], KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -217,31 +107,10 @@ pub async fn segment_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'x' axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0], tag = $thing)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [20 - segStartX(thing), 0])
|
||||
/// |> line(end = [-20, 10])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segStartX",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_start_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -258,32 +127,10 @@ pub async fn segment_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'y' axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, 3], tag = $thing)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> line(end = [0, 20-segStartY(thing)])
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segStartY",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -300,34 +147,12 @@ pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Extract the 'x' axis value of the last line segment in the provided 2-d
|
||||
/// sketch.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [20, 5])
|
||||
/// |> line(end = [lastSegX(%), 0])
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "lastSegX",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
|
||||
let last_line = sketch
|
||||
.paths
|
||||
.last()
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -346,34 +171,12 @@ pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Extract the 'y' axis value of the last line segment in the provided 2-d
|
||||
/// sketch.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [20, 5])
|
||||
/// |> line(end = [0, lastSegY(%)])
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "lastSegY",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch = { docs = "The sketch whose line segment is being queried"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<TyF64, KclError> {
|
||||
let last_line = sketch
|
||||
.paths
|
||||
.last()
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a Sketch with at least one segment, found `{:?}`", sketch),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -390,37 +193,10 @@ pub async fn segment_length(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
Ok(args.make_user_val_from_f64_with_type(result))
|
||||
}
|
||||
|
||||
/// Compute the length of the provided line segment.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> angledLine(
|
||||
/// angle = 60,
|
||||
/// length = 10,
|
||||
/// tag = $thing,
|
||||
/// )
|
||||
/// |> tangentialArc(angle = -120, radius = 5)
|
||||
/// |> angledLine(
|
||||
/// angle = -60,
|
||||
/// length = segLen(thing),
|
||||
/// )
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segLen",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<TyF64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -437,33 +213,10 @@ pub async fn segment_angle(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Compute the angle (in degrees) of the provided line segment.
|
||||
///
|
||||
/// ```no_run
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [10, 0])
|
||||
/// |> line(end = [5, 10], tag = $seg01)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> angledLine(angle = segAng(seg01), length = 10)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> angledLine(angle = segAng(seg01), length = -15)
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 4)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "segAng",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -482,89 +235,10 @@ pub async fn tangent_to_end(exec_state: &mut ExecState, args: Args) -> Result<Kc
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::degrees())))
|
||||
}
|
||||
|
||||
/// Returns the angle coming out of the end of the segment in degrees.
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Horizontal pill.
|
||||
/// pillSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> tangentialArc(end = [0, 10], tag = $arc1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(arc1),
|
||||
/// length = 20,
|
||||
/// )
|
||||
/// |> tangentialArc(end = [0, -10])
|
||||
/// |> close()
|
||||
///
|
||||
/// pillExtrude = extrude(pillSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Vertical pill. Use absolute coordinate for arc.
|
||||
/// pillSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> tangentialArc(endAbsolute = [10, 20], tag = $arc1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(arc1),
|
||||
/// length = 20,
|
||||
/// )
|
||||
/// |> tangentialArc(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// pillExtrude = extrude(pillSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// rectangleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [10, 0], tag = $seg1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(seg1),
|
||||
/// length = 10,
|
||||
/// )
|
||||
/// |> line(end = [0, 10])
|
||||
/// |> line(end = [-20, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// rectangleExtrude = extrude(rectangleSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// bottom = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> arc(
|
||||
/// endAbsolute = [10, 10],
|
||||
/// interiorAbsolute = [5, 1],
|
||||
/// tag = $arc1,
|
||||
/// )
|
||||
/// |> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
/// |> close()
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// circSketch = startSketchOn(XY)
|
||||
/// |> circle( center= [0, 0], radius= 3 , tag= $circ)
|
||||
///
|
||||
/// triangleSketch = startSketchOn(XY)
|
||||
/// |> startProfile(at = [-5, 0])
|
||||
/// |> angledLine(angle = tangentToEnd(circ), length = 10)
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "tangentToEnd",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
tag = { docs = "The line segment being queried by its tag"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<f64, KclError> {
|
||||
let line = args.get_tag_engine_info(exec_state, tag)?;
|
||||
let path = line.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected a line segment with a path, found `{:?}`", line),
|
||||
vec![args.source_range],
|
||||
))
|
||||
|
@ -271,14 +271,14 @@ async fn inner_polygon(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
if num_sides < 3 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Polygon must have at least 3 sides".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if radius.n <= 0.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Radius must be greater than 0".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -407,11 +407,11 @@ pub(crate) fn get_radius(
|
||||
match (radius, diameter) {
|
||||
(Some(radius), None) => Ok(radius),
|
||||
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
|
||||
(None, None) => Err(KclError::Type(KclErrorDetails::new(
|
||||
(None, None) => Err(KclError::new_type(KclErrorDetails::new(
|
||||
"This function needs either `diameter` or `radius`".to_string(),
|
||||
vec![source_range],
|
||||
))),
|
||||
(Some(_), Some(_)) => Err(KclError::Type(KclErrorDetails::new(
|
||||
(Some(_), Some(_)) => Err(KclError::new_type(KclErrorDetails::new(
|
||||
"You cannot specify both `diameter` and `radius`, please remove one".to_string(),
|
||||
vec![source_range],
|
||||
))),
|
||||
|
@ -36,14 +36,14 @@ async fn inner_shell(
|
||||
args: Args,
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
if faces.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"You must shell at least one face".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if solids.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"You must shell at least one solid".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -63,7 +63,7 @@ async fn inner_shell(
|
||||
}
|
||||
|
||||
if face_ids.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Expected at least one valid face".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -72,7 +72,7 @@ async fn inner_shell(
|
||||
// Make sure all the solids have the same id, as we are going to shell them all at
|
||||
// once.
|
||||
if !solids.iter().all(|eg| eg.id == solids[0].id) {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"All solids stem from the same root object, like multiple sketch on face extrusions, etc.".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -12,6 +12,7 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::shapes::get_radius;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
||||
use crate::{
|
||||
@ -32,8 +33,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::shapes::get_radius;
|
||||
|
||||
/// A tag for a face.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -66,13 +65,13 @@ impl FaceTag {
|
||||
match self {
|
||||
FaceTag::Tag(ref t) => args.get_adjacent_face_to_tag(exec_state, t, must_be_planar).await,
|
||||
FaceTag::StartOrEnd(StartOrEnd::Start) => solid.start_cap_id.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
"Expected a start face".to_string(),
|
||||
vec![args.source_range],
|
||||
))
|
||||
}),
|
||||
FaceTag::StartOrEnd(StartOrEnd::End) => solid.end_cap_id.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
"Expected an end face".to_string(),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -329,7 +328,7 @@ async fn straight_line(
|
||||
let from = sketch.current_pen_position()?;
|
||||
let (point, is_absolute) = match (end_absolute, end) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You cannot give both `end` and `endAbsolute` params, you have to choose one or the other".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -337,7 +336,7 @@ async fn straight_line(
|
||||
(Some(end_absolute), None) => (end_absolute, true),
|
||||
(None, Some(end)) => (end, false),
|
||||
(None, None) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("You must supply either `{relative_name}` or `endAbsolute` arguments"),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -604,7 +603,7 @@ async fn inner_angled_line(
|
||||
.filter(|x| x.is_some())
|
||||
.count();
|
||||
if options_given > 1 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
" one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -632,11 +631,11 @@ async fn inner_angled_line(
|
||||
(None, None, None, None, Some(end_absolute_y)) => {
|
||||
inner_angled_line_to_y(angle_degrees, end_absolute_y, sketch, tag, exec_state, args).await
|
||||
}
|
||||
(None, None, None, None, None) => Err(KclError::Type(KclErrorDetails::new(
|
||||
(None, None, None, None, None) => Err(KclError::new_type(KclErrorDetails::new(
|
||||
"One of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` must be given".to_string(),
|
||||
vec![args.source_range],
|
||||
))),
|
||||
_ => Err(KclError::Type(KclErrorDetails::new(
|
||||
_ => Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Only One of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given".to_owned(),
|
||||
vec![args.source_range],
|
||||
))),
|
||||
@ -710,14 +709,14 @@ async fn inner_angled_line_of_x_length(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
if angle_degrees.abs() == 270.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have an x constrained angle of 270 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if angle_degrees.abs() == 90.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have an x constrained angle of 90 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -742,14 +741,14 @@ async fn inner_angled_line_to_x(
|
||||
let from = sketch.current_pen_position()?;
|
||||
|
||||
if angle_degrees.abs() == 270.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have an x constrained angle of 270 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if angle_degrees.abs() == 90.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have an x constrained angle of 90 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -777,14 +776,14 @@ async fn inner_angled_line_of_y_length(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
if angle_degrees.abs() == 0.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have a y constrained angle of 0 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if angle_degrees.abs() == 180.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have a y constrained angle of 180 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -809,14 +808,14 @@ async fn inner_angled_line_to_y(
|
||||
let from = sketch.current_pen_position()?;
|
||||
|
||||
if angle_degrees.abs() == 0.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have a y constrained angle of 0 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
|
||||
if angle_degrees.abs() == 180.0 {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Cannot have a y constrained angle of 180 degrees".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -893,7 +892,7 @@ pub async fn inner_angled_line_that_intersects(
|
||||
) -> Result<Sketch, KclError> {
|
||||
let intersect_path = args.get_tag_engine_info(exec_state, &intersect_tag)?;
|
||||
let path = intersect_path.path.clone().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails::new(
|
||||
KclError::new_type(KclErrorDetails::new(
|
||||
format!("Expected an intersect path with a path, found `{:?}`", intersect_path),
|
||||
vec![args.source_range],
|
||||
))
|
||||
@ -1170,7 +1169,7 @@ async fn inner_start_sketch_on(
|
||||
SketchData::Plane(plane) => {
|
||||
if plane.value == crate::exec::PlaneType::Uninit {
|
||||
if plane.info.origin.units == UnitLen::Unknown {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Origin of plane has unknown units".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -1194,7 +1193,7 @@ async fn inner_start_sketch_on(
|
||||
}
|
||||
SketchData::Solid(solid) => {
|
||||
let Some(tag) = face else {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Expected a tag for the face to sketch on".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -1544,6 +1543,11 @@ pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// Construct a line segment from the current origin back to the profile's
|
||||
/// origin, ensuring the resulting 2-dimensional sketch is not open-ended.
|
||||
///
|
||||
/// If you want to perform some 3-dimensional operation on a sketch, like
|
||||
/// extrude or sweep, you must `close` it first. `close` must be called even
|
||||
/// if the end point of the last segment is coincident with the sketch
|
||||
/// starting point.
|
||||
///
|
||||
/// ```no_run
|
||||
/// startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
@ -1713,7 +1717,7 @@ pub(crate) async fn inner_arc(
|
||||
absolute_arc(&args, id, exec_state, sketch, from, interior_absolute, end_absolute, tag).await
|
||||
}
|
||||
_ => {
|
||||
Err(KclError::Type(KclErrorDetails::new(
|
||||
Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide (angleStart, angleEnd, radius) or (endAbsolute, interiorAbsolute)".to_owned(),
|
||||
vec![args.source_range],
|
||||
)))
|
||||
@ -1800,7 +1804,7 @@ pub async fn relative_arc(
|
||||
let radius = radius.to_length_units(from.units);
|
||||
let (center, end) = arc_center_and_end(from.ignore_units(), a_start, a_end, radius);
|
||||
if a_start == a_end {
|
||||
return Err(KclError::Type(KclErrorDetails::new(
|
||||
return Err(KclError::new_type(KclErrorDetails::new(
|
||||
"Arc start and end angles must be different".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -1968,11 +1972,11 @@ async fn inner_tangential_arc(
|
||||
let data = TangentialArcData::RadiusAndOffset { radius, offset: angle };
|
||||
inner_tangential_arc_radius_angle(data, sketch, tag, exec_state, args).await
|
||||
}
|
||||
(Some(_), Some(_), None, None, None) => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
(Some(_), Some(_), None, None, None) => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You cannot give both `end` and `endAbsolute` params, you have to choose one or the other".to_owned(),
|
||||
vec![args.source_range],
|
||||
))),
|
||||
(_, _, _, _, _) => Err(KclError::Semantic(KclErrorDetails::new(
|
||||
(_, _, _, _, _) => Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You must supply `end`, `endAbsolute`, or both `angle` and `radius`/`diameter` arguments".to_owned(),
|
||||
vec![args.source_range],
|
||||
))),
|
||||
@ -2126,13 +2130,13 @@ async fn inner_tangential_arc_to_point(
|
||||
});
|
||||
|
||||
if result.center[0].is_infinite() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"could not sketch tangential arc, because its center would be infinitely far away in the X direction"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
} else if result.center[1].is_infinite() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"could not sketch tangential arc, because its center would be infinitely far away in the Y direction"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -2310,7 +2314,7 @@ async fn inner_bezier_curve(
|
||||
to
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You must either give `control1`, `control2` and `end`, or `control1Absolute`, `control2Absolute` and `endAbsolute`.".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum SweepPath {
|
||||
Sketch(Sketch),
|
||||
Helix(Box<Helix>),
|
||||
@ -75,7 +76,7 @@ async fn inner_sweep(
|
||||
Some("sketchPlane") => RelativeTo::SketchPlane,
|
||||
Some("trajectoryCurve") | None => RelativeTo::TrajectoryCurve,
|
||||
Some(_) => {
|
||||
return Err(KclError::Syntax(crate::errors::KclErrorDetails::new(
|
||||
return Err(KclError::new_syntax(crate::errors::KclErrorDetails::new(
|
||||
"If you provide relativeTo, it must either be 'sketchPlane' or 'trajectoryCurve'".to_owned(),
|
||||
vec![args.source_range],
|
||||
)))
|
||||
|
@ -37,7 +37,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
|
||||
// Ensure at least one scale value is provided.
|
||||
if scale_x.is_none() && scale_y.is_none() && scale_z.is_none() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `x`, `y`, or `z` to be provided.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -119,7 +119,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
|
||||
// Ensure at least one translation value is provided.
|
||||
if translate_x.is_none() && translate_y.is_none() && translate_z.is_none() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `x`, `y`, or `z` to be provided.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -202,7 +202,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
|
||||
// Check if no rotation values are provided.
|
||||
if roll.is_none() && pitch.is_none() && yaw.is_none() && axis.is_none() && angle.is_none() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `roll`, `pitch`, and `yaw` or `axis` and `angle` to be provided.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -212,7 +212,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
if roll.is_some() || pitch.is_some() || yaw.is_some() {
|
||||
// Ensure they didn't also provide an axis or angle.
|
||||
if axis.is_some() || angle.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `axis` and `angle` to not be provided when `roll`, `pitch`, and `yaw` are provided."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -223,13 +223,13 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
// If they give us an axis or angle, they must give us both.
|
||||
if axis.is_some() || angle.is_some() {
|
||||
if axis.is_none() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `axis` to be provided when `angle` is provided.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
if angle.is_none() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `angle` to be provided when `axis` is provided.".to_string(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -237,7 +237,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
|
||||
// Ensure they didn't also provide a roll, pitch, or yaw.
|
||||
if roll.is_some() || pitch.is_some() || yaw.is_some() {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"Expected `roll`, `pitch`, and `yaw` to not be provided when `axis` and `angle` are provided."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
@ -248,7 +248,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
// Validate the roll, pitch, and yaw values.
|
||||
if let Some(roll) = &roll {
|
||||
if !(-360.0..=360.0).contains(&roll.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected roll to be between -360 and 360, found `{}`", roll.n),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -256,7 +256,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}
|
||||
if let Some(pitch) = &pitch {
|
||||
if !(-360.0..=360.0).contains(&pitch.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected pitch to be between -360 and 360, found `{}`", pitch.n),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -264,7 +264,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
}
|
||||
if let Some(yaw) = &yaw {
|
||||
if !(-360.0..=360.0).contains(&yaw.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected yaw to be between -360 and 360, found `{}`", yaw.n),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
@ -274,7 +274,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
// Validate the axis and angle values.
|
||||
if let Some(angle) = &angle {
|
||||
if !(-360.0..=360.0).contains(&angle.n) {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
format!("Expected angle to be between -360 and 360, found `{}`", angle.n),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
|
@ -93,7 +93,7 @@ async fn do_execute_and_snapshot(
|
||||
for e in exec_state.errors() {
|
||||
if e.severity.is_err() {
|
||||
return Err(ExecErrorWithState::new(
|
||||
KclErrorWithOutputs::no_outputs(KclError::Semantic(e.clone().into())).into(),
|
||||
KclErrorWithOutputs::no_outputs(KclError::new_semantic(e.clone().into())).into(),
|
||||
exec_state.clone(),
|
||||
));
|
||||
}
|
||||
@ -164,7 +164,7 @@ pub async fn execute_and_export_step(
|
||||
for e in exec_state.errors() {
|
||||
if e.severity.is_err() {
|
||||
return Err(ExecErrorWithState::new(
|
||||
KclErrorWithOutputs::no_outputs(KclError::Semantic(e.clone().into())).into(),
|
||||
KclErrorWithOutputs::no_outputs(KclError::new_semantic(e.clone().into())).into(),
|
||||
exec_state.clone(),
|
||||
));
|
||||
}
|
||||
|
@ -883,7 +883,7 @@ pub async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn recast_dir(dir: &std::path::Path, options: &crate::FormatOptions) -> Result<(), anyhow::Error> {
|
||||
let files = walk_dir(&dir.to_path_buf()).await.map_err(|err| {
|
||||
crate::KclError::Internal(crate::errors::KclErrorDetails::new(
|
||||
crate::KclError::new_internal(crate::errors::KclErrorDetails::new(
|
||||
format!("Failed to walk directory `{}`: {:?}", dir.display(), err),
|
||||
vec![crate::SourceRange::default()],
|
||||
))
|
||||
@ -912,7 +912,7 @@ pub async fn recast_dir(dir: &std::path::Path, options: &crate::FormatOptions) -
|
||||
if ce.severity != crate::errors::Severity::Warning {
|
||||
let report = crate::Report {
|
||||
kcl_source: contents.to_string(),
|
||||
error: crate::KclError::Semantic(ce.clone().into()),
|
||||
error: crate::KclError::new_semantic(ce.clone().into()),
|
||||
filename: file.to_string_lossy().to_string(),
|
||||
};
|
||||
let report = miette::Report::new(report);
|
||||
|
@ -96,7 +96,7 @@ fn topsort(all_modules: &[&str], graph: Graph) -> Result<Vec<Vec<String>>, KclEr
|
||||
if stage_modules.is_empty() {
|
||||
waiting_modules.sort();
|
||||
|
||||
return Err(KclError::ImportCycle(KclErrorDetails::new(
|
||||
return Err(KclError::new_import_cycle(KclErrorDetails::new(
|
||||
format!("circular import of modules not allowed: {}", waiting_modules.join(", ")),
|
||||
// TODO: we can get the right import lines from the AST, but we don't
|
||||
vec![SourceRange::default()],
|
||||
@ -146,7 +146,7 @@ pub(crate) fn import_dependencies(
|
||||
// This is a bit of a hack, but it works for now.
|
||||
ret.lock()
|
||||
.map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to lock mutex: {}", err),
|
||||
Default::default(),
|
||||
))
|
||||
@ -156,7 +156,7 @@ pub(crate) fn import_dependencies(
|
||||
ImportPath::Foreign { path } => {
|
||||
ret.lock()
|
||||
.map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to lock mutex: {}", err),
|
||||
Default::default(),
|
||||
))
|
||||
@ -178,7 +178,7 @@ pub(crate) fn import_dependencies(
|
||||
walk(ret.clone(), prog.into(), path, ctx)?;
|
||||
|
||||
let ret = ret.lock().map_err(|err| {
|
||||
KclError::Internal(KclErrorDetails::new(
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to lock mutex: {}", err),
|
||||
Default::default(),
|
||||
))
|
||||
@ -223,7 +223,7 @@ pub(crate) async fn import_universe(
|
||||
|
||||
let repr = {
|
||||
let Some(module_info) = exec_state.get_module(module_id) else {
|
||||
return Err(KclError::Internal(KclErrorDetails::new(
|
||||
return Err(KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Module {} not found", module_id),
|
||||
vec![import_stmt.into()],
|
||||
)));
|
||||
|
@ -895,3 +895,304 @@ export fn patternCircular2d(
|
||||
/// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
|
||||
useOriginal?: bool = false,
|
||||
): [Sketch; 1+] {}
|
||||
|
||||
/// Compute the ending point of the provided line segment.
|
||||
///
|
||||
/// ```kcl
|
||||
/// w = 15
|
||||
/// cube = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [w, 0], tag = $line1)
|
||||
/// |> line(end = [0, w], tag = $line2)
|
||||
/// |> line(end = [-w, 0], tag = $line3)
|
||||
/// |> line(end = [0, -w], tag = $line4)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> circle(radius = radius, center = segEnd(tag) )
|
||||
/// |> extrude(length = radius)
|
||||
/// }
|
||||
///
|
||||
/// cylinder(radius = 1, tag = line1)
|
||||
/// cylinder(radius = 2, tag = line2)
|
||||
/// cylinder(radius = 3, tag = line3)
|
||||
/// cylinder(radius = 4, tag = line4)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segEnd(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): Point2d {}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'x' axis.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0], tag = $thing)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [segEndX(thing), 0])
|
||||
/// |> line(end = [-20, 10])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segEndX(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Length) {}
|
||||
|
||||
/// Compute the ending point of the provided line segment along the 'y' axis.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, 3], tag = $thing)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> line(end = [0, segEndY(thing)])
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segEndY(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Length) {}
|
||||
|
||||
/// Compute the starting point of the provided line segment.
|
||||
///
|
||||
/// ```kcl
|
||||
/// w = 15
|
||||
/// cube = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [w, 0], tag = $line1)
|
||||
/// |> line(end = [0, w], tag = $line2)
|
||||
/// |> line(end = [-w, 0], tag = $line3)
|
||||
/// |> line(end = [0, -w], tag = $line4)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> circle( radius = radius, center = segStart(tag) )
|
||||
/// |> extrude(length = radius)
|
||||
/// }
|
||||
///
|
||||
/// cylinder(radius = 1, tag = line1)
|
||||
/// cylinder(radius = 2, tag = line2)
|
||||
/// cylinder(radius = 3, tag = line3)
|
||||
/// cylinder(radius = 4, tag = line4)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segStart(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): Point2d {}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'x' axis.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0], tag = $thing)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [20 - segStartX(thing), 0])
|
||||
/// |> line(end = [-20, 10])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segStartX(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Length) {}
|
||||
|
||||
/// Compute the starting point of the provided line segment along the 'y' axis.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, 3], tag = $thing)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> line(end = [0, 20-segStartY(thing)])
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segStartY(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Length) {}
|
||||
|
||||
/// Extract the 'x' axis value of the last line segment in the provided 2-d sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [20, 5])
|
||||
/// |> line(end = [lastSegX(%), 0])
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn lastSegX(
|
||||
/// The sketch whose line segment is being queried.
|
||||
@sketch: Sketch,
|
||||
): number(Length) {}
|
||||
|
||||
/// Extract the 'y' axis value of the last line segment in the provided 2-d sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [20, 5])
|
||||
/// |> line(end = [0, lastSegY(%)])
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn lastSegY(
|
||||
/// The sketch whose line segment is being queried.
|
||||
@sketch: Sketch,
|
||||
): number(Length) {}
|
||||
|
||||
/// Compute the length of the provided line segment.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> angledLine(
|
||||
/// angle = 60,
|
||||
/// length = 10,
|
||||
/// tag = $thing,
|
||||
/// )
|
||||
/// |> tangentialArc(angle = -120, radius = 5)
|
||||
/// |> angledLine(
|
||||
/// angle = -60,
|
||||
/// length = segLen(thing),
|
||||
/// )
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 5)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segLen(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Length) {}
|
||||
|
||||
/// Compute the angle (in degrees) of the provided line segment.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [10, 0])
|
||||
/// |> line(end = [5, 10], tag = $seg01)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> angledLine(angle = segAng(seg01), length = 10)
|
||||
/// |> line(end = [-10, 0])
|
||||
/// |> angledLine(angle = segAng(seg01), length = -15)
|
||||
/// |> close()
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 4)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn segAng(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Angle) {}
|
||||
|
||||
/// Returns the angle coming out of the end of the segment in degrees.
|
||||
///
|
||||
/// ```kcl
|
||||
/// // Horizontal pill.
|
||||
/// pillSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> tangentialArc(end = [0, 10], tag = $arc1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(arc1),
|
||||
/// length = 20,
|
||||
/// )
|
||||
/// |> tangentialArc(end = [0, -10])
|
||||
/// |> close()
|
||||
///
|
||||
/// pillExtrude = extrude(pillSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```kcl
|
||||
/// // Vertical pill. Use absolute coordinate for arc.
|
||||
/// pillSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> tangentialArc(endAbsolute = [10, 20], tag = $arc1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(arc1),
|
||||
/// length = 20,
|
||||
/// )
|
||||
/// |> tangentialArc(end = [-10, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// pillExtrude = extrude(pillSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```kcl
|
||||
/// rectangleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [10, 0], tag = $seg1)
|
||||
/// |> angledLine(
|
||||
/// angle = tangentToEnd(seg1),
|
||||
/// length = 10,
|
||||
/// )
|
||||
/// |> line(end = [0, 10])
|
||||
/// |> line(end = [-20, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// rectangleExtrude = extrude(rectangleSketch, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```kcl
|
||||
/// bottom = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> arc(
|
||||
/// endAbsolute = [10, 10],
|
||||
/// interiorAbsolute = [5, 1],
|
||||
/// tag = $arc1,
|
||||
/// )
|
||||
/// |> angledLine(angle = tangentToEnd(arc1), length = 20)
|
||||
/// |> close()
|
||||
/// ```
|
||||
///
|
||||
/// ```kcl
|
||||
/// circSketch = startSketchOn(XY)
|
||||
/// |> circle(center = [0, 0], radius= 3, tag = $circ)
|
||||
///
|
||||
/// triangleSketch = startSketchOn(XY)
|
||||
/// |> startProfile(at = [-5, 0])
|
||||
/// |> angledLine(angle = tangentToEnd(circ), length = 10)
|
||||
/// |> line(end = [-15, 0])
|
||||
/// |> close()
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn tangentToEnd(
|
||||
/// The line segment being queried by its tag.
|
||||
@tag: tag,
|
||||
): number(Angle) {}
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact commands ascription_unknown_type.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart ascription_unknown_type.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,3 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
```
|
67
rust/kcl-lib/tests/ascription_unknown_type/ast.snap
Normal file
67
rust/kcl-lib/tests/ascription_unknown_type/ast.snap
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Result of parsing ascription_unknown_type.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"declaration": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"id": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "z",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"expr": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "10",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 10.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"start": 0,
|
||||
"ty": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"id": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "NotARealType",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"p_type": "Named",
|
||||
"start": 0,
|
||||
"type": "Primitive"
|
||||
},
|
||||
"type": "AscribedExpression",
|
||||
"type": "AscribedExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 0,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Error from executing ascription_unknown_type.kcl
|
||||
---
|
||||
KCL Semantic error
|
||||
|
||||
× semantic: Unknown type: NotARealType
|
||||
╭────
|
||||
1 │ z = 10: NotARealType
|
||||
· ─┬
|
||||
· ╰── tests/ascription_unknown_type/input.kcl
|
||||
╰────
|
1
rust/kcl-lib/tests/ascription_unknown_type/input.kcl
Normal file
1
rust/kcl-lib/tests/ascription_unknown_type/input.kcl
Normal file
@ -0,0 +1 @@
|
||||
z = 10: NotARealType
|
5
rust/kcl-lib/tests/ascription_unknown_type/ops.snap
Normal file
5
rust/kcl-lib/tests/ascription_unknown_type/ops.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Operations executed ascription_unknown_type.kcl
|
||||
---
|
||||
[]
|
5
rust/kcl-lib/tests/ascription_unknown_type/unparsed.snap
Normal file
5
rust/kcl-lib/tests/ascription_unknown_type/unparsed.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Result of unparsing ascription_unknown_type.kcl
|
||||
---
|
||||
z = 10: NotARealType
|
@ -797,6 +797,36 @@ description: Operations executed import_async.kcl
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 10.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "GroupEnd"
|
||||
},
|
||||
|
@ -37,6 +37,7 @@ flowchart LR
|
||||
25["SweepEdge Adjacent"]
|
||||
26["SweepEdge Adjacent"]
|
||||
27["SweepEdge Adjacent"]
|
||||
28["SweepEdge Adjacent"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
@ -50,28 +51,28 @@ flowchart LR
|
||||
2 ---- 12
|
||||
12 <--x 3
|
||||
3 --- 16
|
||||
3 x--> 21
|
||||
3 --- 21
|
||||
12 <--x 4
|
||||
4 --- 15
|
||||
4 --- 21
|
||||
4 --- 22
|
||||
12 <--x 5
|
||||
5 --- 13
|
||||
5 --- 22
|
||||
5 --- 23
|
||||
12 <--x 6
|
||||
6 --- 20
|
||||
6 --- 23
|
||||
6 --- 24
|
||||
12 <--x 7
|
||||
7 --- 17
|
||||
7 --- 24
|
||||
7 --- 25
|
||||
12 <--x 8
|
||||
8 --- 19
|
||||
8 --- 25
|
||||
8 --- 26
|
||||
12 <--x 9
|
||||
9 --- 14
|
||||
9 --- 26
|
||||
9 --- 27
|
||||
12 <--x 10
|
||||
10 --- 18
|
||||
10 --- 27
|
||||
10 --- 28
|
||||
12 --- 13
|
||||
12 --- 14
|
||||
12 --- 15
|
||||
@ -87,18 +88,21 @@ flowchart LR
|
||||
12 --- 25
|
||||
12 --- 26
|
||||
12 --- 27
|
||||
13 --- 22
|
||||
25 <--x 14
|
||||
14 --- 26
|
||||
15 --- 21
|
||||
12 --- 28
|
||||
22 <--x 13
|
||||
13 --- 23
|
||||
26 <--x 14
|
||||
14 --- 27
|
||||
21 <--x 15
|
||||
15 --- 22
|
||||
16 --- 21
|
||||
27 <--x 16
|
||||
23 <--x 17
|
||||
17 --- 24
|
||||
26 <--x 18
|
||||
18 --- 27
|
||||
24 <--x 19
|
||||
19 --- 25
|
||||
22 <--x 20
|
||||
20 --- 23
|
||||
28 <--x 16
|
||||
24 <--x 17
|
||||
17 --- 25
|
||||
27 <--x 18
|
||||
18 --- 28
|
||||
25 <--x 19
|
||||
19 --- 26
|
||||
23 <--x 20
|
||||
20 --- 24
|
||||
```
|
||||
|
@ -14,7 +14,7 @@ description: Operations executed import_mesh_clone.kcl
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "clone",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
@ -22,7 +22,40 @@ description: Operations executed import_mesh_clone.kcl
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {},
|
||||
"labeledArgs": {
|
||||
"x": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": -2000.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": -2000.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
@ -38,6 +71,36 @@ description: Operations executed import_mesh_clone.kcl
|
||||
"labeledArgs": {},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"x": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 4000.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "clone",
|
||||
@ -51,6 +114,79 @@ description: Operations executed import_mesh_clone.kcl
|
||||
"labeledArgs": {},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 4000.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "clone",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 4000.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "GroupEnd"
|
||||
}
|
||||
|
@ -12,6 +12,177 @@ description: Operations executed import_transform.kcl
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "rotate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"pitch": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"roll": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"yaw": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "translate",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"x": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"z": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "StdLibCall",
|
||||
"name": "scale",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "ImportedGeometry",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"x": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"y": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"z": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.14,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"type": "GroupEnd"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user