forked from Team-Ortix/golang-wasm
feat: Implement a type-safe Object struct
parent
da1769920a
commit
392b175abf
@ -0,0 +1,3 @@
|
||||
module gitea.teamortix.com/Team-Ortix/go-mod-wasm/wasm
|
||||
|
||||
go 1.16
|
@ -0,0 +1,142 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// TypeMismatchError is returned when a function is called with a js.Value that has the incorrect type.
|
||||
type TypeMismatchError struct {
|
||||
Expected js.Type
|
||||
Actual js.Type
|
||||
}
|
||||
|
||||
func (e TypeMismatchError) Error() string {
|
||||
return fmt.Sprintf("expected %v type, got %v type instead", e.Expected, e.Actual)
|
||||
}
|
||||
|
||||
// Global returns the global object as a Object.
|
||||
// If the global object is not an object, it panics.
|
||||
func Global() Object {
|
||||
global, err := NewObject(js.Global())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return global
|
||||
}
|
||||
|
||||
// Object is a statically typed Object instance of js.Value.
|
||||
// It should be instantiated with NewObject where it is checked for type instead of directly.
|
||||
// Calling methods on a zero Object is undefined behaviour.
|
||||
type Object struct {
|
||||
value js.Value
|
||||
}
|
||||
|
||||
// NewObject instantiates a new Object with the provided js.Value.
|
||||
// If the js.Value is not an Object, it returns a TypeMismatchError.
|
||||
func NewObject(raw js.Value) (Object, error) {
|
||||
if raw.Type() != js.TypeObject {
|
||||
return Object{}, TypeMismatchError{
|
||||
Expected: js.TypeObject,
|
||||
Actual: raw.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
return Object{raw}, nil
|
||||
}
|
||||
|
||||
// Get recursively gets the Object's properties, returning a TypeMismatchError if it encounters a non-object while
|
||||
// descending through the object.
|
||||
func (o Object) Get(path ...string) (js.Value, error) {
|
||||
current := o.value
|
||||
for _, v := range path {
|
||||
if current.Type() != js.TypeObject {
|
||||
return js.Value{}, TypeMismatchError{
|
||||
Expected: js.TypeObject,
|
||||
Actual: current.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
current = current.Get(v)
|
||||
}
|
||||
return current, nil
|
||||
}
|
||||
|
||||
// Expect is a helper function that calls Get and checks the type of the final result.
|
||||
// It returns a TypeMismatchError if a non-object is encountered while descending the path or the final type does not
|
||||
// match with the provided expected type.
|
||||
func (o Object) Expect(expectedType js.Type, path ...string) (js.Value, error) {
|
||||
value, err := o.Get(path...)
|
||||
if err != nil {
|
||||
return js.Value{}, err
|
||||
}
|
||||
|
||||
if value.Type() != expectedType {
|
||||
return js.Value{}, TypeMismatchError{
|
||||
Expected: expectedType,
|
||||
Actual: value.Type(),
|
||||
}
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Delete removes property p from the object.
|
||||
func (o Object) Delete(p string) {
|
||||
o.value.Delete(p)
|
||||
}
|
||||
|
||||
// Equal checks if the object is equal to another value.
|
||||
// It is equivalent to JS's === operator.
|
||||
func (o Object) Equal(v js.Value) bool {
|
||||
return o.value.Equal(v)
|
||||
}
|
||||
|
||||
// Index indexes into the object.
|
||||
func (o Object) Index(i int) js.Value {
|
||||
return o.value.Index(i)
|
||||
}
|
||||
|
||||
// InstanceOf implements the instanceof operator in JavaScript.
|
||||
// If t is not a constructor, this function returns false.
|
||||
func (o Object) InstanceOf(t js.Value) bool {
|
||||
if t.Type() != js.TypeFunction {
|
||||
return false
|
||||
}
|
||||
return o.value.InstanceOf(t)
|
||||
}
|
||||
|
||||
// JSValue implements the js.Wrapper interface.
|
||||
func (o Object) JSValue() js.Value {
|
||||
return o.value
|
||||
}
|
||||
|
||||
// Length returns the "length" property of the object.
|
||||
func (o Object) Length() int {
|
||||
return o.value.Length()
|
||||
}
|
||||
|
||||
// Set sets the property p to the value of js.ValueOf(x).
|
||||
func (o Object) Set(p string, x interface{}) {
|
||||
o.value.Set(p, x)
|
||||
}
|
||||
|
||||
// SetIndex sets the index i to the value of js.ValueOf(x).
|
||||
func (o Object) SetIndex(i int, x interface{}) {
|
||||
o.value.SetIndex(i, x)
|
||||
}
|
||||
|
||||
// String returns the object marshalled as a JSON string for debugging purposes.
|
||||
func (o Object) String() string {
|
||||
stringify, err := Global().Expect(js.TypeFunction, "JSON", "stringify")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
jsonStr := stringify.Invoke(o)
|
||||
if jsonStr.Type() != js.TypeString {
|
||||
panic("JSON.stringify returned a " + jsonStr.Type().String())
|
||||
}
|
||||
|
||||
return jsonStr.String()
|
||||
}
|
Loading…
Reference in New Issue