1package gomod
2
3import (
4	"path"
5	"strings"
6)
7
8// IsModuleChanged returns whether the given set of changes applies to the module.
9// The submodules argument is must be lexicographically sorted list of submodule locations that are located
10// under moduleDir.
11func IsModuleChanged(moduleDir string, submodules []string, changes []string) (bool, error) {
12	if moduleDir == "." {
13		moduleDir = ""
14	} else {
15		// Append the path separator to ensure it is used in prefix matches. This ensure that we are looking
16		// at this specific directory and children rather then any directory that has this moduleDir prefix.
17		moduleDir += "/"
18	}
19
20	isChildPathCache := make(map[string]bool)
21
22	hasChanges := false
23
24	for i := 0; i < len(changes) && !hasChanges; i++ {
25		dir, fileName := path.Split(changes[i])
26
27		if len(dir) == 0 && moduleDir != "" {
28			continue
29		}
30
31		if len(moduleDir) > 0 && !strings.HasPrefix(dir, moduleDir) {
32			continue
33		}
34
35		if len(dir) == 0 && (IsGoSource(fileName) || IsGoMod(fileName)) {
36			hasChanges = true
37			continue
38		} else if !(IsGoSource(fileName) || IsGoMod(fileName)) {
39			continue
40		}
41		dir = path.Clean(dir)
42
43		if len(submodules) == 0 {
44			hasChanges = true
45			continue
46		}
47
48		if isChild, ok := isChildPathCache[dir]; !ok {
49			if IsSubmodulePath(dir, submodules) {
50				isChildPathCache[dir] = true
51			} else {
52				isChildPathCache[dir] = false
53				hasChanges = true
54			}
55		} else if !isChild {
56			hasChanges = true
57		}
58	}
59
60	return hasChanges, nil
61}
62
63// IsGoSource returns whether a given file name is a Go source code file ending in `.go`
64func IsGoSource(name string) bool {
65	return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
66}
67
68// IsGoMod returns whether a given file name is `go.mod`.
69func IsGoMod(name string) bool {
70	return name == "go.mod"
71}
72