Skip to content

Commit

Permalink
mod/modfile: add custom data field
Browse files Browse the repository at this point in the history
This provides a place to put data that users might currently
be storing at the top level of a module.cue file.

We define a two level map to make it easy for people to "do the
right thing" and avoid namespace conflicts and/or ambiguities
between different tools using the same space. The top level
defines the namespace for a set of custom data; the next
level defines attributes within that namespace. This approach
also makes it easier to define validation schemas for
custom data.

The "legacy" namespace is defined as a place to put existing
top level attributes so that there's a default place for an automated
migration tool to put them.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I2ceb5161406bce584e3fd0e947adb43233f2fdce
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1194670
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
  • Loading branch information
rogpeppe committed May 15, 2024
1 parent 21a5c8b commit a8693a0
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 12 deletions.
9 changes: 5 additions & 4 deletions mod/modfile/modfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ const schemaFile = "cuelang.org/go/mod/modfile/schema.cue"

// File represents the contents of a cue.mod/module.cue file.
type File struct {
Module string `json:"module"`
Language *Language `json:"language,omitempty"`
Source *Source `json:"source,omitempty"`
Deps map[string]*Dep `json:"deps,omitempty"`
Module string `json:"module"`
Language *Language `json:"language,omitempty"`
Source *Source `json:"source,omitempty"`
Deps map[string]*Dep `json:"deps,omitempty"`
Custom map[string]map[string]any `json:"custom,omitempty"`
versions []module.Version
// defaultMajorVersions maps from module base path to the
// major version default for that path.
Expand Down
45 changes: 37 additions & 8 deletions mod/modfile/modfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ deps: "example.com": v: "v1.2.3"
data: `
module: "foo.com/bar"
something: 4
cue: lang: "xxx"
language: version: "xxx"
`,
want: &File{
Module: "foo.com/bar",
Expand All @@ -296,32 +296,52 @@ _foo: "blah.example"
data: `
module: "foo.com/bar"
_foo: "v0.9.0"
cue: lang: _foo
language: version: _foo
`,
wantError: `invalid module.cue file syntax: references not allowed in data mode:
module.cue:4:12`,
module.cue:4:20`,
}, {
testName: "ReferencesNotAllowed#2",
parse: Parse,
data: `
module: "foo.com/bar"
let foo = "v0.9.0"
cue: lang: foo
language: version: foo
`,
wantError: `invalid module.cue file syntax: references not allowed in data mode:
module.cue:3:1
invalid module.cue file syntax: references not allowed in data mode:
module.cue:4:12`,
module.cue:4:20`,
}, {
testName: "DefinitionsNotAllowed",
parse: Parse,
data: `
module: "foo.com/bar"
#x: "v0.9.0"
cue: lang: "v0.9.0"
language: version: "v0.9.0"
`,
wantError: `invalid module.cue file syntax: definitions not allowed in data mode:
module.cue:3:1`,
}, {
testName: "CustomData",
parse: Parse,
data: `
module: "foo.com/bar@v0"
language: version: "v0.9.0"
custom: "somewhere.com": foo: true
`,
want: &File{
Module: "foo.com/bar@v0",
Language: &Language{Version: "v0.9.0"},
Custom: map[string]map[string]any{
"somewhere.com": {
"foo": true,
},
},
},
wantDefaults: map[string]string{
"foo.com/bar": "v0",
},
}}

func TestParse(t *testing.T) {
Expand All @@ -334,7 +354,7 @@ func TestParse(t *testing.T) {
return
}
qt.Assert(t, qt.IsNil(err), qt.Commentf("details: %v", strings.TrimSuffix(errors.Details(err, nil), "\n")))
qt.Assert(t, qt.CmpEquals(f, test.want, cmpopts.IgnoreUnexported(File{})))
qt.Assert(t, fileEquals(f, test.want))
qt.Assert(t, qt.DeepEquals(f.DepVersions(), test.wantVersions))
qt.Assert(t, qt.DeepEquals(f.DefaultMajorVersions(), test.wantDefaults))
})
Expand Down Expand Up @@ -433,7 +453,7 @@ language: {
// Check that it round-trips.
f, err := Parse(data, "")
qt.Assert(t, qt.IsNil(err))
qt.Assert(t, qt.CmpEquals(f, test.file, cmpopts.IgnoreUnexported(File{}), cmpopts.EquateEmpty()))
qt.Assert(t, fileEquals(f, test.file))
})
}

Expand All @@ -452,3 +472,12 @@ func parseVersions(vs ...string) []module.Version {
}
return vvs
}

// fileEquals returns a checker that checks whether two File instances
// are equal.
func fileEquals(got, want *File) qt.Checker {
return qt.CmpEquals(got, want,
cmpopts.IgnoreUnexported(File{}),
cmpopts.EquateEmpty(),
)
}
7 changes: 7 additions & 0 deletions mod/modfile/schema.cue
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ versions: "v0.9.0-alpha.0": {
// deps holds dependency information for modules, keyed by module path.
deps?: [#Module]: #Dep

// custom holds arbitrary data intended for use by
// third-party tools. Each field at the top level
// represents a tooling namespace, conventionally
// a module or domain name. Data migrated from legacy
// module.cue files is placed in the "legacy" namespace.
custom?: [#Module | "legacy"]: [_]: _

#Dep: {
// v indicates the minimum required version of the module.
// This can be null if the version is unknown and the module
Expand Down

0 comments on commit a8693a0

Please sign in to comment.