diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index e7803a445..0d7fc98ef 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -14,3 +14,4 @@ - Fixes `null` being set on a nested shape not working in certain cases when connections also pointed to that shape [#1830](https://github.com/terrastruct/d2/pull/1830) - Fixes edge case of bad import syntax crashing using d2 as a library [#1829](https://github.com/terrastruct/d2/pull/1829) - Fixes `style.fill` not applying to markdown [#1872](https://github.com/terrastruct/d2/pull/1872) +- Fixes compiler erroring on certain styles when the shape's `shape` value is not all lowercase (e.g. `Circle`) [#1887](https://github.com/terrastruct/d2/pull/1887) diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 664512b5a..a1ff2de63 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -338,11 +338,11 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { } if obj.Parent != nil { - if obj.Parent.Shape.Value == d2target.ShapeSQLTable { + if strings.EqualFold(obj.Parent.Shape.Value, d2target.ShapeSQLTable) { c.errorf(f.LastRef().AST(), "sql_table columns cannot have children") return } - if obj.Parent.Shape.Value == d2target.ShapeClass { + if strings.EqualFold(obj.Parent.Shape.Value, d2target.ShapeClass) { c.errorf(f.LastRef().AST(), "class fields cannot have children") return } @@ -511,7 +511,7 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) { return } attrs.Shape.Value = scalar.ScalarString() - if attrs.Shape.Value == d2target.ShapeCode { + if strings.EqualFold(attrs.Shape.Value, d2target.ShapeCode) { // Explicit code shape is plaintext. attrs.Language = d2target.ShapeText } @@ -1009,12 +1009,12 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) { } } if obj.Style.DoubleBorder != nil { - if obj.Shape.Value != "" && obj.Shape.Value != d2target.ShapeSquare && obj.Shape.Value != d2target.ShapeRectangle && obj.Shape.Value != d2target.ShapeCircle && obj.Shape.Value != d2target.ShapeOval { + if obj.Shape.Value != "" && !strings.EqualFold(obj.Shape.Value, d2target.ShapeSquare) && !strings.EqualFold(obj.Shape.Value, d2target.ShapeRectangle) && !strings.EqualFold(obj.Shape.Value, d2target.ShapeCircle) && !strings.EqualFold(obj.Shape.Value, d2target.ShapeOval) { c.errorf(obj.Style.DoubleBorder.MapKey, `key "double-border" can only be applied to squares, rectangles, circles, ovals`) } } case "shape": - if obj.Shape.Value == d2target.ShapeImage && obj.Icon == nil { + if strings.EqualFold(obj.Shape.Value, d2target.ShapeImage) && obj.Icon == nil { c.errorf(f.LastPrimaryKey(), `image shape must include an "icon" field`) } @@ -1024,14 +1024,14 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) { c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid shape, can only set "%s" for arrowheads`, obj.Shape.Value)) } case "constraint": - if obj.Shape.Value != d2target.ShapeSQLTable { + if !strings.EqualFold(obj.Shape.Value, d2target.ShapeSQLTable) { c.errorf(f.LastPrimaryKey(), `"constraint" keyword can only be used in "sql_table" shapes`) } } return } - if obj.Shape.Value == d2target.ShapeImage { + if strings.EqualFold(obj.Shape.Value, d2target.ShapeImage) { c.errorf(f.LastRef().AST(), "image shapes cannot have children.") return } @@ -1044,7 +1044,7 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) { func (c *compiler) validateLabels(g *d2graph.Graph) { for _, obj := range g.Objects { - if obj.Shape.Value != d2target.ShapeText { + if !strings.EqualFold(obj.Shape.Value, d2target.ShapeText) { continue } if obj.Attributes.Language != "" { diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index bd2f9530d..7ce85198f 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -3482,6 +3482,24 @@ hi: "1 ${x} 2" assert.Equal(t, "1 im a var 2", g.Objects[0].Label.Value) }, }, + { + name: "double-border", + run: func(t *testing.T) { + assertCompile(t, ` +a.shape: Circle +a.style.double-border: true +`, "") + }, + }, + { + name: "invalid-double-border", + run: func(t *testing.T) { + assertCompile(t, ` +a.shape: hexagon +a.style.double-border: true +`, `d2/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.d2:3:1: key "double-border" can only be applied to squares, rectangles, circles, ovals`) + }, + }, { name: "single-quoted", run: func(t *testing.T) { diff --git a/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json b/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json new file mode 100644 index 000000000..8b7f30d5d --- /dev/null +++ b/testdata/d2compiler/TestCompile2/vars/basic/double-border.exp.json @@ -0,0 +1,234 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,0:0:0-3:0:45", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:0:1-1:15:16", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:0:1-1:7:8", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:2:3-1:7:8", + "value": [ + { + "string": "shape", + "raw_string": "shape" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:9:10-1:15:16", + "value": [ + { + "string": "Circle", + "raw_string": "Circle" + } + ] + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:0:17-2:27:44", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:0:17-2:21:38", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:0:17-2:1:18", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:2:19-2:7:24", + "value": [ + { + "string": "style", + "raw_string": "style" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:8:25-2:21:38", + "value": [ + { + "string": "double-border", + "raw_string": "double-border" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "boolean": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:23:40-2:27:44", + "value": true + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "a", + "id_val": "a", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:0:1-1:7:8", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,1:2:3-1:7:8", + "value": [ + { + "string": "shape", + "raw_string": "shape" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + }, + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:0:17-2:21:38", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:0:17-2:1:18", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:2:19-2:7:24", + "value": [ + { + "string": "style", + "raw_string": "style" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/double-border.d2,2:8:25-2:21:38", + "value": [ + { + "string": "double-border", + "raw_string": "double-border" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "a" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": { + "doubleBorder": { + "value": "true" + } + }, + "near_key": null, + "shape": { + "value": "Circle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +} diff --git a/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.exp.json b/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.exp.json new file mode 100644 index 000000000..6bbe158b3 --- /dev/null +++ b/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.exp.json @@ -0,0 +1,11 @@ +{ + "graph": null, + "err": { + "errs": [ + { + "range": "d2/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.d2,2:0:18-2:27:45", + "errmsg": "d2/testdata/d2compiler/TestCompile2/vars/basic/invalid-double-border.d2:3:1: key \"double-border\" can only be applied to squares, rectangles, circles, ovals" + } + ] + } +}