github.com/rochacon/deis@v1.0.2-0.20150903015341-6839b592a1ff/Godeps/_workspace/src/gopkg.in/yaml.v2/resolve.go (about)

     1  package yaml
     2  
     3  import (
     4  	"encoding/base64"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  	"unicode/utf8"
     9  )
    10  
    11  type resolveMapItem struct {
    12  	value interface{}
    13  	tag   string
    14  }
    15  
    16  var resolveTable = make([]byte, 256)
    17  var resolveMap = make(map[string]resolveMapItem)
    18  
    19  func init() {
    20  	t := resolveTable
    21  	t[int('+')] = 'S' // Sign
    22  	t[int('-')] = 'S'
    23  	for _, c := range "0123456789" {
    24  		t[int(c)] = 'D' // Digit
    25  	}
    26  	for _, c := range "yYnNtTfFoO~" {
    27  		t[int(c)] = 'M' // In map
    28  	}
    29  	t[int('.')] = '.' // Float (potentially in map)
    30  
    31  	var resolveMapList = []struct {
    32  		v   interface{}
    33  		tag string
    34  		l   []string
    35  	}{
    36  		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
    37  		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
    38  		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
    39  		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
    40  		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
    41  		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
    42  		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
    43  		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
    44  		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
    45  		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
    46  		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
    47  		{"<<", yaml_MERGE_TAG, []string{"<<"}},
    48  	}
    49  
    50  	m := resolveMap
    51  	for _, item := range resolveMapList {
    52  		for _, s := range item.l {
    53  			m[s] = resolveMapItem{item.v, item.tag}
    54  		}
    55  	}
    56  }
    57  
    58  const longTagPrefix = "tag:yaml.org,2002:"
    59  
    60  func shortTag(tag string) string {
    61  	// TODO This can easily be made faster and produce less garbage.
    62  	if strings.HasPrefix(tag, longTagPrefix) {
    63  		return "!!" + tag[len(longTagPrefix):]
    64  	}
    65  	return tag
    66  }
    67  
    68  func longTag(tag string) string {
    69  	if strings.HasPrefix(tag, "!!") {
    70  		return longTagPrefix + tag[2:]
    71  	}
    72  	return tag
    73  }
    74  
    75  func resolvableTag(tag string) bool {
    76  	switch tag {
    77  	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
    78  		return true
    79  	}
    80  	return false
    81  }
    82  
    83  func resolve(tag string, in string) (rtag string, out interface{}) {
    84  	if !resolvableTag(tag) {
    85  		return tag, in
    86  	}
    87  
    88  	defer func() {
    89  		switch tag {
    90  		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
    91  			return
    92  		}
    93  		failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
    94  	}()
    95  
    96  	// Any data is accepted as a !!str or !!binary.
    97  	// Otherwise, the prefix is enough of a hint about what it might be.
    98  	hint := byte('N')
    99  	if in != "" {
   100  		hint = resolveTable[in[0]]
   101  	}
   102  	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
   103  		// Handle things we can lookup in a map.
   104  		if item, ok := resolveMap[in]; ok {
   105  			return item.tag, item.value
   106  		}
   107  
   108  		// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
   109  		// are purposefully unsupported here. They're still quoted on
   110  		// the way out for compatibility with other parser, though.
   111  
   112  		switch hint {
   113  		case 'M':
   114  			// We've already checked the map above.
   115  
   116  		case '.':
   117  			// Not in the map, so maybe a normal float.
   118  			floatv, err := strconv.ParseFloat(in, 64)
   119  			if err == nil {
   120  				return yaml_FLOAT_TAG, floatv
   121  			}
   122  
   123  		case 'D', 'S':
   124  			// Int, float, or timestamp.
   125  			plain := strings.Replace(in, "_", "", -1)
   126  			intv, err := strconv.ParseInt(plain, 0, 64)
   127  			if err == nil {
   128  				if intv == int64(int(intv)) {
   129  					return yaml_INT_TAG, int(intv)
   130  				} else {
   131  					return yaml_INT_TAG, intv
   132  				}
   133  			}
   134  			floatv, err := strconv.ParseFloat(plain, 64)
   135  			if err == nil {
   136  				return yaml_FLOAT_TAG, floatv
   137  			}
   138  			if strings.HasPrefix(plain, "0b") {
   139  				intv, err := strconv.ParseInt(plain[2:], 2, 64)
   140  				if err == nil {
   141  					return yaml_INT_TAG, int(intv)
   142  				}
   143  			} else if strings.HasPrefix(plain, "-0b") {
   144  				intv, err := strconv.ParseInt(plain[3:], 2, 64)
   145  				if err == nil {
   146  					return yaml_INT_TAG, -int(intv)
   147  				}
   148  			}
   149  			// XXX Handle timestamps here.
   150  
   151  		default:
   152  			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
   153  		}
   154  	}
   155  	if tag == yaml_BINARY_TAG {
   156  		return yaml_BINARY_TAG, in
   157  	}
   158  	if utf8.ValidString(in) {
   159  		return yaml_STR_TAG, in
   160  	}
   161  	return yaml_BINARY_TAG, encodeBase64(in)
   162  }
   163  
   164  // encodeBase64 encodes s as base64 that is broken up into multiple lines
   165  // as appropriate for the resulting length.
   166  func encodeBase64(s string) string {
   167  	const lineLen = 70
   168  	encLen := base64.StdEncoding.EncodedLen(len(s))
   169  	lines := encLen/lineLen + 1
   170  	buf := make([]byte, encLen*2+lines)
   171  	in := buf[0:encLen]
   172  	out := buf[encLen:]
   173  	base64.StdEncoding.Encode(in, []byte(s))
   174  	k := 0
   175  	for i := 0; i < len(in); i += lineLen {
   176  		j := i + lineLen
   177  		if j > len(in) {
   178  			j = len(in)
   179  		}
   180  		k += copy(out[k:], in[i:j])
   181  		if lines > 1 {
   182  			out[k] = '\n'
   183  			k++
   184  		}
   185  	}
   186  	return string(out[:k])
   187  }