github.com/benchkram/bob@v0.0.0-20240314204020-b7a57f2f9be9/bobgit/pathspec/pathspec.go (about)

     1  package pathspec
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/benchkram/bob/pkg/filepathutil"
     8  )
     9  
    10  // map the repo as key and pathspec as value
    11  //
    12  // where repository is relative to the bobroot
    13  // and pathspec is relative to the repository.
    14  type RepoPathspecMap map[string]string
    15  
    16  var ErrRepoNotFound = fmt.Errorf("Repository name not found in target path repository list")
    17  
    18  // P, stores the pathspec attribute for git
    19  // intended to be used for the multi repo case.
    20  type P struct {
    21  	path          string
    22  	possibleRepos RepoPathspecMap
    23  }
    24  
    25  func New(path string) *P {
    26  	at := &P{
    27  		path:          path,
    28  		possibleRepos: ComputePossibleRepos(path),
    29  	}
    30  
    31  	return at
    32  }
    33  
    34  // SelectReposByPath filter the repos from all the repository list
    35  // by the pathspec where git add command will be executed.
    36  //
    37  // Select all repos in case of target "." and set pathspecc
    38  // to "." for all repos.
    39  func (p *P) SelectReposByPath(repolist []string) []string {
    40  	// return all the possible repos in case of
    41  	// all `.`
    42  	if p.path == "." || p.path == "./" {
    43  		for _, repo := range repolist {
    44  			if repo != "." {
    45  				p.possibleRepos[repo] = "."
    46  			}
    47  		}
    48  		return repolist
    49  	}
    50  
    51  	filterd := []string{}
    52  	for _, repo := range repolist {
    53  		for r := range p.possibleRepos {
    54  			if repo == r {
    55  				filterd = append(filterd, repo)
    56  			}
    57  		}
    58  	}
    59  
    60  	// get filtered repos from the target path
    61  	filterd = removeParentRepos(filterd)
    62  
    63  	// add all possible repositories that situated
    64  	// in the target directory if path ends with `.`
    65  	if strings.Contains(p.path, "/.") {
    66  		for _, repo := range repolist {
    67  			if !contains(filterd, repo) {
    68  				temptarget := strings.Trim(p.path, ".")
    69  				if strings.HasPrefix(repo, temptarget) {
    70  					filterd = append(filterd, repo)
    71  					p.possibleRepos[repo] = "."
    72  				}
    73  			}
    74  		}
    75  	}
    76  
    77  	return filterd
    78  }
    79  
    80  // GetRelativePathspec returns the relative pathspec from the internal map.
    81  func (p *P) GetRelativePathspec(reponame string) (string, error) {
    82  	if val, ok := p.possibleRepos[reponame]; ok {
    83  		return val, nil
    84  	}
    85  	return "", ErrRepoNotFound
    86  }
    87  
    88  // ComputePossibleRepos Compute all the possible repository path
    89  // from the provided path starting from bobroot inside bob workspace
    90  // and returns a map of string  where key is repository path and value is
    91  // the relative path from that repository.
    92  //
    93  // repositories can be filtered later from the computed repository paths.
    94  //
    95  // Example:
    96  //
    97  // 'bobroot/sample/path' => {".": 'bobroot/sample/path',
    98  // "bobroot": 'sample/path', "bobroot/sample": 'path', ..}
    99  //
   100  // can be interpreted this way, if the selected repository path is `bobroot/sample`,
   101  // then pathspec for that target path would be only `path`, and so on.
   102  func ComputePossibleRepos(path string) RepoPathspecMap {
   103  
   104  	possibleRepos := make(RepoPathspecMap)
   105  	possibleRepos["."] = path
   106  
   107  	if path == "." {
   108  		return possibleRepos
   109  	}
   110  
   111  	splitted := strings.Split(path, "/")
   112  
   113  	for i := 0; i < len(splitted)-1; i++ {
   114  		repo := strings.Join(splitted[:i+1], "/")
   115  		target := strings.Join(splitted[i+1:], "/")
   116  		possibleRepos[repo] = target
   117  	}
   118  	possibleRepos[strings.Join(splitted, "/")] = "."
   119  
   120  	return possibleRepos
   121  }
   122  
   123  // removeParentRepos removes the parent repository path from each
   124  // repo in `repolist`.  Keeping only the child repository path.
   125  func removeParentRepos(repolist []string) []string {
   126  	filtered := []string{}
   127  	for _, repo := range repolist {
   128  		hasChild := false
   129  		for _, r := range repolist {
   130  			if r != repo {
   131  				hasChild = filepathutil.IsChild(repo, r)
   132  			}
   133  		}
   134  		if !hasChild {
   135  			filtered = append(filtered, repo)
   136  		}
   137  	}
   138  	return filtered
   139  }
   140  
   141  // contains returns true when e is contained in s
   142  func contains(s []string, e string) bool {
   143  	for _, a := range s {
   144  		if a == e {
   145  			return true
   146  		}
   147  	}
   148  	return false
   149  }