github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/rc/cache.go (about)

     1  // Utilities for accessing the Fs cache
     2  
     3  package rc
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/rclone/rclone/fs"
    11  	"github.com/rclone/rclone/fs/cache"
    12  	"github.com/rclone/rclone/fs/config/configmap"
    13  	"github.com/rclone/rclone/fs/filter"
    14  	"github.com/rclone/rclone/fs/fspath"
    15  )
    16  
    17  // getFsName gets an fs name from fsName either from the cache or direct
    18  func getFsName(in Params, fsName string) (fsString string, err error) {
    19  	fsString, err = in.GetString(fsName)
    20  	if err != nil {
    21  		if !IsErrParamInvalid(err) {
    22  			return fsString, err
    23  		}
    24  		fsString, err = getConfigMap(in, fsName)
    25  		if err != nil {
    26  			return fsString, err
    27  		}
    28  	}
    29  	return fsString, err
    30  }
    31  
    32  // GetFsNamed gets an fs.Fs named fsName either from the cache or creates it afresh
    33  func GetFsNamed(ctx context.Context, in Params, fsName string) (f fs.Fs, err error) {
    34  	fsString, err := getFsName(in, fsName)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	return cache.Get(ctx, fsString)
    39  }
    40  
    41  // GetFsNamedFileOK gets an fs.Fs named fsName either from the cache or creates it afresh
    42  //
    43  // If the fs.Fs points to a single file then it returns a new ctx with
    44  // filters applied to make the listings return only that file.
    45  func GetFsNamedFileOK(ctx context.Context, in Params, fsName string) (newCtx context.Context, f fs.Fs, err error) {
    46  	fsString, err := getFsName(in, fsName)
    47  	if err != nil {
    48  		return ctx, nil, err
    49  	}
    50  	f, err = cache.Get(ctx, fsString)
    51  	if err == nil {
    52  		return ctx, f, nil
    53  	} else if !errors.Is(err, fs.ErrorIsFile) {
    54  		return ctx, nil, err
    55  	}
    56  	// f points to the directory above the file so find the remote name
    57  	_, fileName, err := fspath.Split(fsString)
    58  	if err != nil {
    59  		return ctx, f, err
    60  	}
    61  	ctx, fi := filter.AddConfig(ctx)
    62  	if !fi.InActive() {
    63  		return ctx, f, fmt.Errorf("can't limit to single files when using filters: %q", fileName)
    64  	}
    65  	// Limit transfers to this file
    66  	err = fi.AddFile(fileName)
    67  	if err != nil {
    68  		return ctx, f, fmt.Errorf("failed to limit to single file: %w", err)
    69  	}
    70  	return ctx, f, nil
    71  }
    72  
    73  // getConfigMap gets the config as a map from in and converts it to a
    74  // config string
    75  //
    76  // It uses the special parameters _name to name the remote and _root
    77  // to make the root of the remote.
    78  func getConfigMap(in Params, fsName string) (fsString string, err error) {
    79  	var m configmap.Simple
    80  	err = in.GetStruct(fsName, &m)
    81  	if err != nil {
    82  		return fsString, err
    83  	}
    84  	pop := func(key string) string {
    85  		value := m[key]
    86  		delete(m, key)
    87  		return value
    88  	}
    89  	Type := pop("type")
    90  	name := pop("_name")
    91  	root := pop("_root")
    92  	if name != "" {
    93  		fsString = name
    94  	} else if Type != "" {
    95  		fsString = ":" + Type
    96  	} else {
    97  		return fsString, errors.New(`couldn't find "type" or "_name" in JSON config definition`)
    98  	}
    99  	config := m.String()
   100  	if config != "" {
   101  		fsString += ","
   102  		fsString += config
   103  	}
   104  	fsString += ":"
   105  	fsString += root
   106  	return fsString, nil
   107  }
   108  
   109  // GetFs gets an fs.Fs named "fs" either from the cache or creates it afresh
   110  func GetFs(ctx context.Context, in Params) (f fs.Fs, err error) {
   111  	return GetFsNamed(ctx, in, "fs")
   112  }
   113  
   114  // GetFsAndRemoteNamed gets the fsName parameter from in, makes a
   115  // remote or fetches it from the cache then gets the remoteName
   116  // parameter from in too.
   117  func GetFsAndRemoteNamed(ctx context.Context, in Params, fsName, remoteName string) (f fs.Fs, remote string, err error) {
   118  	remote, err = in.GetString(remoteName)
   119  	if err != nil {
   120  		return
   121  	}
   122  	f, err = GetFsNamed(ctx, in, fsName)
   123  	return
   124  
   125  }
   126  
   127  // GetFsAndRemote gets the `fs` parameter from in, makes a remote or
   128  // fetches it from the cache then gets the `remote` parameter from in
   129  // too.
   130  func GetFsAndRemote(ctx context.Context, in Params) (f fs.Fs, remote string, err error) {
   131  	return GetFsAndRemoteNamed(ctx, in, "fs", "remote")
   132  }
   133  
   134  func init() {
   135  	Add(Call{
   136  		Path:         "fscache/clear",
   137  		Fn:           rcCacheClear,
   138  		Title:        "Clear the Fs cache.",
   139  		AuthRequired: true,
   140  		Help: `
   141  This clears the fs cache. This is where remotes created from backends
   142  are cached for a short while to make repeated rc calls more efficient.
   143  
   144  If you change the parameters of a backend then you may want to call
   145  this to clear an existing remote out of the cache before re-creating
   146  it.
   147  `,
   148  	})
   149  }
   150  
   151  // Clear the fs cache
   152  func rcCacheClear(ctx context.Context, in Params) (out Params, err error) {
   153  	cache.Clear()
   154  	return nil, nil
   155  }
   156  
   157  func init() {
   158  	Add(Call{
   159  		Path:         "fscache/entries",
   160  		Fn:           rcCacheEntries,
   161  		Title:        "Returns the number of entries in the fs cache.",
   162  		AuthRequired: true,
   163  		Help: `
   164  This returns the number of entries in the fs cache.
   165  
   166  Returns
   167  - entries - number of items in the cache
   168  `,
   169  	})
   170  }
   171  
   172  // Return the Entries the fs cache
   173  func rcCacheEntries(ctx context.Context, in Params) (out Params, err error) {
   174  	return Params{
   175  		"entries": cache.Entries(),
   176  	}, nil
   177  }