diff --git a/kclvm_test.go b/kclvm_test.go index 808dcfef..442d4cbe 100644 --- a/kclvm_test.go +++ b/kclvm_test.go @@ -154,7 +154,8 @@ a2 = App { result, err = kcl.Run(testdata_main_k, kcl.WithCode(code), kcl.WithOverrides(":a1.image=\"new-a1-image\""), - kcl.WithOverrides("__main__:a2.image=\"new-a2-image:v123\""), + kcl.WithOverrides("a2.image=\"new-a2-image:v123\""), + kcl.WithOverrides("a2.name-"), kcl.WithPrintOverridesAST(true), ) if err != nil { @@ -167,6 +168,9 @@ a2 = App { if expect, got := "new-a2-image:v123", result.First().Get("a2.image"); expect != got { t.Fatalf("expect = %v, got = %v", expect, got) } + if expect, got := "app", result.First().Get("a2.name"); expect != got { + t.Fatalf("expect = %v, got = %v", expect, got) + } data, err := os.ReadFile(testdata_main_k) if err != nil { @@ -183,10 +187,7 @@ a1 = App { image = "new-a1-image" } -a2 = App { - image = "new-a2-image:v123" - name = "a2-app" -}`) +a2 = App {image = "new-a2-image:v123"}`) got := strings.TrimSpace(string(data)) got = strings.ReplaceAll(got, "\r\n", "\n") diff --git a/pkg/kcl/opt.go b/pkg/kcl/opt.go index 00eb77c9..089fa733 100644 --- a/pkg/kcl/opt.go +++ b/pkg/kcl/opt.go @@ -11,6 +11,7 @@ import ( "kcl-lang.io/kcl-go/pkg/settings" "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" + "kcl-lang.io/kcl-go/pkg/tools/override" ) type Option struct { @@ -125,30 +126,34 @@ func WithOptions(key_value_list ...string) Option { } // kcl -O pkgpath:path.to.field=field_value +// kcl -O pkgpath.path.to.field- func WithOverrides(override_list ...string) Option { var overrides []*gpyrpc.CmdOverrideSpec - for _, kv := range override_list { - idx0 := strings.Index(kv, ":") - if idx0 < 0 { - idx0 = 0 - } - idx1 := strings.Index(kv, "=") - if idx0 >= 0 && idx1 >= 0 && idx0 < idx1 { - var pkgpath = kv[:idx0] - var field_path = kv[idx0+1 : idx1] - var field_value = kv[idx1+1:] - overrides = append(overrides, &gpyrpc.CmdOverrideSpec{ - Pkgpath: pkgpath, - FieldPath: field_path, - FieldValue: field_value, - }) - } + for _, spec := range override_list { + o, _ := override.ParseOverrideSpec(spec) + overrides = append(overrides, o) } var opt = NewOption() opt.Overrides = overrides return *opt } +// kcl -O pkgpath:path.to.field=field_value +// kcl -O pkgpath.path.to.field- +func WithOverridesError(override_list ...string) (Option, error) { + var overrides []*gpyrpc.CmdOverrideSpec + var opt = NewOption() + for _, spec := range override_list { + o, err := override.ParseOverrideSpec(spec) + if err != nil { + return *opt, err + } + overrides = append(overrides, o) + } + opt.Overrides = overrides + return *opt, nil +} + // kcl -S path.to.field func WithSelectors(selectors ...string) Option { var opt = NewOption() diff --git a/pkg/tools/override/override.go b/pkg/tools/override/override.go index ca27f2fd..151f0da6 100644 --- a/pkg/tools/override/override.go +++ b/pkg/tools/override/override.go @@ -3,10 +3,19 @@ package override import ( + "errors" + "fmt" + "strings" + "kcl-lang.io/kcl-go/pkg/service" "kcl-lang.io/kcl-go/pkg/spec/gpyrpc" ) +const ( + DeleteAction = "Delete" + CreateOrUpdateAction = "CreateOrUpdate" +) + func OverrideFile(file string, specs, importPaths []string) (result bool, err error) { client := service.NewKclvmServiceClient() resp, err := client.OverrideFile(&gpyrpc.OverrideFile_Args{ @@ -19,3 +28,69 @@ func OverrideFile(file string, specs, importPaths []string) (result bool, err er } return resp.Result, nil } + +func ParseOverrideSpec(spec string) (*gpyrpc.CmdOverrideSpec, error) { + if strings.Contains(spec, "=") { + // Create or update the override value. + splitValues := strings.SplitN(spec, "=", 2) + if len(splitValues) < 2 { + return nil, invalidSpecError(spec) + } + path := splitValues[0] + fieldValue := splitValues[1] + pkgpath, fieldPath, err := splitFieldPath(path) + if err != nil { + return nil, err + } + return &gpyrpc.CmdOverrideSpec{ + Pkgpath: pkgpath, + FieldPath: fieldPath, + FieldValue: fieldValue, + Action: CreateOrUpdateAction, + }, nil + } else if strippedSpec := strings.TrimSuffix(spec, "-"); strippedSpec != spec { + // Delete the override value. + pkgpath, fieldPath, err := splitFieldPath(strippedSpec) + if err != nil { + return nil, err + } + return &gpyrpc.CmdOverrideSpec{ + Pkgpath: pkgpath, + FieldPath: fieldPath, + FieldValue: "", + Action: DeleteAction, + }, nil + } else { + return nil, invalidSpecError(spec) + } +} + +// Get field package path and identifier name from the path. +// +// split_field_path("pkg.to.path:field") -> ("pkg.to.path", "field") +func splitFieldPath(path string) (string, string, error) { + err := errors.New("Invalid field path " + path) + paths := strings.SplitN(path, ":", 2) + if len(paths) == 1 { + pkgpath := "" + fieldPath := paths[0] + if fieldPath == "" { + return "", "", err + } + return pkgpath, fieldPath, nil + } else if len(paths) == 2 { + pkgpath := paths[0] + fieldPath := paths[1] + if fieldPath == "" { + return "", "", err + } + return pkgpath, fieldPath, nil + } else { + return "", "", err + } +} + +// / Get the invalid spec error message. +func invalidSpecError(spec string) error { + return fmt.Errorf("invalid spec format '%s', expected := or :-", spec) +}