github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/gopkg.in/yaml.v2/encode.go (about) 1 package yaml 2 3 import ( 4 "encoding" 5 "fmt" 6 "io" 7 "reflect" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 "time" 13 "unicode/utf8" 14 ) 15 16 type encoder struct { 17 emitter yaml_emitter_t 18 event yaml_event_t 19 out []byte 20 flow bool 21 // doneInit holds whether the initial stream_start_event has been 22 // emitted. 23 doneInit bool 24 } 25 26 func newEncoder() *encoder { 27 e := &encoder{} 28 yaml_emitter_initialize(&e.emitter) 29 yaml_emitter_set_output_string(&e.emitter, &e.out) 30 yaml_emitter_set_unicode(&e.emitter, true) 31 return e 32 } 33 34 func newEncoderWithWriter(w io.Writer) *encoder { 35 e := &encoder{} 36 yaml_emitter_initialize(&e.emitter) 37 yaml_emitter_set_output_writer(&e.emitter, w) 38 yaml_emitter_set_unicode(&e.emitter, true) 39 return e 40 } 41 42 func (e *encoder) init() { 43 if e.doneInit { 44 return 45 } 46 yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) 47 e.emit() 48 e.doneInit = true 49 } 50 51 func (e *encoder) finish() { 52 e.emitter.open_ended = false 53 yaml_stream_end_event_initialize(&e.event) 54 e.emit() 55 } 56 57 func (e *encoder) destroy() { 58 yaml_emitter_delete(&e.emitter) 59 } 60 61 func (e *encoder) emit() { 62 // This will internally delete the e.event value. 63 e.must(yaml_emitter_emit(&e.emitter, &e.event)) 64 } 65 66 func (e *encoder) must(ok bool) { 67 if !ok { 68 msg := e.emitter.problem 69 if msg == "" { 70 msg = "unknown problem generating YAML content" 71 } 72 failf("%s", msg) 73 } 74 } 75 76 func (e *encoder) marshalDoc(tag string, in reflect.Value) { 77 e.init() 78 yaml_document_start_event_initialize(&e.event, nil, nil, true) 79 e.emit() 80 e.marshal(tag, in) 81 yaml_document_end_event_initialize(&e.event, true) 82 e.emit() 83 } 84 85 func (e *encoder) marshal(tag string, in reflect.Value) { 86 if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { 87 e.nilv() 88 return 89 } 90 iface := in.Interface() 91 switch m := iface.(type) { 92 case time.Time, *time.Time: 93 // Although time.Time implements TextMarshaler, 94 // we don't want to treat it as a string for YAML 95 // purposes because YAML has special support for 96 // timestamps. 97 case Marshaler: 98 v, err := m.MarshalYAML() 99 if err != nil { 100 fail(err) 101 } 102 if v == nil { 103 e.nilv() 104 return 105 } 106 in = reflect.ValueOf(v) 107 case encoding.TextMarshaler: 108 text, err := m.MarshalText() 109 if err != nil { 110 fail(err) 111 } 112 in = reflect.ValueOf(string(text)) 113 case nil: 114 e.nilv() 115 return 116 } 117 switch in.Kind() { 118 case reflect.Interface: 119 e.marshal(tag, in.Elem()) 120 case reflect.Map: 121 e.mapv(tag, in) 122 case reflect.Ptr: 123 if in.Type() == ptrTimeType { 124 e.timev(tag, in.Elem()) 125 } else { 126 e.marshal(tag, in.Elem()) 127 } 128 case reflect.Struct: 129 if in.Type() == timeType { 130 e.timev(tag, in) 131 } else { 132 e.structv(tag, in) 133 } 134 case reflect.Slice, reflect.Array: 135 if in.Type().Elem() == mapItemType { 136 e.itemsv(tag, in) 137 } else { 138 e.slicev(tag, in) 139 } 140 case reflect.String: 141 e.stringv(tag, in) 142 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 143 if in.Type() == durationType { 144 e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) 145 } else { 146 e.intv(tag, in) 147 } 148 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 149 e.uintv(tag, in) 150 case reflect.Float32, reflect.Float64: 151 e.floatv(tag, in) 152 case reflect.Bool: 153 e.boolv(tag, in) 154 default: 155 panic("cannot marshal type: " + in.Type().String()) 156 } 157 } 158 159 func (e *encoder) mapv(tag string, in reflect.Value) { 160 e.mappingv(tag, func() { 161 keys := keyList(in.MapKeys()) 162 sort.Sort(keys) 163 for _, k := range keys { 164 e.marshal("", k) 165 e.marshal("", in.MapIndex(k)) 166 } 167 }) 168 } 169 170 func (e *encoder) itemsv(tag string, in reflect.Value) { 171 e.mappingv(tag, func() { 172 slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) 173 for _, item := range slice { 174 e.marshal("", reflect.ValueOf(item.Key)) 175 e.marshal("", reflect.ValueOf(item.Value)) 176 } 177 }) 178 } 179 180 func (e *encoder) structv(tag string, in reflect.Value) { 181 sinfo, err := getStructInfo(in.Type()) 182 if err != nil { 183 panic(err) 184 } 185 e.mappingv(tag, func() { 186 for _, info := range sinfo.FieldsList { 187 var value reflect.Value 188 if info.Inline == nil { 189 value = in.Field(info.Num) 190 } else { 191 value = in.FieldByIndex(info.Inline) 192 } 193 if info.OmitEmpty && isZero(value) { 194 continue 195 } 196 e.marshal("", reflect.ValueOf(info.Key)) 197 e.flow = info.Flow 198 e.marshal("", value) 199 } 200 if sinfo.InlineMap >= 0 { 201 m := in.Field(sinfo.InlineMap) 202 if m.Len() > 0 { 203 e.flow = false 204 keys := keyList(m.MapKeys()) 205 sort.Sort(keys) 206 for _, k := range keys { 207 if _, found := sinfo.FieldsMap[k.String()]; found { 208 panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) 209 } 210 e.marshal("", k) 211 e.flow = false 212 e.marshal("", m.MapIndex(k)) 213 } 214 } 215 } 216 }) 217 } 218 219 func (e *encoder) mappingv(tag string, f func()) { 220 implicit := tag == "" 221 style := yaml_BLOCK_MAPPING_STYLE 222 if e.flow { 223 e.flow = false 224 style = yaml_FLOW_MAPPING_STYLE 225 } 226 yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) 227 e.emit() 228 f() 229 yaml_mapping_end_event_initialize(&e.event) 230 e.emit() 231 } 232 233 func (e *encoder) slicev(tag string, in reflect.Value) { 234 implicit := tag == "" 235 style := yaml_BLOCK_SEQUENCE_STYLE 236 if e.flow { 237 e.flow = false 238 style = yaml_FLOW_SEQUENCE_STYLE 239 } 240 e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 241 e.emit() 242 n := in.Len() 243 for i := 0; i < n; i++ { 244 e.marshal("", in.Index(i)) 245 } 246 e.must(yaml_sequence_end_event_initialize(&e.event)) 247 e.emit() 248 } 249 250 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 251 // 252 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 253 // in YAML 1.2 and by this package, but these should be marshalled quoted for 254 // the time being for compatibility with other parsers. 255 func isBase60Float(s string) (result bool) { 256 // Fast path. 257 if s == "" { 258 return false 259 } 260 c := s[0] 261 if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 262 return false 263 } 264 // Do the full match. 265 return base60float.MatchString(s) 266 } 267 268 // From http://yaml.org/type/float.html, except the regular expression there 269 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 270 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 271 272 func (e *encoder) stringv(tag string, in reflect.Value) { 273 var style yaml_scalar_style_t 274 s := in.String() 275 canUsePlain := true 276 switch { 277 case !utf8.ValidString(s): 278 if tag == yaml_BINARY_TAG { 279 failf("explicitly tagged !!binary data must be base64-encoded") 280 } 281 if tag != "" { 282 failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 283 } 284 // It can't be encoded directly as YAML so use a binary tag 285 // and encode it as base64. 286 tag = yaml_BINARY_TAG 287 s = encodeBase64(s) 288 case tag == "": 289 // Check to see if it would resolve to a specific 290 // tag when encoded unquoted. If it doesn't, 291 // there's no need to quote it. 292 rtag, _ := resolve("", s) 293 canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) 294 } 295 // Note: it's possible for user code to emit invalid YAML 296 // if they explicitly specify a tag and a string containing 297 // text that's incompatible with that tag. 298 switch { 299 case strings.Contains(s, "\n"): 300 style = yaml_LITERAL_SCALAR_STYLE 301 case canUsePlain: 302 style = yaml_PLAIN_SCALAR_STYLE 303 default: 304 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 305 } 306 e.emitScalar(s, "", tag, style) 307 } 308 309 func (e *encoder) boolv(tag string, in reflect.Value) { 310 var s string 311 if in.Bool() { 312 s = "true" 313 } else { 314 s = "false" 315 } 316 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 317 } 318 319 func (e *encoder) intv(tag string, in reflect.Value) { 320 s := strconv.FormatInt(in.Int(), 10) 321 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 322 } 323 324 func (e *encoder) uintv(tag string, in reflect.Value) { 325 s := strconv.FormatUint(in.Uint(), 10) 326 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 327 } 328 329 func (e *encoder) timev(tag string, in reflect.Value) { 330 t := in.Interface().(time.Time) 331 s := t.Format(time.RFC3339Nano) 332 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 333 } 334 335 func (e *encoder) floatv(tag string, in reflect.Value) { 336 // Issue #352: When formatting, use the precision of the underlying value 337 precision := 64 338 if in.Kind() == reflect.Float32 { 339 precision = 32 340 } 341 342 s := strconv.FormatFloat(in.Float(), 'g', -1, precision) 343 switch s { 344 case "+Inf": 345 s = ".inf" 346 case "-Inf": 347 s = "-.inf" 348 case "NaN": 349 s = ".nan" 350 } 351 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 352 } 353 354 func (e *encoder) nilv() { 355 e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 356 } 357 358 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 359 implicit := tag == "" 360 e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 361 e.emit() 362 }