github.com/rochacon/deis@v1.0.2-0.20150903015341-6839b592a1ff/Godeps/_workspace/src/gopkg.in/yaml.v2/encode.go (about) 1 package yaml 2 3 import ( 4 "encoding" 5 "reflect" 6 "regexp" 7 "sort" 8 "strconv" 9 "strings" 10 "time" 11 ) 12 13 type encoder struct { 14 emitter yaml_emitter_t 15 event yaml_event_t 16 out []byte 17 flow bool 18 } 19 20 func newEncoder() (e *encoder) { 21 e = &encoder{} 22 e.must(yaml_emitter_initialize(&e.emitter)) 23 yaml_emitter_set_output_string(&e.emitter, &e.out) 24 yaml_emitter_set_unicode(&e.emitter, true) 25 e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) 26 e.emit() 27 e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) 28 e.emit() 29 return e 30 } 31 32 func (e *encoder) finish() { 33 e.must(yaml_document_end_event_initialize(&e.event, true)) 34 e.emit() 35 e.emitter.open_ended = false 36 e.must(yaml_stream_end_event_initialize(&e.event)) 37 e.emit() 38 } 39 40 func (e *encoder) destroy() { 41 yaml_emitter_delete(&e.emitter) 42 } 43 44 func (e *encoder) emit() { 45 // This will internally delete the e.event value. 46 if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { 47 e.must(false) 48 } 49 } 50 51 func (e *encoder) must(ok bool) { 52 if !ok { 53 msg := e.emitter.problem 54 if msg == "" { 55 msg = "unknown problem generating YAML content" 56 } 57 failf("%s", msg) 58 } 59 } 60 61 func (e *encoder) marshal(tag string, in reflect.Value) { 62 if !in.IsValid() { 63 e.nilv() 64 return 65 } 66 iface := in.Interface() 67 if m, ok := iface.(Marshaler); ok { 68 v, err := m.MarshalYAML() 69 if err != nil { 70 fail(err) 71 } 72 if v == nil { 73 e.nilv() 74 return 75 } 76 in = reflect.ValueOf(v) 77 } 78 if m, ok := iface.(encoding.TextMarshaler); ok { 79 text, err := m.MarshalText() 80 if err != nil { 81 fail(err) 82 } 83 in = reflect.ValueOf(string(text)) 84 } 85 switch in.Kind() { 86 case reflect.Interface: 87 if in.IsNil() { 88 e.nilv() 89 } else { 90 e.marshal(tag, in.Elem()) 91 } 92 case reflect.Map: 93 e.mapv(tag, in) 94 case reflect.Ptr: 95 if in.IsNil() { 96 e.nilv() 97 } else { 98 e.marshal(tag, in.Elem()) 99 } 100 case reflect.Struct: 101 e.structv(tag, in) 102 case reflect.Slice: 103 if in.Type().Elem() == mapItemType { 104 e.itemsv(tag, in) 105 } else { 106 e.slicev(tag, in) 107 } 108 case reflect.String: 109 e.stringv(tag, in) 110 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 111 if in.Type() == durationType { 112 e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) 113 } else { 114 e.intv(tag, in) 115 } 116 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 117 e.uintv(tag, in) 118 case reflect.Float32, reflect.Float64: 119 e.floatv(tag, in) 120 case reflect.Bool: 121 e.boolv(tag, in) 122 default: 123 panic("cannot marshal type: " + in.Type().String()) 124 } 125 } 126 127 func (e *encoder) mapv(tag string, in reflect.Value) { 128 e.mappingv(tag, func() { 129 keys := keyList(in.MapKeys()) 130 sort.Sort(keys) 131 for _, k := range keys { 132 e.marshal("", k) 133 e.marshal("", in.MapIndex(k)) 134 } 135 }) 136 } 137 138 func (e *encoder) itemsv(tag string, in reflect.Value) { 139 e.mappingv(tag, func() { 140 slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) 141 for _, item := range slice { 142 e.marshal("", reflect.ValueOf(item.Key)) 143 e.marshal("", reflect.ValueOf(item.Value)) 144 } 145 }) 146 } 147 148 func (e *encoder) structv(tag string, in reflect.Value) { 149 sinfo, err := getStructInfo(in.Type()) 150 if err != nil { 151 panic(err) 152 } 153 e.mappingv(tag, func() { 154 for _, info := range sinfo.FieldsList { 155 var value reflect.Value 156 if info.Inline == nil { 157 value = in.Field(info.Num) 158 } else { 159 value = in.FieldByIndex(info.Inline) 160 } 161 if info.OmitEmpty && isZero(value) { 162 continue 163 } 164 e.marshal("", reflect.ValueOf(info.Key)) 165 e.flow = info.Flow 166 e.marshal("", value) 167 } 168 }) 169 } 170 171 func (e *encoder) mappingv(tag string, f func()) { 172 implicit := tag == "" 173 style := yaml_BLOCK_MAPPING_STYLE 174 if e.flow { 175 e.flow = false 176 style = yaml_FLOW_MAPPING_STYLE 177 } 178 e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 179 e.emit() 180 f() 181 e.must(yaml_mapping_end_event_initialize(&e.event)) 182 e.emit() 183 } 184 185 func (e *encoder) slicev(tag string, in reflect.Value) { 186 implicit := tag == "" 187 style := yaml_BLOCK_SEQUENCE_STYLE 188 if e.flow { 189 e.flow = false 190 style = yaml_FLOW_SEQUENCE_STYLE 191 } 192 e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 193 e.emit() 194 n := in.Len() 195 for i := 0; i < n; i++ { 196 e.marshal("", in.Index(i)) 197 } 198 e.must(yaml_sequence_end_event_initialize(&e.event)) 199 e.emit() 200 } 201 202 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 203 // 204 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 205 // in YAML 1.2 and by this package, but these should be marshalled quoted for 206 // the time being for compatibility with other parsers. 207 func isBase60Float(s string) (result bool) { 208 // Fast path. 209 if s == "" { 210 return false 211 } 212 c := s[0] 213 if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 214 return false 215 } 216 // Do the full match. 217 return base60float.MatchString(s) 218 } 219 220 // From http://yaml.org/type/float.html, except the regular expression there 221 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 222 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 223 224 func (e *encoder) stringv(tag string, in reflect.Value) { 225 var style yaml_scalar_style_t 226 s := in.String() 227 rtag, rs := resolve("", s) 228 if rtag == yaml_BINARY_TAG { 229 if tag == "" || tag == yaml_STR_TAG { 230 tag = rtag 231 s = rs.(string) 232 } else if tag == yaml_BINARY_TAG { 233 failf("explicitly tagged !!binary data must be base64-encoded") 234 } else { 235 failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 236 } 237 } 238 if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { 239 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 240 } else if strings.Contains(s, "\n") { 241 style = yaml_LITERAL_SCALAR_STYLE 242 } else { 243 style = yaml_PLAIN_SCALAR_STYLE 244 } 245 e.emitScalar(s, "", tag, style) 246 } 247 248 func (e *encoder) boolv(tag string, in reflect.Value) { 249 var s string 250 if in.Bool() { 251 s = "true" 252 } else { 253 s = "false" 254 } 255 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 256 } 257 258 func (e *encoder) intv(tag string, in reflect.Value) { 259 s := strconv.FormatInt(in.Int(), 10) 260 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 261 } 262 263 func (e *encoder) uintv(tag string, in reflect.Value) { 264 s := strconv.FormatUint(in.Uint(), 10) 265 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 266 } 267 268 func (e *encoder) floatv(tag string, in reflect.Value) { 269 // FIXME: Handle 64 bits here. 270 s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) 271 switch s { 272 case "+Inf": 273 s = ".inf" 274 case "-Inf": 275 s = "-.inf" 276 case "NaN": 277 s = ".nan" 278 } 279 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 280 } 281 282 func (e *encoder) nilv() { 283 e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 284 } 285 286 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 287 implicit := tag == "" 288 e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 289 e.emit() 290 }