File: importpath.go

package info (click to toggle)
golang-github-cue-lang-cue 0.14.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,644 kB
  • sloc: makefile: 20; sh: 15
file content (148 lines) | stat: -rw-r--r-- 4,502 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package ast

import "strings"

// ParseImportPath returns the various components of an import path.
// It does not check the result for validity.
func ParseImportPath(p string) ImportPath {
	var parts ImportPath
	pathWithoutQualifier := p
	if i := strings.LastIndexAny(p, "/:"); i >= 0 && p[i] == ':' {
		pathWithoutQualifier = p[:i]
		parts.Qualifier = p[i+1:]
		parts.ExplicitQualifier = true
	}
	parts.Path = pathWithoutQualifier
	if path, version, ok := SplitPackageVersion(pathWithoutQualifier); ok {
		parts.Version = version
		parts.Path = path
	}
	if !parts.ExplicitQualifier {
		parts.Qualifier = impliedQualifier(parts.Path)
	}
	return parts
}

// ImportPath holds the various components of an import path.
type ImportPath struct {
	// Path holds the base package/directory path, similar
	// to that returned by [Version.BasePath].
	Path string

	// Version holds the version of the import
	// or empty if not present. Note: in general this
	// will contain a major version only, but there's no
	// guarantee of that.
	Version string

	// Qualifier holds the package qualifier within the path.
	// This will be derived from the last component of Path
	// if it wasn't explicitly present in the import path.
	// This is not guaranteed to be a valid CUE identifier.
	Qualifier string

	// ExplicitQualifier holds whether the qualifier will
	// always be added regardless of whether it matches
	// the final path element.
	ExplicitQualifier bool
}

// Canonical returns the canonical form of the import path.
// Specifically, it will only include the package qualifier
// if it's different from the last component of parts.Path.
//
// It also ensures that the Qualifier field is set when
// appropriate.
func (parts ImportPath) Canonical() ImportPath {
	q := impliedQualifier(parts.Path)
	if q == "" {
		parts.ExplicitQualifier = parts.Qualifier != ""
		return parts
	}
	if q == parts.Qualifier {
		// The qualifier matches the implied qualifier, so ensure that
		// it is not included in string representations.
		parts.ExplicitQualifier = false
	} else if parts.Qualifier == "" && !parts.ExplicitQualifier {
		// There's an implied qualifier but none set; this
		// could happen if someone has manually constructed the
		// ImportPath instance (it should never happen otherwise),
		// so be defensive and set the qualifier anyway.
		parts.Qualifier = q
		parts.ExplicitQualifier = false
	} else {
		// There's a qualifier set that does not match the implied
		// qualifier. This must be explicit.
		parts.ExplicitQualifier = true
	}
	return parts
}

// Unqualified returns the import path without any package qualifier.
func (parts ImportPath) Unqualified() ImportPath {
	parts.Qualifier = ""
	parts.ExplicitQualifier = false
	return parts
}

func (parts ImportPath) String() string {
	needQualifier := parts.ExplicitQualifier
	if !needQualifier && parts.Qualifier != "" {
		if impliedQualifier(parts.Path) != parts.Qualifier {
			needQualifier = true
		}
	}
	if parts.Version == "" && !needQualifier {
		// Fast path.
		return parts.Path
	}
	var buf strings.Builder
	buf.WriteString(parts.Path)
	if parts.Version != "" {
		buf.WriteByte('@')
		buf.WriteString(parts.Version)
	}
	if needQualifier {
		buf.WriteByte(':')
		buf.WriteString(parts.Qualifier)
	}
	return buf.String()
}

// impliedQualifier returns the package qualifier implied
// from the last component of the (bare) package path.
func impliedQualifier(path string) string {
	var q string
	if i := strings.LastIndex(path, "/"); i >= 0 {
		q = path[i+1:]
	} else {
		q = path
	}
	if !IsValidIdent(q) || strings.HasPrefix(q, "#") || q == "_" {
		return ""
	}
	return q
}

// SplitPackageVersion returns a prefix and version suffix such that
// prefix+"@"+version == path.
//
// SplitPackageVersion returns (path, "", false) when there is no `@`
// character splitting the path or if the version is empty.
//
// It does not check that the version is valid in any way other than
// checking that it is not empty.
//
// For example:
//
// SplitPackageVersion("foo.com/bar@v0.1") returns ("foo.com/bar", "v0.1", true).
// SplitPackageVersion("foo.com/bar@badvers") returns ("foo.com/bar", "badvers", true).
// SplitPackageVersion("foo.com/bar") returns ("foo.com/bar", "", false).
// SplitPackageVersion("foo.com/bar@") returns ("foo.com/bar@", "", false).
func SplitPackageVersion(path string) (prefix, version string, ok bool) {
	prefix, vers, ok := strings.Cut(path, "@")
	if vers == "" {
		ok = false
	}
	return prefix, vers, ok
}