github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/cd/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/lmorg/murex/lang"
    13  	"github.com/lmorg/murex/lang/types"
    14  	"github.com/lmorg/murex/utils/consts"
    15  )
    16  
    17  type cachedWalkT struct {
    18  	Path     string
    19  	FileInfo os.FileInfo
    20  }
    21  
    22  var (
    23  	cancel     = func() {}
    24  	cachedWalk map[string][]cachedWalkT
    25  	lastScan   map[string]time.Time
    26  	mutex      sync.Mutex
    27  )
    28  
    29  func init() {
    30  	cachedWalk = make(map[string][]cachedWalkT)
    31  	lastScan = make(map[string]time.Time)
    32  
    33  	go garbageCollection()
    34  }
    35  
    36  func garbageCollection() {
    37  	for {
    38  		time.Sleep(time.Duration(gcSleep) * time.Second)
    39  
    40  		mutex.Lock()
    41  		for s := range lastScan {
    42  			if lastScan[s].Add(time.Duration(cacheTimeout) * time.Second).Before(time.Now()) {
    43  				delete(lastScan, s)
    44  				delete(cachedWalk, s)
    45  			}
    46  		}
    47  		mutex.Unlock()
    48  	}
    49  }
    50  
    51  func cleanPath(pwd string) string {
    52  	if len(pwd) == 0 {
    53  		return ""
    54  	}
    55  
    56  	if pwd[0] != '/' {
    57  		wd, err := os.Getwd()
    58  		if err == nil {
    59  			pwd = wd + "/" + pwd
    60  		}
    61  	}
    62  	return path.Clean(pwd)
    63  }
    64  
    65  func GatherFileCompletions(pwd string) {
    66  	pwd = cleanPath(pwd)
    67  
    68  	if len(pwd) == 0 {
    69  		return
    70  	}
    71  
    72  	mutex.Lock()
    73  	cancel()
    74  
    75  	if lastScan[pwd].Add(time.Duration(cacheTimeout) * time.Second).After(time.Now()) {
    76  		mutex.Unlock()
    77  		return
    78  	}
    79  
    80  	var ctx context.Context
    81  
    82  	maxDepth, err := lang.ShellProcess.Config.Get("shell", "recursive-max-depth", types.Integer)
    83  	if err != nil {
    84  		maxDepth = 0 // This should only crop up in testing
    85  	}
    86  
    87  	ctx, cancel = context.WithTimeout(context.Background(), time.Duration(walkTimeout)*time.Second)
    88  	mutex.Unlock()
    89  
    90  	currentDepth := len(strings.Split(pwd, consts.PathSlash))
    91  
    92  	var (
    93  		cw []cachedWalkT
    94  		//m  sync.Mutex
    95  	)
    96  
    97  	walker := func(walkedPath string, info os.FileInfo, err error) error {
    98  		select {
    99  		case <-ctx.Done():
   100  			return ctx.Err()
   101  		default:
   102  		}
   103  
   104  		if err != nil {
   105  			return nil
   106  		}
   107  
   108  		dirs := strings.Split(walkedPath, consts.PathSlash)
   109  
   110  		if len(dirs)-currentDepth > maxDepth.(int) {
   111  			return filepath.SkipDir
   112  		}
   113  
   114  		/*if len(dirs) != 0 && len(dirs[len(dirs)-1]) == 0 {
   115  			return nil
   116  		}*/
   117  
   118  		//m.Lock()
   119  		cw = append(cw, cachedWalkT{walkedPath, info})
   120  		//m.Unlock()
   121  
   122  		return nil
   123  	}
   124  
   125  	filepath.Walk(pwd, walker)
   126  
   127  	mutex.Lock()
   128  	cachedWalk[pwd] = cw
   129  	lastScan[pwd] = time.Now()
   130  	mutex.Unlock()
   131  }
   132  
   133  func WalkCompletions(pwd string, walker filepath.WalkFunc) bool {
   134  	pwd = cleanPath(pwd)
   135  
   136  	if len(pwd) == 0 {
   137  		return false
   138  	}
   139  
   140  	mutex.Lock()
   141  
   142  	if lastScan[pwd].Add(time.Duration(cacheTimeout) * time.Second).Before(time.Now()) {
   143  		mutex.Unlock()
   144  		return false
   145  	}
   146  
   147  	cw := cachedWalk[pwd]
   148  	mutex.Unlock()
   149  
   150  	var err error
   151  	for _, file := range cw {
   152  		err = walker(file.Path, file.FileInfo, nil)
   153  		if err != nil {
   154  			return false
   155  		}
   156  	}
   157  
   158  	return true
   159  }
   160  
   161  func DumpCompletions() interface{} {
   162  	type dumpT struct {
   163  		Walk     []cachedWalkT
   164  		LastScan string
   165  	}
   166  
   167  	dump := make(map[string]dumpT)
   168  
   169  	mutex.Lock()
   170  	for s := range cachedWalk {
   171  		walk := make([]cachedWalkT, len(cachedWalk[s]))
   172  		copy(walk, cachedWalk[s])
   173  		dump[s] = dumpT{
   174  			Walk:     walk,
   175  			LastScan: lastScan[s].String(),
   176  		}
   177  	}
   178  	mutex.Unlock()
   179  
   180  	return dump
   181  }