func (p *cparser) parseEnum(s paramSpec) (Param, error) { var e *enumSpec if err := json.Unmarshal(s.S, &e); err != nil { return nil, trace.Wrap( err, fmt.Sprintf("failed to parse: '%v'", string(s.S))) } if len(e.Values) == 0 { return nil, trace.Errorf("provide at least one value for '%v'", s.Name) } values := make([]string, len(e.Values)) seen := make(map[string]bool, len(e.Values)) for i, v := range e.Values { if v == "" { return nil, trace.Errorf("value can not be an empty string") } if seen[v] { return nil, trace.Errorf("duplicate value: '%v'", v) } values[i] = v } ep := &EnumParam{values: values} ep.paramCommon = s.common() return ep, nil }
func (p *cparser) parseKeyVal(s paramSpec) (Param, error) { var k *kvSpec if err := json.Unmarshal(s.S, &k); err != nil { return nil, trace.Wrap( err, fmt.Sprintf("failed to parse: '%v'", string(s.S))) } if len(k.Keys) == 0 { return nil, trace.Errorf("provide at least one key for '%v'", s.Name) } keys := make([]Param, len(k.Keys)) for i, ks := range k.Keys { k, err := p.parseParam(ks, true) if err != nil { return nil, err } keys[i] = k } if err := checkSameNames(keys); err != nil { return nil, err } kv := &KVParam{keys: keys, separator: k.Separator} kv.paramCommon = s.common() return kv, nil }
// ProcessObject checks if the object is valid from this schema's standpoint // and returns an object with defaults set up according to schema's spec func (j *JSONSchema) ProcessObject(in interface{}) (interface{}, error) { result, err := j.schema.Validate(gojsonschema.NewGoLoader(in)) if err != nil { return nil, trace.Wrap(err) } if !result.Valid() { return nil, trace.Wrap(trace.Errorf("errors: %v", schemaErrors(result.Errors()))) } return setDefaults(j.rawSchema, in), nil }
func checkSameNames(ps []Param) error { n := map[string]bool{} for _, p := range ps { if n[p.Name()] { return trace.Errorf("parameter '%v' is already defined", n) } n[p.Name()] = true } return nil }
func (c *ctx) Env(key string) (string, error) { v, ok := c.env[key] if !ok { return "", trace.Errorf("environment variable '%v' is not set", key) } values := cstrings.SplitComma(v) out := make([]string, len(values)) for i, p := range values { out[i] = quoteYAML(p) } return strings.Join(out, ","), nil }
func parseEnvironment() (map[string]string, error) { values := os.Environ() env := make(map[string]string, len(values)) for _, v := range values { vals := strings.SplitN(v, "=", 2) if len(vals) != 2 { return nil, trace.Errorf("failed to parse variable: '%v'", v) } env[vals[0]] = vals[1] } return env, nil }
func (p *cparser) parseParam(s paramSpec, scalar bool) (Param, error) { if s.Name == "" { return nil, trace.Errorf("set a type name") } if err := p.checkName(s.Name); err != nil { return nil, err } if s.Type == "" { return nil, trace.Errorf("set a type for '%v'", s.Name) } switch s.Type { case "String": pr := &StringParam{} pr.paramCommon = s.common() return pr, nil case "Path": pr := &PathParam{} pr.paramCommon = s.common() return pr, nil case "Int": pr := &IntParam{} pr.paramCommon = s.common() return pr, nil case "Bool": pr := &BoolParam{} pr.paramCommon = s.common() return pr, nil case "KeyVal": return p.parseKeyVal(s) case "Enum": return p.parseEnum(s) case "List": if scalar { return nil, trace.Errorf( "scalar values are not allowed here: '%v'", s.Type) } return p.parseList(s) } return nil, trace.Errorf("unrecognized type: '%v'", s.Type) }
// Set accepts string with arguments in the form "key:val,key2:val2" func (kv *KeyVal) Set(v string) error { if len(*kv) == 0 { *kv = make(map[string]string) } for _, i := range cstrings.SplitComma(v) { vals := strings.SplitN(i, ":", 2) if len(vals) != 2 { return trace.Errorf("extra options should be defined like KEY:VAL") } (*kv)[vals[0]] = vals[1] } return nil }
func newCtx() (*ctx, error) { values := os.Environ() c := &ctx{ env: make(map[string]string, len(values)), } for _, v := range values { vals := strings.SplitN(v, "=", 2) if len(vals) != 2 { return nil, trace.Errorf("failed to parse variable: '%v'", v) } c.env[vals[0]] = vals[1] } return c, nil }
func (c *Config) ParseVars(vars map[string]string) error { for _, p := range c.Params { val, ok := vars[p.Name()] if !ok { if p.Required() { return trace.Errorf("missing value for required variable: %v", p.Name()) } else { val = p.Default() } } if err := p.Set(val); err != nil { return trace.Wrap(err) } } return nil }
func (p *EnumParam) Set(s string) error { found := false for _, v := range p.values { if s == v { found = true } } if !found { return trace.Errorf( "value '%v' is not one of the allowed '%v'", s, strings.Join(p.values, ","), ) } p.value = &s return nil }
func (p *cparser) checkName(n string) error { for _, pr := range p.params { if pr.Name() == n { return trace.Errorf("parameter '%v' is already defined", n) } } e, err := parser.ParseExpr(n) if err != nil { return trace.Wrap( err, fmt.Sprintf("failed to parse name: '%v'", n)) } if _, ok := e.(*ast.Ident); !ok { return trace.Wrap( err, fmt.Sprintf("name should be a valid identifier: '%v'", n)) } return nil }
// ProcessObject checks if the object is valid from this schema's standpoint // and returns an object with defaults set up according to schema's spec func (j *JSONSchema) ProcessObject(in interface{}) (interface{}, error) { defaults := setDefaults(j.rawSchema, in) result, err := j.schema.Validate(gojsonschema.NewGoLoader(defaults)) if err != nil { return nil, trace.Wrap(err) } if !result.Valid() { errors := result.Errors() output := make([]string, len(errors)) for i, err := range errors { output[i] = fmt.Sprintf("%v", err) } return nil, trace.Errorf("failed to validate: %v", strings.Join(output, ",")) } return defaults, nil }
func (p *KVParam) Set(s string) error { sep := p.sep() parts := strings.Split(s, sep) if len(parts) != len(p.keys) { return trace.Errorf( "expected elements separated by '%v', got '%v'", sep, s) } values := make([]Param, len(p.keys)) for i, pt := range parts { el := p.keys[i].New() if err := el.Set(pt); err != nil { return err } values[i] = el } p.values = values return nil }
func setupApp(app *kingpin.Application, v reflect.Value) error { // for structs, walk every element and parse vType := v.Type() if v.Kind() != reflect.Struct { return nil } for i := 0; i < v.NumField(); i++ { structField := vType.Field(i) field := v.Field(i) if !field.CanSet() { continue } kind := field.Kind() if kind == reflect.Struct { if err := setupApp(app, field); err != nil { return trace.Wrap(err, fmt.Sprintf("failed parsing struct field %v", structField.Name)) } } cliFlag := structField.Tag.Get("cli") if cliFlag == "" { continue } if !field.CanAddr() { continue } f := app.Flag(cliFlag, cliFlag) fieldPtr := field.Addr().Interface() if setter, ok := fieldPtr.(CLISetter); ok { f.SetValue(&cliValue{setter: setter}) continue } if setter, ok := fieldPtr.(StringSetter); ok { f.SetValue(&cliValue{setter: &cliStringSetter{setter: setter}}) continue } switch ptr := fieldPtr.(type) { case *map[string]string: f.SetValue(&cliMapValue{v: ptr}) continue case *[]map[string]string: f.SetValue(&cliSliceMapValue{v: ptr}) continue case *int: f.SetValue(&cliIntValue{v: ptr}) continue case *int32: f.SetValue(&cliInt32Value{v: ptr}) continue case *int64: f.SetValue(&cliInt64Value{v: ptr}) continue case *string: f.SetValue(&cliStringValue{v: ptr}) case *bool: f.SetValue(&cliBoolValue{v: ptr}) default: return trace.Errorf("unsupported type: %T", ptr) } } return nil }