rclone/fs/rc/js/main.go
Nick Craig-Wood e43b5ce5e5 Remove github.com/pkg/errors and replace with std library version
This is possible now that we no longer support go1.12 and brings
rclone into line with standard practices in the Go world.

This also removes errors.New and errors.Errorf from lib/errors and
prefers the stdlib errors package over lib/errors.
2021-11-07 11:53:30 +00:00

147 lines
3.5 KiB
Go

// Rclone as a wasm library
//
// This library exports the core rc functionality
//go:build js
// +build js
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"runtime"
"syscall/js"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/rc"
// Core functionality we need
_ "github.com/rclone/rclone/fs/operations"
_ "github.com/rclone/rclone/fs/sync"
// _ "github.com/rclone/rclone/backend/all" // import all backends
// Backends
_ "github.com/rclone/rclone/backend/memory"
)
var (
document js.Value
jsJSON js.Value
)
func getElementById(name string) js.Value {
node := document.Call("getElementById", name)
if node.IsUndefined() {
log.Fatalf("Couldn't find element %q", name)
}
return node
}
func time() int {
return js.Global().Get("Date").New().Call("getTime").Int()
}
func paramToValue(in rc.Params) (out js.Value) {
return js.Value{}
}
// errorValue turns an error into a js.Value
func errorValue(method string, in js.Value, err error) js.Value {
fs.Errorf(nil, "rc: %q: error: %v", method, err)
// Adjust the error return for some well known errors
status := http.StatusInternalServerError
switch {
case errors.Is(err, fs.ErrorDirNotFound) || errors.Is(err, fs.ErrorObjectNotFound):
status = http.StatusNotFound
case rc.IsErrParamInvalid(err) || rc.IsErrParamNotFound(err):
status = http.StatusBadRequest
}
return js.ValueOf(map[string]interface{}{
"status": status,
"error": err.Error(),
"input": in,
"path": method,
})
}
// rcCallback is a callback for javascript to access the api
//
// FIXME should this should return a promise so we can return errors properly?
func rcCallback(this js.Value, args []js.Value) interface{} {
ctx := context.Background() // FIXME
log.Printf("rcCallback: this=%v args=%v", this, args)
if len(args) != 2 {
return errorValue("", js.Undefined(), errors.New("need two parameters to rc call"))
}
method := args[0].String()
inRaw := args[1]
var in = rc.Params{}
switch inRaw.Type() {
case js.TypeNull:
case js.TypeObject:
inJSON := jsJSON.Call("stringify", inRaw).String()
err := json.Unmarshal([]byte(inJSON), &in)
if err != nil {
return errorValue(method, inRaw, fmt.Errorf("couldn't unmarshal input: %w", err))
}
default:
return errorValue(method, inRaw, errors.New("in parameter must be null or object"))
}
call := rc.Calls.Get(method)
if call == nil {
return errorValue(method, inRaw, fmt.Errorf("method %q not found", method))
}
out, err := call.Fn(ctx, in)
if err != nil {
return errorValue(method, inRaw, fmt.Errorf("method call failed: %w", err))
}
if out == nil {
return nil
}
var out2 map[string]interface{}
err = rc.Reshape(&out2, out)
if err != nil {
return errorValue(method, inRaw, fmt.Errorf("result reshape failed: %w", err))
}
return js.ValueOf(out2)
}
func main() {
log.Printf("Running on goos/goarch = %s/%s", runtime.GOOS, runtime.GOARCH)
if js.Global().IsUndefined() {
log.Fatalf("Didn't find Global - not running in browser")
}
document = js.Global().Get("document")
if document.IsUndefined() {
log.Fatalf("Didn't find document - not running in browser")
}
jsJSON = js.Global().Get("JSON")
if jsJSON.IsUndefined() {
log.Fatalf("can't find JSON")
}
// Set rc
js.Global().Set("rc", js.FuncOf(rcCallback))
// Signal that it is valid
rcValidResolve := js.Global().Get("rcValidResolve")
if rcValidResolve.IsUndefined() {
log.Fatalf("Didn't find rcValidResolve")
}
rcValidResolve.Invoke()
// Wait forever
select {}
}