Skip to content

Commit 7701a87

Browse files
committed
internal/filetypes: clarify FromFile
Currently it's not entirely clear which parts of the build.File are used by FromFile to feed into the resulting FileInfo. Make the logic clearer by unifying only the encoding, form, and interpretation fields, making it obvious that the tags do not play any role here, and this implying that the number of possible inputs are more limited than those to toFile. Also make it clear that the subsidiary tags are not expected to be an output of `FromFile` by inlining only those parts of `build.File` which are actually used. This does not affect behavior because nothing actually uses those fields. Also require the Encoding field, as documented, which requires some test changes, but this should have no overall effect because the file passed into FromFile has invariably come originally from toFile (e.g. via ParseArgs) and that will fill in the Encoding field. Thus none of the cmd/cue tests fail as a result. Also remove a TODO that seems unlikely to be possible to do, as the current mode _can_ influence various FileInfo fields, and that doesn't necessarily seem like a bad thing. Also, change the "handle common case" code to return the actual Form returned by the underlying logic that this is optimized from: when checking the "emulated" version for discrepancies in a subsequent CL, it became clear that this was the correct behavior. For #3280. Signed-off-by: Roger Peppe <[email protected]> Change-Id: Icbb54b544469002ea08710fa585177dada38c974 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1213873 Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Daniel Martí <[email protected]>
1 parent 8240e3c commit 7701a87

File tree

4 files changed

+289
-302
lines changed

4 files changed

+289
-302
lines changed

internal/filetypes/filetypes.go

Lines changed: 4 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ import (
1919
"path/filepath"
2020
"strconv"
2121
"strings"
22-
"sync"
2322

24-
"cuelang.org/go/cue"
2523
"cuelang.org/go/cue/build"
2624
"cuelang.org/go/cue/errors"
2725
"cuelang.org/go/cue/token"
@@ -53,7 +51,10 @@ func (m Mode) String() string {
5351

5452
// FileInfo defines the parsing plan for a file.
5553
type FileInfo struct {
56-
*build.File
54+
Filename string `json:"filename"`
55+
Encoding build.Encoding `json:"encoding,omitempty"`
56+
Interpretation build.Interpretation `json:"interpretation,omitempty"`
57+
Form build.Form `json:"form,omitempty"`
5758

5859
Definitions bool `json:"definitions"` // include/allow definition fields
5960
Data bool `json:"data"` // include/allow regular fields
@@ -69,88 +70,6 @@ type FileInfo struct {
6970
Attributes bool `json:"attributes"` // include/allow attributes
7071
}
7172

72-
// evalMu guards against concurrent execution of the CUE evaluator.
73-
// See issue https://cuelang.org/issue/2733
74-
var evalMu sync.Mutex
75-
76-
// TODO(mvdan): the funcs below make use of typesValue concurrently,
77-
// even though we clearly document that cue.Values are not safe for concurrent use.
78-
// It seems to be OK in practice, as otherwise we would run into `go test -race` failures.
79-
80-
// FromFile return detailed file info for a given build file.
81-
// Encoding must be specified.
82-
// TODO: mode should probably not be necessary here.
83-
func FromFile(b *build.File, mode Mode) (*FileInfo, error) {
84-
// Handle common case. This allows certain test cases to be analyzed in
85-
// isolation without interference from evaluating these files.
86-
if mode == Input &&
87-
b.Encoding == build.CUE &&
88-
b.Form == "" &&
89-
b.Interpretation == "" {
90-
return &FileInfo{
91-
File: b,
92-
93-
Definitions: true,
94-
Data: true,
95-
Optional: true,
96-
Constraints: true,
97-
References: true,
98-
Cycles: true,
99-
KeepDefaults: true,
100-
Incomplete: true,
101-
Imports: true,
102-
Docs: true,
103-
Attributes: true,
104-
}, nil
105-
}
106-
evalMu.Lock()
107-
defer evalMu.Unlock()
108-
typesInit()
109-
modeVal := lookup(typesValue, "modes", mode.String())
110-
fileVal := lookup(modeVal, "FileInfo")
111-
fileVal = fileVal.FillPath(cue.Path{}, b)
112-
113-
if b.Encoding == "" {
114-
ext := lookup(modeVal, "extensions", fileExt(b.Filename))
115-
if ext.Exists() {
116-
fileVal = fileVal.Unify(ext)
117-
}
118-
}
119-
var errs errors.Error
120-
121-
interpretation, _ := lookup(fileVal, "interpretation").String()
122-
if b.Form != "" {
123-
fileVal, errs = unifyWith(errs, fileVal, typesValue, "forms", string(b.Form))
124-
// may leave some encoding-dependent options open in data mode.
125-
} else if interpretation != "" {
126-
// always sets form=*schema
127-
fileVal, errs = unifyWith(errs, fileVal, typesValue, "interpretations", interpretation)
128-
}
129-
if interpretation == "" {
130-
s, err := lookup(fileVal, "encoding").String()
131-
if err != nil {
132-
return nil, err
133-
}
134-
fileVal, errs = unifyWith(errs, fileVal, modeVal, "encodings", s)
135-
}
136-
137-
fi := &FileInfo{}
138-
if err := fileVal.Decode(fi); err != nil {
139-
return nil, errors.Wrapf(err, token.NoPos, "could not parse arguments")
140-
}
141-
return fi, errs
142-
}
143-
144-
// unifyWith returns the equivalent of `v1 & v2[field][value]`.
145-
func unifyWith(errs errors.Error, v1, v2 cue.Value, field, value string) (cue.Value, errors.Error) {
146-
v1 = v1.Unify(lookup(v2, field, value))
147-
if err := v1.Err(); err != nil {
148-
errs = errors.Append(errs,
149-
errors.Newf(token.NoPos, "unknown %s %s", field, value))
150-
}
151-
return v1, errs
152-
}
153-
15473
// ParseArgs converts a sequence of command line arguments representing
15574
// files into a sequence of build file specifications.
15675
//
@@ -307,60 +226,6 @@ func ParseFileAndType(file, scope string, mode Mode) (*build.File, error) {
307226
return toFile(mode, sc, file)
308227
}
309228

310-
func hasEncoding(v cue.Value) bool {
311-
return lookup(v, "encoding").Exists()
312-
}
313-
314-
func toFile(mode Mode, sc *scope, filename string) (*build.File, error) {
315-
modeVal := lookup(typesValue, "modes", mode.String())
316-
fileVal := lookup(modeVal, "FileInfo")
317-
318-
for tagName := range sc.topLevel {
319-
info := lookup(typesValue, "tagInfo", tagName)
320-
if info.Exists() {
321-
fileVal = fileVal.Unify(info)
322-
} else {
323-
return nil, errors.Newf(token.NoPos, "unknown filetype %s", tagName)
324-
}
325-
}
326-
allowedSubsidiaryBool := lookup(fileVal, "boolTags")
327-
for tagName, val := range sc.subsidiaryBool {
328-
if !lookup(allowedSubsidiaryBool, tagName).Exists() {
329-
return nil, errors.Newf(token.NoPos, "tag %s is not allowed in this context", tagName)
330-
}
331-
fileVal = fileVal.FillPath(cue.MakePath(cue.Str("boolTags"), cue.Str(tagName)), val)
332-
}
333-
allowedSubsidiaryString := lookup(fileVal, "tags")
334-
for tagName, val := range sc.subsidiaryString {
335-
if !lookup(allowedSubsidiaryString, tagName).Exists() {
336-
return nil, errors.Newf(token.NoPos, "tag %s is not allowed in this context", tagName)
337-
}
338-
fileVal = fileVal.FillPath(cue.MakePath(cue.Str("tags"), cue.Str(tagName)), val)
339-
}
340-
if !hasEncoding(fileVal) {
341-
if filename == "-" {
342-
fileVal = fileVal.Unify(lookup(modeVal, "Default"))
343-
} else if ext := fileExt(filename); ext != "" {
344-
extFile := lookup(modeVal, "extensions", ext)
345-
if !extFile.Exists() {
346-
return nil, errors.Newf(token.NoPos, "unknown file extension %s", ext)
347-
}
348-
fileVal = fileVal.Unify(extFile)
349-
} else {
350-
return nil, errors.Newf(token.NoPos, "no encoding specified for file %q", filename)
351-
}
352-
}
353-
354-
// Note that the filename is only filled in the Go value, and not the CUE value.
355-
// This makes no difference to the logic, but saves a non-trivial amount of evaluator work.
356-
f := &build.File{Filename: filename}
357-
if err := fileVal.Decode(&f); err != nil {
358-
return nil, errors.Wrapf(err, token.NoPos,
359-
"could not determine file type")
360-
}
361-
return f, nil
362-
}
363-
364229
// scope holds attributes that influence encoding and decoding.
365230
// Together with the mode and the file name, they determine
366231
// a number of properties of the encoding process.
@@ -430,31 +295,3 @@ func seqConcat[T any](iters ...iter.Seq[T]) iter.Seq[T] {
430295
}
431296
}
432297
}
433-
434-
// lookup looks up the given string field path in v.
435-
func lookup(v cue.Value, elems ...string) cue.Value {
436-
sels := make([]cue.Selector, len(elems))
437-
for i := range elems {
438-
sels[i] = cue.Str(elems[i])
439-
}
440-
return v.LookupPath(cue.MakePath(sels...))
441-
}
442-
443-
// structFields returns an iterator over the names of all the regulat fields
444-
// in v and their values.
445-
func structFields(v cue.Value) iter.Seq2[string, cue.Value] {
446-
return func(yield func(string, cue.Value) bool) {
447-
if !v.Exists() {
448-
return
449-
}
450-
iter, err := v.Fields()
451-
if err != nil {
452-
return
453-
}
454-
for iter.Next() {
455-
if !yield(iter.Selector().Unquoted(), iter.Value()) {
456-
break
457-
}
458-
}
459-
}
460-
}

0 commit comments

Comments
 (0)