Skip to content

Commit b684a52

Browse files
committed
mod/modfile: add ModuleForImportPath
This makes it straightforward to resolve modules and default major versions for packages without consulting a registry or invoking any of the code inside internal/mod. This is valid to do for packages that are part of the current dependency graph as long as the module is tidy, because the tidy algorithm guarantees that all dependency modules are present in the module.cue file. Signed-off-by: Roger Peppe <[email protected]> Change-Id: Ia0d86b335e2c1f2963e75f7901322fb55a842643 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1207985 Reviewed-by: Daniel Martí <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent c269767 commit b684a52

File tree

2 files changed

+109
-17
lines changed

2 files changed

+109
-17
lines changed

mod/modfile/modfile.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package modfile
2222
import (
2323
_ "embed"
2424
"fmt"
25+
"path"
2526
"slices"
2627
"strings"
2728
"sync"
@@ -53,12 +54,13 @@ type File struct {
5354
// Use the [File.QualifiedModule] method to obtain a module
5455
// path that's always qualified. See also the
5556
// [File.ModulePath] and [File.MajorVersion] methods.
56-
Module string `json:"module"`
57-
Language *Language `json:"language,omitempty"`
58-
Source *Source `json:"source,omitempty"`
59-
Deps map[string]*Dep `json:"deps,omitempty"`
60-
Custom map[string]map[string]any `json:"custom,omitempty"`
61-
versions []module.Version
57+
Module string `json:"module"`
58+
Language *Language `json:"language,omitempty"`
59+
Source *Source `json:"source,omitempty"`
60+
Deps map[string]*Dep `json:"deps,omitempty"`
61+
Custom map[string]map[string]any `json:"custom,omitempty"`
62+
versions []module.Version
63+
versionByModule map[string]module.Version
6264
// defaultMajorVersions maps from module base path to the
6365
// major version default for that path.
6466
defaultMajorVersions map[string]string
@@ -463,6 +465,7 @@ func parse(modfile []byte, filename string, strict bool) (*File, error) {
463465
return nil, fmt.Errorf("language version %v in %s is not canonical", vers, filename)
464466
}
465467
}
468+
mf.versionByModule = make(map[string]module.Version)
466469
var versions []module.Version
467470
defaultMajorVersions := make(map[string]string)
468471
if mainPath != "" {
@@ -486,8 +489,17 @@ func parse(modfile []byte, filename string, strict bool) (*File, error) {
486489
}
487490
defaultMajorVersions[mp] = semver.Major(vers.Version())
488491
}
492+
mf.versionByModule[vers.Path()] = vers
493+
}
494+
if mainPath != "" {
495+
// We don't necessarily have a full version for the main module.
496+
mainWithMajor := mainPath + "@" + mainMajor
497+
mainVersion, err := module.NewVersion(mainWithMajor, "")
498+
if err != nil {
499+
return nil, err
500+
}
501+
mf.versionByModule[mainWithMajor] = mainVersion
489502
}
490-
491503
if len(defaultMajorVersions) > 0 {
492504
mf.defaultMajorVersions = defaultMajorVersions
493505
}
@@ -582,3 +594,27 @@ func (f *File) DepVersions() []module.Version {
582594
func (f *File) DefaultMajorVersions() map[string]string {
583595
return f.defaultMajorVersions
584596
}
597+
598+
// ModuleForImportPath returns the module that should contain the given
599+
// import path and reports whether the module was found.
600+
// It does not check to see if the import path actually exists within the module.
601+
//
602+
// It works entirely from information in f, meaning that it does
603+
// not consult a registry to resolve a package whose module is not
604+
// mentioned in the file, which means it will not work in general unless
605+
// the module is tidy (as with `cue mod tidy`).
606+
func (f *File) ModuleForImportPath(importPath string) (module.Version, bool) {
607+
ip := module.ParseImportPath(importPath)
608+
for prefix := ip.Path; prefix != "."; prefix = path.Dir(prefix) {
609+
pkgVersion := ip.Version
610+
if pkgVersion == "" {
611+
if pkgVersion = f.defaultMajorVersions[prefix]; pkgVersion == "" {
612+
continue
613+
}
614+
}
615+
if mv, ok := f.versionByModule[prefix+"@"+pkgVersion]; ok {
616+
return mv, true
617+
}
618+
}
619+
return module.Version{}, false
620+
}

mod/modfile/modfile_test.go

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ import (
2727
)
2828

2929
var parseTests = []struct {
30-
testName string
31-
parse func(modfile []byte, filename string) (*File, error)
32-
data string
33-
wantError string
34-
want *File
35-
wantVersions []module.Version
36-
wantDefaults map[string]string
30+
testName string
31+
parse func(modfile []byte, filename string) (*File, error)
32+
data string
33+
wantError string
34+
want *File
35+
wantVersions []module.Version
36+
wantDefaults map[string]string
37+
wantModVersionForPkg map[string]string
3738
}{{
3839
testName: "NoDeps",
3940
parse: Parse,
@@ -50,6 +51,15 @@ language: version: "v0.8.0-alpha.0"
5051
wantDefaults: map[string]string{
5152
"foo.com/bar": "v0",
5253
},
54+
wantModVersionForPkg: map[string]string{
55+
"foo.com/bar": "foo.com/bar@v0",
56+
"foo.com/bar@v0": "foo.com/bar@v0",
57+
"foo.com/bar/baz@v0": "foo.com/bar@v0",
58+
"foo.com/bar@v1": "",
59+
"foo.com/bar:hello": "foo.com/bar@v0",
60+
"foo.com/bar/baz:hello": "foo.com/bar@v0",
61+
"foo.com/bar/baz@v0:hello": "foo.com/bar@v0",
62+
},
5363
}, {
5464
testName: "WithDeps",
5565
parse: Parse,
@@ -60,6 +70,11 @@ deps: "example.com@v1": {
6070
default: true
6171
v: "v1.2.3"
6272
}
73+
deps: "example.com/other@v1": v: "v1.9.10"
74+
deps: "example.com/other/more/nested@v2": {
75+
v: "v2.9.20"
76+
default: true
77+
}
6378
deps: "other.com/something@v0": v: "v0.2.3"
6479
`,
6580
want: &File{
@@ -75,12 +90,36 @@ deps: "other.com/something@v0": v: "v0.2.3"
7590
"other.com/something@v0": {
7691
Version: "v0.2.3",
7792
},
93+
"example.com/other@v1": {
94+
Version: "v1.9.10",
95+
},
96+
"example.com/other/more/nested@v2": {
97+
Version: "v2.9.20",
98+
Default: true,
99+
},
78100
},
79101
},
80-
wantVersions: parseVersions("[email protected]", "other.com/[email protected]"),
102+
wantVersions: parseVersions(
103+
"example.com/other/more/[email protected]",
104+
"example.com/[email protected]",
105+
106+
"other.com/[email protected]",
107+
),
81108
wantDefaults: map[string]string{
82-
"foo.com/bar": "v0",
83-
"example.com": "v1",
109+
"example.com/other/more/nested": "v2",
110+
"foo.com/bar": "v0",
111+
"example.com": "v1",
112+
},
113+
wantModVersionForPkg: map[string]string{
114+
"example.com": "[email protected]",
115+
"example.com/x/y@v1": "[email protected]",
116+
"example.com/x/y@v1:x": "[email protected]",
117+
"example.com/other@v1": "example.com/[email protected]",
118+
"example.com/other/p@v1": "example.com/[email protected]",
119+
"example.com/other/more": "[email protected]",
120+
"example.com/other/more@v1": "example.com/[email protected]",
121+
"example.com/other/more/nested": "example.com/other/more/[email protected]",
122+
"example.com/other/more/nested/x:p": "example.com/other/more/[email protected]",
84123
},
85124
}, {
86125
testName: "WithSource",
@@ -270,6 +309,12 @@ deps: "example.com": v: "v1.2.3"
270309
wantDefaults: map[string]string{
271310
"foo.com/bar": "v0",
272311
},
312+
wantModVersionForPkg: map[string]string{
313+
"example.com": "", // No default major version.
314+
"example.com@v1": "[email protected]",
315+
"example.com/x/y@v1": "[email protected]",
316+
"example.com/x/y@v1:x": "[email protected]",
317+
},
273318
}, {
274319
testName: "LegacyWithExtraFields",
275320
parse: ParseLegacy,
@@ -445,6 +490,17 @@ func TestParse(t *testing.T) {
445490
qt.Assert(t, qt.Equals(f.ModulePath(), f.Module))
446491
qt.Assert(t, qt.Equals(f.MajorVersion(), "v0"))
447492
}
493+
for p, m := range test.wantModVersionForPkg {
494+
t.Run("package-"+p, func(t *testing.T) {
495+
mv, ok := f.ModuleForImportPath(p)
496+
if m == "" {
497+
qt.Assert(t, qt.IsFalse(ok), qt.Commentf("got version %v", mv))
498+
return
499+
}
500+
qt.Check(t, qt.IsTrue(ok))
501+
qt.Check(t, qt.Equals(mv.String(), m))
502+
})
503+
}
448504
})
449505
}
450506
}

0 commit comments

Comments
 (0)