146 lines
3.5 KiB
Go
146 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"
|
|
"log"
|
|
"net/http"
|
|
"runtime"
|
|
"syscall/js"
|
|
|
|
"github.com/pkg/errors"
|
|
"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
|
|
errOrig := errors.Cause(err)
|
|
status := http.StatusInternalServerError
|
|
switch {
|
|
case errOrig == fs.ErrorDirNotFound || errOrig == 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, errors.Wrap(err, "couldn't unmarshal input"))
|
|
}
|
|
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, errors.Errorf("method %q not found", method))
|
|
}
|
|
|
|
out, err := call.Fn(ctx, in)
|
|
if err != nil {
|
|
return errorValue(method, inRaw, errors.Wrap(err, "method call failed"))
|
|
}
|
|
if out == nil {
|
|
return nil
|
|
}
|
|
var out2 map[string]interface{}
|
|
err = rc.Reshape(&out2, out)
|
|
if err != nil {
|
|
return errorValue(method, inRaw, errors.Wrap(err, "result reshape failed"))
|
|
}
|
|
|
|
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 {}
|
|
}
|