cmd/cue/cmd: allow specifying field type for path flag

The current new format does not allow indicating
whether a label is for a definition or regular field.

Reintroduce the old method, but require it to end in
a colon.

Change-Id: I81161b4d66ccbdd5fb42cb7b54e8f14324d113fa
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5241
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/orphans.go b/cmd/cue/cmd/orphans.go
index dd31d9d..f7b764d 100644
--- a/cmd/cue/cmd/orphans.go
+++ b/cmd/cue/cmd/orphans.go
@@ -15,11 +15,13 @@
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"path/filepath"
 	"regexp"
 	"strconv"
 
+	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/build"
 	"cuelang.org/go/cue/parser"
@@ -132,6 +134,7 @@
 
 		// Compute a path different from root.
 		var pathElems []ast.Label
+		var pathTokens []token.Token
 
 		switch {
 		case len(flagPath.StringArray(cmd)) > 0:
@@ -152,7 +155,14 @@
 			for _, str := range flagPath.StringArray(cmd) {
 				l, err := parser.ParseExpr("--path", str)
 				if err != nil {
-					return nil, fmt.Errorf(`labels are of form "cue import -l foo -l 'strings.ToLower(bar)'": %v`, err)
+					labels, tokens, err := parseFullPath(inst, str)
+					if err != nil {
+						return nil, fmt.Errorf(
+							`labels must be expressions (-l foo -l 'strings.ToLower(bar)') or full paths (-l '"foo": "\(strings.ToLower(bar))":) : %v`, err)
+					}
+					pathElems = append(pathElems, labels...)
+					pathTokens = append(pathTokens, tokens...)
+					continue
 				}
 
 				str, err := inst.Eval(l).String()
@@ -160,6 +170,7 @@
 					return nil, fmt.Errorf("unsupported label path type: %v", err)
 				}
 				pathElems = append(pathElems, ast.NewString(str))
+				pathTokens = append(pathTokens, 0)
 			}
 		}
 
@@ -187,10 +198,12 @@
 			f.Decls = append(f.Decls, obj.Elts...)
 		} else {
 			field := &ast.Field{Label: pathElems[0]}
+			field.Token = pathTokens[0]
 			f.Decls = append(f.Decls, field)
-			for _, e := range pathElems[1:] {
+			for i, e := range pathElems[1:] {
 				newField := &ast.Field{Label: e}
 				newVal := ast.NewStruct(newField)
+				newField.Token = pathTokens[i+1]
 				field.Value = newVal
 				field = newField
 			}
@@ -217,6 +230,55 @@
 	return f, nil
 }
 
+func parseFullPath(inst *cue.Instance, exprs string) (p []ast.Label, t []token.Token, err error) {
+	f, err := parser.ParseFile("--path", exprs+"_")
+	if err != nil {
+		return nil, nil, fmt.Errorf("parser error in path %q: %v", exprs, err)
+	}
+
+	if len(f.Decls) != 1 {
+		return nil, nil, errors.New("path flag must be a space-separated sequence of labels")
+	}
+
+	for d := f.Decls[0]; ; {
+		field, ok := d.(*ast.Field)
+		if !ok {
+			// This should never happen
+			return nil, nil, errors.New("%q not a sequence of labels")
+		}
+
+		t = append(t, field.Token)
+
+		switch x := field.Label.(type) {
+		case *ast.Ident, *ast.BasicLit:
+			p = append(p, x)
+
+		case *ast.TemplateLabel:
+			return nil, nil, fmt.Errorf("template labels not supported in path flag")
+
+		case ast.Expr:
+			v := inst.Eval(x)
+			if v.Kind() == cue.BottomKind {
+				return nil, nil, v.Err()
+			}
+			p = append(p, v.Syntax().(ast.Label))
+
+		}
+
+		v, ok := field.Value.(*ast.StructLit)
+		if !ok {
+			break
+		}
+
+		if len(v.Elts) != 1 {
+			return nil, nil, errors.New("path value may not contain a struct")
+		}
+
+		d = v.Elts[0]
+	}
+	return p, t, nil
+}
+
 type listIndex struct {
 	index map[string]*listIndex
 	field *ast.Field
diff --git a/cmd/cue/cmd/testdata/script/import_context.txt b/cmd/cue/cmd/testdata/script/import_context.txt
index 34860ca..648604b 100644
--- a/cmd/cue/cmd/testdata/script/import_context.txt
+++ b/cmd/cue/cmd/testdata/script/import_context.txt
@@ -1,3 +1,5 @@
+cue import -o - -f --with-context -l '"\(path.Ext(filename)):\(index+1)/\(recordCount)": "\(data["@name"])":' ./import
+cmp stdout expect-stdout
 cue import -o - -f --with-context -l '"\(path.Ext(filename)):\(index+1)/\(recordCount)"' -l 'data["@name"]' ./import
 cmp stdout expect-stdout
 -- expect-stdout --
diff --git a/cmd/cue/cmd/testdata/script/import_hoiststr.txt b/cmd/cue/cmd/testdata/script/import_hoiststr.txt
index e0f50f6..5172148 100644
--- a/cmd/cue/cmd/testdata/script/import_hoiststr.txt
+++ b/cmd/cue/cmd/testdata/script/import_hoiststr.txt
@@ -1,3 +1,6 @@
+cue import -o - -f --list -l '"\(strings.ToLower(kind))": "\(name)":' --recursive ./import
+cmp stdout expect-stdout
+
 cue import -o - -f --list -l 'strings.ToLower(kind)' -l name --recursive ./import
 cmp stdout expect-stdout
 -- expect-stdout --
diff --git a/cmd/cue/cmd/testdata/script/import_path.txt b/cmd/cue/cmd/testdata/script/import_path.txt
index b6715a6..ff50b4b 100644
--- a/cmd/cue/cmd/testdata/script/import_path.txt
+++ b/cmd/cue/cmd/testdata/script/import_path.txt
@@ -1,3 +1,6 @@
+cue import -o - -f -l '"\(strings.ToLower(kind))": "\(name)":' ./import
+cmp stdout expect-stdout
+
 cue import -o - -f -l 'strings.ToLower(kind)' -l name ./import
 cmp stdout expect-stdout
 -- expect-stdout --