github.com/swaros/contxt/module/runner@v0.0.0-20240305083542-3dbd4436ac40/dirfind.go (about)

     1  // Copyright (c) 2023 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved.
     2  //
     3  // Licensed under the MIT License
     4  //
     5  //
     6  // Permission is hereby granted, free of charge, to any person obtaining a copy
     7  // of this software and associated documentation files (the "Software"), to deal
     8  // in the Software without restriction, including without limitation the rights
     9  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    10  // copies of the Software, and to permit persons to whom the Software is
    11  // furnished to do so, subject to the following conditions:
    12  //
    13  // The above copyright notice and this permission notice shall be included in all
    14  // copies or substantial portions of the Software.
    15  //
    16  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22  // SOFTWARE.
    23  
    24  // Package to decide what path is ment by some inputs, and also
    25  // creates a label for them, if not set
    26  package runner
    27  
    28  import (
    29  	"errors"
    30  	"sort"
    31  	"strings"
    32  
    33  	"github.com/sirupsen/logrus"
    34  	"github.com/swaros/contxt/module/configure"
    35  	"github.com/swaros/contxt/module/systools"
    36  )
    37  
    38  func (c *CmdExecutorImpl) DirFindApplyAndSave(args []string) (string, error) {
    39  	dir := c.DirFind(args)
    40  	if dir != "" && dir != "." {
    41  		index, ok := c.FindIndexByPath(dir)
    42  		if !ok {
    43  			return dir, errors.New("path not found in configuration")
    44  		}
    45  		if err := configure.GetGlobalConfig().SetCurrentPathIndex(index); err != nil {
    46  			return dir, err
    47  		}
    48  		return dir, configure.GetGlobalConfig().SaveConfiguration()
    49  	}
    50  	return dir, nil // just no or the same path reported
    51  }
    52  
    53  func (c *CmdExecutorImpl) FindIndexByPath(path string) (index string, ok bool) {
    54  	indexFound := ""
    55  	configure.GetGlobalConfig().PathWorkerNoCd(func(index string, p string) {
    56  		if p == path {
    57  			ok = true
    58  			indexFound = index
    59  		}
    60  	})
    61  	return indexFound, ok
    62  }
    63  
    64  // DirFind returns the best matching part of depending the arguments, what of the stored paths
    65  // would be the expected one
    66  func (c *CmdExecutorImpl) DirFind(args []string) string {
    67  	// no arguments? then we report the current dir
    68  	if len(args) < 1 {
    69  		return "."
    70  	}
    71  
    72  	paths := []string{}
    73  	indexMatchMap := map[string]string{}
    74  
    75  	configure.GetGlobalConfig().PathWorkerNoCd(func(index string, path string) {
    76  		paths = append(paths, path)
    77  		indexMatchMap[index] = path
    78  	})
    79  
    80  	if iPath, ok := indexMatchMap[args[0]]; ok {
    81  		c.GetLogger().Debug("Found match by index:", iPath)
    82  		return iPath
    83  	}
    84  
    85  	if p, ok := c.DecidePath(args, paths); ok {
    86  		fields := logrus.Fields{"path": p}
    87  		c.GetLogger().Debug("Found match by comparing strings", fields)
    88  		return p
    89  	}
    90  	return "."
    91  
    92  }
    93  
    94  func (c *CmdExecutorImpl) DecidePath(searchWords, paths []string) (path string, found bool) {
    95  	usePath := "."
    96  	foundSome := false
    97  	// starts iterate paths
    98  	for _, search := range searchWords {
    99  		if possiblePaths, ok := c.filterStringList(paths, search); ok {
   100  			// keep the best match from the current list
   101  			usePath = c.findBestByLast(possiblePaths, search)
   102  			// now we continue with the filtered result
   103  			paths = possiblePaths
   104  			// for sure we found something
   105  			foundSome = true
   106  		} else {
   107  			// nothing found by filtering. we could be already in a followup. so return the current findings
   108  			return usePath, foundSome
   109  		}
   110  
   111  	}
   112  
   113  	return usePath, foundSome
   114  }
   115  
   116  func (c *CmdExecutorImpl) filterStringList(paths []string, byWord string) (result []string, ok bool) {
   117  	possiblePaths := []string{}
   118  	found := false
   119  	for _, path := range paths {
   120  		// first we add any path to possible paths they have  at least one matching part
   121  		if strings.Contains(path, byWord) {
   122  			possiblePaths = append(possiblePaths, path)
   123  			found = true
   124  		}
   125  	}
   126  	sort.Strings(possiblePaths)
   127  	return possiblePaths, found
   128  }
   129  
   130  func (c *CmdExecutorImpl) findBestByLast(paths []string, byWord string) string {
   131  	match := ""
   132  	bestIndex := -1
   133  	bestlen := -1 // best length. we always go for the shortest
   134  	for _, path := range paths {
   135  		// first we add any path to possible paths they have  at least one matching part
   136  		if strings.Contains(path, byWord) {
   137  			if i := strings.Index(path, byWord); i > bestIndex {
   138  				match = path
   139  				bestIndex = i
   140  				bestlen = systools.StrLen(path)
   141  			} else if i == bestIndex && systools.StrLen(path) < bestlen { // on same position we use the shorter path
   142  				match = path
   143  				bestIndex = i
   144  				bestlen = systools.StrLen(path)
   145  			}
   146  		}
   147  	}
   148  	return match
   149  }