1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
// 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
}
|