| // Copyright 2015 Canonical Ltd. |
| // Licensed under the LGPLv3, see LICENCE file for details. |
| |
| package schema |
| |
| import ( |
| "fmt" |
| "net/url" |
| "reflect" |
| "regexp" |
| ) |
| |
| // String returns a Checker that accepts a string value only and returns |
| // it unprocessed. |
| func String() Checker { |
| return stringC{} |
| } |
| |
| type stringC struct{} |
| |
| func (c stringC) Coerce(v interface{}, path []string) (interface{}, error) { |
| if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
| return reflect.ValueOf(v).String(), nil |
| } |
| return nil, error_{"string", v, path} |
| } |
| |
| // URL returns a Checker that accepts a string value that must be parseable as a |
| // URL, and returns a *net.URL. |
| func URL() Checker { |
| return urlC{} |
| } |
| |
| type urlC struct{} |
| |
| func (c urlC) Coerce(v interface{}, path []string) (interface{}, error) { |
| if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
| s := reflect.ValueOf(v).String() |
| u, err := url.Parse(s) |
| if err != nil { |
| return nil, error_{"valid url", s, path} |
| } |
| return u, nil |
| } |
| return nil, error_{"url string", v, path} |
| } |
| |
| // SimpleRegexp returns a checker that accepts a string value that is |
| // a valid regular expression and returns it unprocessed. |
| func SimpleRegexp() Checker { |
| return sregexpC{} |
| } |
| |
| type sregexpC struct{} |
| |
| func (c sregexpC) Coerce(v interface{}, path []string) (interface{}, error) { |
| // XXX The regexp package happens to be extremely simple right now. |
| // Once exp/regexp goes mainstream, we'll have to update this |
| // logic to use a more widely accepted regexp subset. |
| if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
| s := reflect.ValueOf(v).String() |
| _, err := regexp.Compile(s) |
| if err != nil { |
| return nil, error_{"valid regexp", s, path} |
| } |
| return v, nil |
| } |
| return nil, error_{"regexp string", v, path} |
| } |
| |
| // UUID returns a Checker that accepts a string value only and returns |
| // it unprocessed. |
| func UUID() Checker { |
| return uuidC{} |
| } |
| |
| type uuidC struct{} |
| |
| var uuidregex = regexp.MustCompile(`[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`) |
| |
| func (c uuidC) Coerce(v interface{}, path []string) (interface{}, error) { |
| if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
| uuid := reflect.ValueOf(v).String() |
| if uuidregex.MatchString(uuid) { |
| return uuid, nil |
| } |
| } |
| return nil, error_{"uuid", v, path} |
| } |
| |
| // Stringified returns a checker that accepts a bool/int/float/string |
| // value and returns its string. Other value types may be supported by |
| // passing in their checkers. |
| func Stringified(checkers ...Checker) Checker { |
| return stringifiedC{ |
| checkers: checkers, |
| } |
| } |
| |
| type stringifiedC struct { |
| checkers []Checker |
| } |
| |
| func (c stringifiedC) Coerce(v interface{}, path []string) (interface{}, error) { |
| if newStr, err := String().Coerce(v, path); err == nil { |
| return newStr, nil |
| } |
| _, err := OneOf(append(c.checkers, |
| Bool(), |
| Int(), |
| Float(), |
| String(), |
| URL(), |
| )...).Coerce(v, path) |
| if err != nil { |
| return nil, err |
| } |
| return fmt.Sprintf("%#v", v), nil |
| } |
| |
| // NonEmptyString returns a Checker that only accepts non-empty strings. To |
| // tweak the error message, valueLabel can contain a label of the value being |
| // checked, e.g. "my special name". If valueLabel is "", "string" will be used |
| // as a label instead. |
| // |
| // Example 1: |
| // schema.NonEmptyString("widget").Coerce("", nil) will return an error message |
| // like `expected non-empty widget, got string("")`. |
| // |
| // Example 2: |
| // schema.NonEmptyString("").Coerce("", nil) will return an error message like |
| // `expected non-empty string, got string("")`. |
| func NonEmptyString(valueLabel string) Checker { |
| if valueLabel == "" { |
| valueLabel = "string" |
| } |
| return nonEmptyStringC{valueLabel} |
| } |
| |
| type nonEmptyStringC struct { |
| valueLabel string |
| } |
| |
| func (c nonEmptyStringC) Coerce(v interface{}, path []string) (interface{}, error) { |
| label := fmt.Sprintf("non-empty %s", c.valueLabel) |
| invalidError := error_{label, v, path} |
| |
| if v == nil || reflect.TypeOf(v).Kind() != reflect.String { |
| return nil, invalidError |
| } |
| if stringValue := reflect.ValueOf(v).String(); stringValue != "" { |
| return stringValue, nil |
| } |
| return nil, invalidError |
| } |