1package getter
2
3import (
4	"fmt"
5	"path/filepath"
6
7	"github.com/hashicorp/go-getter/helper/url"
8)
9
10// Detector defines the interface that an invalid URL or a URL with a blank
11// scheme is passed through in order to determine if its shorthand for
12// something else well-known.
13type Detector interface {
14	// Detect will detect whether the string matches a known pattern to
15	// turn it into a proper URL.
16	Detect(string, string) (string, bool, error)
17}
18
19// Detectors is the list of detectors that are tried on an invalid URL.
20// This is also the order they're tried (index 0 is first).
21var Detectors []Detector
22
23func init() {
24	Detectors = []Detector{
25		new(GitHubDetector),
26		new(GitLabDetector),
27		new(GitDetector),
28		new(BitBucketDetector),
29		new(S3Detector),
30		new(GCSDetector),
31		new(FileDetector),
32	}
33}
34
35// Detect turns a source string into another source string if it is
36// detected to be of a known pattern.
37//
38// The third parameter should be the list of detectors to use in the
39// order to try them. If you don't want to configure this, just use
40// the global Detectors variable.
41//
42// This is safe to be called with an already valid source string: Detect
43// will just return it.
44func Detect(src string, pwd string, ds []Detector) (string, error) {
45	getForce, getSrc := getForcedGetter(src)
46
47	// Separate out the subdir if there is one, we don't pass that to detect
48	getSrc, subDir := SourceDirSubdir(getSrc)
49
50	u, err := url.Parse(getSrc)
51	if err == nil && u.Scheme != "" {
52		// Valid URL
53		return src, nil
54	}
55
56	for _, d := range ds {
57		result, ok, err := d.Detect(getSrc, pwd)
58		if err != nil {
59			return "", err
60		}
61		if !ok {
62			continue
63		}
64
65		var detectForce string
66		detectForce, result = getForcedGetter(result)
67		result, detectSubdir := SourceDirSubdir(result)
68
69		// If we have a subdir from the detection, then prepend it to our
70		// requested subdir.
71		if detectSubdir != "" {
72			if subDir != "" {
73				subDir = filepath.Join(detectSubdir, subDir)
74			} else {
75				subDir = detectSubdir
76			}
77		}
78
79		if subDir != "" {
80			u, err := url.Parse(result)
81			if err != nil {
82				return "", fmt.Errorf("Error parsing URL: %s", err)
83			}
84			u.Path += "//" + subDir
85
86			// a subdir may contain wildcards, but in order to support them we
87			// have to ensure the path isn't escaped.
88			u.RawPath = u.Path
89
90			result = u.String()
91		}
92
93		// Preserve the forced getter if it exists. We try to use the
94		// original set force first, followed by any force set by the
95		// detector.
96		if getForce != "" {
97			result = fmt.Sprintf("%s::%s", getForce, result)
98		} else if detectForce != "" {
99			result = fmt.Sprintf("%s::%s", detectForce, result)
100		}
101
102		return result, nil
103	}
104
105	return "", fmt.Errorf("invalid source string: %s", src)
106}
107