Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
alixander committed Dec 29, 2024
1 parent 2a56126 commit 8d9bf98
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 319 deletions.
70 changes: 70 additions & 0 deletions d2js/d2wasm/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//go:build js && wasm

package d2wasm

import (
"encoding/json"
"fmt"
"syscall/js"
)

type D2API struct {
exports map[string]js.Func
}

func NewD2API() *D2API {
return &D2API{
exports: make(map[string]js.Func),
}
}

func (api *D2API) Register(name string, fn func(args []js.Value) (interface{}, error)) {
api.exports[name] = wrapWASMCall(fn)
}

func (api *D2API) ExportTo(target js.Value) {
d2Namespace := make(map[string]interface{})
for name, fn := range api.exports {
d2Namespace[name] = fn
}
target.Set("d2", js.ValueOf(d2Namespace))
}

func wrapWASMCall(fn func(args []js.Value) (interface{}, error)) js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) (result any) {
defer func() {
if r := recover(); r != nil {
resp := WASMResponse{
Error: &WASMError{
Message: fmt.Sprintf("panic recovered: %v", r),
Code: 500,
},
}
jsonResp, _ := json.Marshal(resp)
result = string(jsonResp)
}
}()

data, err := fn(args)
if err != nil {
wasmErr, ok := err.(*WASMError)
if !ok {
wasmErr = &WASMError{
Message: err.Error(),
Code: 500,
}
}
resp := WASMResponse{
Error: wasmErr,
}
jsonResp, _ := json.Marshal(resp)
return string(jsonResp)
}

resp := WASMResponse{
Data: data,
}
jsonResp, _ := json.Marshal(resp)
return string(jsonResp)
})
}
161 changes: 161 additions & 0 deletions d2js/d2wasm/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//go:build js && wasm

package d2wasm

import (
"encoding/json"
"strings"
"syscall/js"

"oss.terrastruct.com/d2/d2ast"
"oss.terrastruct.com/d2/d2compiler"
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2lsp"
"oss.terrastruct.com/d2/d2oracle"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/lib/version"
)

func GetParentID(args []js.Value) (interface{}, error) {
if len(args) < 1 {
return nil, &WASMError{Message: "missing id argument", Code: 400}
}

id := args[0].String()
mk, err := d2parser.ParseMapKey(id)
if err != nil {
return nil, &WASMError{Message: err.Error(), Code: 400}
}

if len(mk.Edges) > 0 {
return "", nil
}

if mk.Key != nil {
if len(mk.Key.Path) == 1 {
return "root", nil
}
mk.Key.Path = mk.Key.Path[:len(mk.Key.Path)-1]
return strings.Join(mk.Key.StringIDA(), "."), nil
}

return "", nil
}

func GetObjOrder(args []js.Value) (interface{}, error) {
if len(args) < 1 {
return nil, &WASMError{Message: "missing dsl argument", Code: 400}
}

dsl := args[0].String()
g, _, err := d2compiler.Compile("", strings.NewReader(dsl), &d2compiler.CompileOptions{
UTF16Pos: true,
})
if err != nil {
return nil, &WASMError{Message: err.Error(), Code: 400}
}

objOrder, err := d2oracle.GetObjOrder(g, nil)
if err != nil {
return nil, &WASMError{Message: err.Error(), Code: 500}
}

return map[string]interface{}{
"order": objOrder,
}, nil
}

func GetRefRanges(args []js.Value) (interface{}, error) {
if len(args) < 4 {
return nil, &WASMError{Message: "missing required arguments", Code: 400}
}

var fs map[string]string
if err := json.Unmarshal([]byte(args[0].String()), &fs); err != nil {
return nil, &WASMError{Message: "invalid fs argument", Code: 400}
}

file := args[1].String()
key := args[2].String()

var boardPath []string
if err := json.Unmarshal([]byte(args[3].String()), &boardPath); err != nil {
return nil, &WASMError{Message: "invalid boardPath argument", Code: 400}
}

ranges, importRanges, err := d2lsp.GetRefRanges(file, fs, boardPath, key)
if err != nil {
return nil, &WASMError{Message: err.Error(), Code: 500}
}

return RefRangesResponse{
Ranges: ranges,
ImportRanges: importRanges,
}, nil
}

func Compile(args []js.Value) (interface{}, error) {
if len(args) < 1 {
return nil, &WASMError{Message: "missing script argument", Code: 400}
}

script := args[0].String()
g, _, err := d2compiler.Compile("", strings.NewReader(script), &d2compiler.CompileOptions{
UTF16Pos: true,
})
if err != nil {
if pe, ok := err.(*d2parser.ParseError); ok {
return nil, &WASMError{Message: pe.Error(), Code: 400}
}
return nil, &WASMError{Message: err.Error(), Code: 500}
}

newScript := d2format.Format(g.AST)
if script != newScript {
return map[string]string{"result": newScript}, nil
}

return nil, nil
}

func GetBoardAtPosition(args []js.Value) (interface{}, error) {
if len(args) < 3 {
return nil, &WASMError{Message: "missing required arguments", Code: 400}
}

dsl := args[0].String()
line := args[1].Int()
column := args[2].Int()

boardPath, err := d2lsp.GetBoardAtPosition(dsl, d2ast.Position{
Line: line,
Column: column,
})
if err != nil {
return nil, &WASMError{Message: err.Error(), Code: 500}
}

return BoardPositionResponse{BoardPath: boardPath}, nil
}

func Encode(args []js.Value) (interface{}, error) {
if len(args) < 1 {
return nil, &WASMError{Message: "missing script argument", Code: 400}
}

script := args[0].String()
return map[string]string{"result": script}, nil
}

func Decode(args []js.Value) (interface{}, error) {
if len(args) < 1 {
return nil, &WASMError{Message: "missing script argument", Code: 400}
}

script := args[0].String()
return map[string]string{"result": script}, nil
}

func GetVersion(args []js.Value) (interface{}, error) {
return version.Version, nil
}
28 changes: 28 additions & 0 deletions d2js/d2wasm/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build js && wasm

package d2wasm

import "oss.terrastruct.com/d2/d2ast"

type WASMResponse struct {
Data interface{} `json:"data,omitempty"`
Error *WASMError `json:"error,omitempty"`
}

type WASMError struct {
Message string `json:"message"`
Code int `json:"code"`
}

func (e *WASMError) Error() string {
return e.Message
}

type RefRangesResponse struct {
Ranges []d2ast.Range `json:"ranges"`
ImportRanges []d2ast.Range `json:"importRanges"`
}

type BoardPositionResponse struct {
BoardPath []string `json:"boardPath"`
}
Loading

0 comments on commit 8d9bf98

Please sign in to comment.