github.com/0chain/gosdk@v1.17.11/core/sys/fs_mem.go (about)

     1  //go:build js && wasm
     2  // +build js,wasm
     3  
     4  package sys
     5  
     6  import (
     7  	"errors"
     8  	"io/fs"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  	"syscall/js"
    14  	"time"
    15  
    16  	"github.com/0chain/gosdk/core/common"
    17  	"github.com/0chain/gosdk/wasmsdk/jsbridge"
    18  	"github.com/valyala/bytebufferpool"
    19  )
    20  
    21  // MemFS implement file system on memory
    22  type MemFS struct {
    23  	files map[string]*MemFile
    24  	dirs  map[string]js.Value
    25  	sync.Mutex
    26  }
    27  
    28  // NewMemFS create MemFS instance
    29  func NewMemFS() FS {
    30  	return &MemFS{
    31  		files: make(map[string]*MemFile),
    32  		dirs:  make(map[string]js.Value),
    33  	}
    34  }
    35  
    36  // Open opens the named file for reading. If successful, methods on
    37  // the returned file can be used for reading; the associated file
    38  // descriptor has mode O_RDONLY.
    39  // If there is an error, it will be of type *PathError.
    40  func (mfs *MemFS) Open(name string) (File, error) {
    41  	mfs.Lock()
    42  	defer mfs.Unlock()
    43  	file := mfs.files[name]
    44  	if file != nil {
    45  		return file, nil
    46  	}
    47  
    48  	fileName := filepath.Base(name)
    49  
    50  	file = &MemFile{Name: fileName, Mode: fs.ModePerm, ModTime: time.Now()}
    51  
    52  	mfs.files[name] = file
    53  
    54  	return file, nil
    55  }
    56  
    57  func (mfs *MemFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
    58  	mfs.Lock()
    59  	defer mfs.Unlock()
    60  	file := mfs.files[name]
    61  	if file != nil {
    62  		return file, nil
    63  	}
    64  
    65  	fileName := filepath.Base(name)
    66  	file = &MemFile{Name: fileName, Mode: perm, ModTime: time.Now()}
    67  
    68  	mfs.files[name] = file
    69  
    70  	return file, nil
    71  
    72  }
    73  
    74  // ReadFile reads the file named by filename and returns the contents.
    75  func (mfs *MemFS) ReadFile(name string) ([]byte, error) {
    76  	mfs.Lock()
    77  	defer mfs.Unlock()
    78  	file, ok := mfs.files[name]
    79  	if ok {
    80  		return file.Buffer, nil
    81  	}
    82  
    83  	return nil, os.ErrNotExist
    84  }
    85  
    86  // WriteFile writes data to a file named by filename.
    87  func (mfs *MemFS) WriteFile(name string, data []byte, perm os.FileMode) error {
    88  	fileName := filepath.Base(name)
    89  	file := &MemFile{Name: fileName, Mode: perm, ModTime: time.Now()}
    90  	mfs.Lock()
    91  	defer mfs.Unlock()
    92  	mfs.files[name] = file
    93  
    94  	return nil
    95  }
    96  
    97  // Remove removes the named file or (empty) directory.
    98  // If there is an error, it will be of type *PathError.
    99  func (mfs *MemFS) Remove(name string) error {
   100  	mfs.Lock()
   101  	defer mfs.Unlock()
   102  	f, ok := mfs.files[name]
   103  	if ok {
   104  		b := f.Buffer
   105  		if len(b) > 0 {
   106  			buff := &bytebufferpool.ByteBuffer{
   107  				B: b,
   108  			}
   109  			common.MemPool.Put(buff)
   110  		}
   111  	}
   112  	delete(mfs.files, name)
   113  	return nil
   114  }
   115  
   116  // MkdirAll creates a directory named path
   117  func (mfs *MemFS) MkdirAll(path string, perm os.FileMode) error {
   118  	return nil
   119  }
   120  
   121  // Stat returns a FileInfo describing the named file.
   122  // If there is an error, it will be of type *PathError.
   123  func (mfs *MemFS) Stat(name string) (fs.FileInfo, error) {
   124  	mfs.Lock()
   125  	defer mfs.Unlock()
   126  	file, ok := mfs.files[name]
   127  	if ok {
   128  		return file.Stat()
   129  	}
   130  
   131  	return nil, os.ErrNotExist
   132  }
   133  
   134  func (mfs *MemFS) LoadProgress(progressID string) ([]byte, error) {
   135  	key := filepath.Base(progressID)
   136  	val := js.Global().Get("localStorage").Call("getItem", key)
   137  	if val.Truthy() {
   138  		return []byte(val.String()), nil
   139  	}
   140  	return nil, os.ErrNotExist
   141  }
   142  
   143  func (mfs *MemFS) SaveProgress(progressID string, data []byte, _ fs.FileMode) error {
   144  	key := filepath.Base(progressID)
   145  	js.Global().Get("localStorage").Call("setItem", key, string(data))
   146  	return nil
   147  }
   148  
   149  func (mfs *MemFS) RemoveProgress(progressID string) error {
   150  	key := filepath.Base(progressID)
   151  	js.Global().Get("localStorage").Call("removeItem", key)
   152  	return nil
   153  }
   154  
   155  func (mfs *MemFS) CreateDirectory(dirID string) error {
   156  	if !js.Global().Get("showDirectoryPicker").Truthy() || !js.Global().Get("WritableStream").Truthy() {
   157  		return errors.New("dir_picker: not supported")
   158  	}
   159  	showDirectoryPicker := js.Global().Get("showDirectoryPicker")
   160  	dirHandle, err := jsbridge.Await(showDirectoryPicker.Invoke())
   161  	if len(err) > 0 && !err[0].IsNull() {
   162  		return errors.New("dir_picker: " + err[0].String())
   163  	}
   164  	mfs.dirs[dirID] = dirHandle[0]
   165  	return nil
   166  }
   167  
   168  func (mfs *MemFS) GetFileHandler(dirID, path string) (File, error) {
   169  	dirHandler, ok := mfs.dirs[dirID]
   170  	if !ok {
   171  		return nil, errors.New("dir_picker: directory not found")
   172  	}
   173  	currHandler, err := mfs.mkdir(dirHandler, filepath.Dir(path))
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	return jsbridge.NewFileWriterFromHandle(currHandler, filepath.Base(path))
   178  }
   179  
   180  func (mfs *MemFS) RemoveAllDirectories() {
   181  	for k := range mfs.dirs {
   182  		delete(mfs.dirs, k)
   183  	}
   184  }
   185  
   186  func (mfs *MemFS) mkdir(dirHandler js.Value, dirPath string) (js.Value, error) {
   187  	if dirPath == "/" {
   188  		return dirHandler, nil
   189  	}
   190  	currHandler, ok := mfs.dirs[dirPath]
   191  	if !ok {
   192  		currHandler = dirHandler
   193  		paths := strings.Split(dirPath, "/")
   194  		paths = paths[1:]
   195  		currPath := "/"
   196  		for _, path := range paths {
   197  			currPath = filepath.Join(currPath, path)
   198  			handler, ok := mfs.dirs[currPath]
   199  			if ok {
   200  				currHandler = handler
   201  				continue
   202  			}
   203  			options := js.Global().Get("Object").New()
   204  			options.Set("create", true)
   205  			currHandlers, err := jsbridge.Await(currHandler.Call("getDirectoryHandle", path, options))
   206  			if len(err) > 0 && !err[0].IsNull() {
   207  				return js.Value{}, errors.New("dir_picker: " + err[0].String())
   208  			}
   209  			currHandler = currHandlers[0]
   210  			mfs.dirs[currPath] = currHandler
   211  		}
   212  		if !currHandler.Truthy() {
   213  			return js.Value{}, errors.New("dir_picker: failed to create directory")
   214  		}
   215  	}
   216  	return currHandler, nil
   217  }