github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/rc/js/main.go (about) 1 // Rclone as a wasm library 2 // 3 // This library exports the core rc functionality 4 5 //go:build js 6 7 package main 8 9 import ( 10 "context" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "log" 15 "net/http" 16 "runtime" 17 "syscall/js" 18 19 "github.com/rclone/rclone/fs" 20 "github.com/rclone/rclone/fs/rc" 21 22 // Core functionality we need 23 _ "github.com/rclone/rclone/fs/operations" 24 _ "github.com/rclone/rclone/fs/sync" 25 26 // _ "github.com/rclone/rclone/backend/all" // import all backends 27 28 // Backends 29 _ "github.com/rclone/rclone/backend/memory" 30 ) 31 32 var ( 33 document js.Value 34 jsJSON js.Value 35 ) 36 37 func getElementById(name string) js.Value { 38 node := document.Call("getElementById", name) 39 if node.IsUndefined() { 40 log.Fatalf("Couldn't find element %q", name) 41 } 42 return node 43 } 44 45 func time() int { 46 return js.Global().Get("Date").New().Call("getTime").Int() 47 } 48 49 func paramToValue(in rc.Params) (out js.Value) { 50 return js.Value{} 51 } 52 53 // errorValue turns an error into a js.Value 54 func errorValue(method string, in js.Value, err error) js.Value { 55 fs.Errorf(nil, "rc: %q: error: %v", method, err) 56 // Adjust the error return for some well known errors 57 status := http.StatusInternalServerError 58 switch { 59 case errors.Is(err, fs.ErrorDirNotFound) || errors.Is(err, fs.ErrorObjectNotFound): 60 status = http.StatusNotFound 61 case rc.IsErrParamInvalid(err) || rc.IsErrParamNotFound(err): 62 status = http.StatusBadRequest 63 } 64 return js.ValueOf(map[string]interface{}{ 65 "status": status, 66 "error": err.Error(), 67 "input": in, 68 "path": method, 69 }) 70 } 71 72 // rcCallback is a callback for javascript to access the api 73 // 74 // FIXME should this should return a promise so we can return errors properly? 75 func rcCallback(this js.Value, args []js.Value) interface{} { 76 ctx := context.Background() // FIXME 77 log.Printf("rcCallback: this=%v args=%v", this, args) 78 79 if len(args) != 2 { 80 return errorValue("", js.Undefined(), errors.New("need two parameters to rc call")) 81 } 82 method := args[0].String() 83 inRaw := args[1] 84 var in = rc.Params{} 85 switch inRaw.Type() { 86 case js.TypeNull: 87 case js.TypeObject: 88 inJSON := jsJSON.Call("stringify", inRaw).String() 89 err := json.Unmarshal([]byte(inJSON), &in) 90 if err != nil { 91 return errorValue(method, inRaw, fmt.Errorf("couldn't unmarshal input: %w", err)) 92 } 93 default: 94 return errorValue(method, inRaw, errors.New("in parameter must be null or object")) 95 } 96 97 call := rc.Calls.Get(method) 98 if call == nil { 99 return errorValue(method, inRaw, fmt.Errorf("method %q not found", method)) 100 } 101 102 out, err := call.Fn(ctx, in) 103 if err != nil { 104 return errorValue(method, inRaw, fmt.Errorf("method call failed: %w", err)) 105 } 106 if out == nil { 107 return nil 108 } 109 var out2 map[string]interface{} 110 err = rc.Reshape(&out2, out) 111 if err != nil { 112 return errorValue(method, inRaw, fmt.Errorf("result reshape failed: %w", err)) 113 } 114 115 return js.ValueOf(out2) 116 } 117 118 func main() { 119 log.Printf("Running on goos/goarch = %s/%s", runtime.GOOS, runtime.GOARCH) 120 if js.Global().IsUndefined() { 121 log.Fatalf("Didn't find Global - not running in browser") 122 } 123 document = js.Global().Get("document") 124 if document.IsUndefined() { 125 log.Fatalf("Didn't find document - not running in browser") 126 } 127 128 jsJSON = js.Global().Get("JSON") 129 if jsJSON.IsUndefined() { 130 log.Fatalf("can't find JSON") 131 } 132 133 // Set rc 134 js.Global().Set("rc", js.FuncOf(rcCallback)) 135 136 // Signal that it is valid 137 rcValidResolve := js.Global().Get("rcValidResolve") 138 if rcValidResolve.IsUndefined() { 139 log.Fatalf("Didn't find rcValidResolve") 140 } 141 rcValidResolve.Invoke() 142 143 // Wait forever 144 select {} 145 }