github.com/marinho/drone@v0.2.1-0.20140504195434-d3ba962e89a7/Godeps/_workspace/src/launchpad.net/goyaml/goyaml.go (about) 1 // Package goyaml implements YAML support for the Go language. 2 // 3 // WARNING: You are using an out of date import path. Please update your code and import the following instead: 4 // 5 // gonuts.org/v1/yaml 6 // 7 // The package name has changed from "yaml" from "goyaml", but the package API has not changed. 8 // 9 package goyaml 10 11 import ( 12 "errors" 13 "fmt" 14 "reflect" 15 "runtime" 16 "strings" 17 "sync" 18 ) 19 20 func handleErr(err *error) { 21 if r := recover(); r != nil { 22 if _, ok := r.(runtime.Error); ok { 23 panic(r) 24 } else if _, ok := r.(*reflect.ValueError); ok { 25 panic(r) 26 } else if _, ok := r.(externalPanic); ok { 27 panic(r) 28 } else if s, ok := r.(string); ok { 29 *err = errors.New("YAML error: " + s) 30 } else if e, ok := r.(error); ok { 31 *err = e 32 } else { 33 panic(r) 34 } 35 } 36 } 37 38 // Objects implementing the goyaml.Setter interface will receive the YAML 39 // tag and value via the SetYAML method during unmarshaling, rather than 40 // being implicitly assigned by the goyaml machinery. If setting the value 41 // works, the method should return true. If it returns false, the given 42 // value will be omitted from maps and slices. 43 type Setter interface { 44 SetYAML(tag string, value interface{}) bool 45 } 46 47 // Objects implementing the goyaml.Getter interface will get the GetYAML() 48 // method called when goyaml is requested to marshal the given value, and 49 // the result of this method will be marshaled in place of the actual object. 50 type Getter interface { 51 GetYAML() (tag string, value interface{}) 52 } 53 54 // Unmarshal decodes the first document found within the in byte slice 55 // and assigns decoded values into the object pointed by out. 56 // 57 // Maps, pointers to structs and ints, etc, may all be used as out values. 58 // If an internal pointer within a struct is not initialized, goyaml 59 // will initialize it if necessary for unmarshalling the provided data, 60 // but the struct provided as out must not be a nil pointer. 61 // 62 // The type of the decoded values and the type of out will be considered, 63 // and Unmarshal() will do the best possible job to unmarshal values 64 // appropriately. It is NOT considered an error, though, to skip values 65 // because they are not available in the decoded YAML, or if they are not 66 // compatible with the out value. To ensure something was properly 67 // unmarshaled use a map or compare against the previous value for the 68 // field (usually the zero value). 69 // 70 // Struct fields are only unmarshalled if they are exported (have an 71 // upper case first letter), and will be unmarshalled using the field 72 // name lowercased by default. When custom field names are desired, the 73 // tag value may be used to tweak the name. Everything before the first 74 // comma in the field tag will be used as the name. The values following 75 // the comma are used to tweak the marshalling process (see Marshal). 76 // Conflicting names result in a runtime error. 77 // 78 // For example: 79 // 80 // type T struct { 81 // F int `yaml:"a,omitempty"` 82 // B int 83 // } 84 // var T t 85 // goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t) 86 // 87 // See the documentation of Marshal for the format of tags and a list of 88 // supported tag options. 89 // 90 func Unmarshal(in []byte, out interface{}) (err error) { 91 defer handleErr(&err) 92 d := newDecoder() 93 p := newParser(in) 94 defer p.destroy() 95 node := p.parse() 96 if node != nil { 97 d.unmarshal(node, reflect.ValueOf(out)) 98 } 99 return nil 100 } 101 102 // Marshal serializes the value provided into a YAML document. The structure 103 // of the generated document will reflect the structure of the value itself. 104 // Maps, pointers to structs and ints, etc, may all be used as the in value. 105 // 106 // In the case of struct values, only exported fields will be serialized. 107 // The lowercased field name is used as the key for each exported field, 108 // but this behavior may be changed using the respective field tag. 109 // The tag may also contain flags to tweak the marshalling behavior for 110 // the field. Conflicting names result in a runtime error. The tag format 111 // accepted is: 112 // 113 // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` 114 // 115 // The following flags are currently supported: 116 // 117 // omitempty Only include the field if it's not set to the zero 118 // value for the type or to empty slices or maps. 119 // Does not apply to zero valued structs. 120 // 121 // flow Marshal using a flow style (useful for structs, 122 // sequences and maps. 123 // 124 // inline Inline the struct it's applied to, so its fields 125 // are processed as if they were part of the outer 126 // struct. 127 // 128 // In addition, if the key is "-", the field is ignored. 129 // 130 // For example: 131 // 132 // type T struct { 133 // F int "a,omitempty" 134 // B int 135 // } 136 // goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n" 137 // goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" 138 // 139 func Marshal(in interface{}) (out []byte, err error) { 140 defer handleErr(&err) 141 e := newEncoder() 142 defer e.destroy() 143 e.marshal("", reflect.ValueOf(in)) 144 e.finish() 145 out = e.out 146 return 147 } 148 149 // -------------------------------------------------------------------------- 150 // Maintain a mapping of keys to structure field indexes 151 152 // The code in this section was copied from gobson. 153 154 // structInfo holds details for the serialization of fields of 155 // a given struct. 156 type structInfo struct { 157 FieldsMap map[string]fieldInfo 158 FieldsList []fieldInfo 159 160 // InlineMap is the number of the field in the struct that 161 // contains an ,inline map, or -1 if there's none. 162 InlineMap int 163 } 164 165 type fieldInfo struct { 166 Key string 167 Num int 168 OmitEmpty bool 169 Flow bool 170 171 // Inline holds the field index if the field is part of an inlined struct. 172 Inline []int 173 } 174 175 var structMap = make(map[reflect.Type]*structInfo) 176 var fieldMapMutex sync.RWMutex 177 178 type externalPanic string 179 180 func (e externalPanic) String() string { 181 return string(e) 182 } 183 184 func getStructInfo(st reflect.Type) (*structInfo, error) { 185 fieldMapMutex.RLock() 186 sinfo, found := structMap[st] 187 fieldMapMutex.RUnlock() 188 if found { 189 return sinfo, nil 190 } 191 192 n := st.NumField() 193 fieldsMap := make(map[string]fieldInfo) 194 fieldsList := make([]fieldInfo, 0, n) 195 inlineMap := -1 196 for i := 0; i != n; i++ { 197 field := st.Field(i) 198 if field.PkgPath != "" { 199 continue // Private field 200 } 201 202 info := fieldInfo{Num: i} 203 204 tag := field.Tag.Get("yaml") 205 if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 206 tag = string(field.Tag) 207 } 208 if tag == "-" { 209 continue 210 } 211 212 inline := false 213 fields := strings.Split(tag, ",") 214 if len(fields) > 1 { 215 for _, flag := range fields[1:] { 216 switch flag { 217 case "omitempty": 218 info.OmitEmpty = true 219 case "flow": 220 info.Flow = true 221 case "inline": 222 inline = true 223 default: 224 msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) 225 panic(externalPanic(msg)) 226 } 227 } 228 tag = fields[0] 229 } 230 231 if inline { 232 switch field.Type.Kind() { 233 //case reflect.Map: 234 // if inlineMap >= 0 { 235 // return nil, errors.New("Multiple ,inline maps in struct " + st.String()) 236 // } 237 // if field.Type.Key() != reflect.TypeOf("") { 238 // return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) 239 // } 240 // inlineMap = info.Num 241 case reflect.Struct: 242 sinfo, err := getStructInfo(field.Type) 243 if err != nil { 244 return nil, err 245 } 246 for _, finfo := range sinfo.FieldsList { 247 if _, found := fieldsMap[finfo.Key]; found { 248 msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() 249 return nil, errors.New(msg) 250 } 251 if finfo.Inline == nil { 252 finfo.Inline = []int{i, finfo.Num} 253 } else { 254 finfo.Inline = append([]int{i}, finfo.Inline...) 255 } 256 fieldsMap[finfo.Key] = finfo 257 fieldsList = append(fieldsList, finfo) 258 } 259 default: 260 //panic("Option ,inline needs a struct value or map field") 261 panic("Option ,inline needs a struct value field") 262 } 263 continue 264 } 265 266 if tag != "" { 267 info.Key = tag 268 } else { 269 info.Key = strings.ToLower(field.Name) 270 } 271 272 if _, found = fieldsMap[info.Key]; found { 273 msg := "Duplicated key '" + info.Key + "' in struct " + st.String() 274 return nil, errors.New(msg) 275 } 276 277 fieldsList = append(fieldsList, info) 278 fieldsMap[info.Key] = info 279 } 280 281 sinfo = &structInfo{fieldsMap, fieldsList, inlineMap} 282 283 fieldMapMutex.Lock() 284 structMap[st] = sinfo 285 fieldMapMutex.Unlock() 286 return sinfo, nil 287 } 288 289 func isZero(v reflect.Value) bool { 290 switch v.Kind() { 291 case reflect.String: 292 return len(v.String()) == 0 293 case reflect.Interface, reflect.Ptr: 294 return v.IsNil() 295 case reflect.Slice: 296 return v.Len() == 0 297 case reflect.Map: 298 return v.Len() == 0 299 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 300 return v.Int() == 0 301 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 302 return v.Uint() == 0 303 case reflect.Bool: 304 return !v.Bool() 305 } 306 return false 307 }