github.com/go-xe2/third@v1.0.3/gopkg.in/yaml.v2/encode.go (about) 1 package yaml 2 3 import ( 4 "encoding" 5 "fmt" 6 "reflect" 7 "regexp" 8 "sort" 9 "strconv" 10 "strings" 11 "time" 12 ) 13 14 type encoder struct { 15 emitter yaml_emitter_t 16 event yaml_event_t 17 out []byte 18 flow bool 19 } 20 21 func newEncoder() (e *encoder) { 22 e = &encoder{} 23 e.must(yaml_emitter_initialize(&e.emitter)) 24 yaml_emitter_set_output_string(&e.emitter, &e.out) 25 yaml_emitter_set_unicode(&e.emitter, true) 26 e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) 27 e.emit() 28 e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) 29 e.emit() 30 return e 31 } 32 33 func (e *encoder) finish() { 34 e.must(yaml_document_end_event_initialize(&e.event, true)) 35 e.emit() 36 e.emitter.open_ended = false 37 e.must(yaml_stream_end_event_initialize(&e.event)) 38 e.emit() 39 } 40 41 func (e *encoder) destroy() { 42 yaml_emitter_delete(&e.emitter) 43 } 44 45 func (e *encoder) emit() { 46 // This will internally delete the e.event value. 47 if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { 48 e.must(false) 49 } 50 } 51 52 func (e *encoder) must(ok bool) { 53 if !ok { 54 msg := e.emitter.problem 55 if msg == "" { 56 msg = "unknown problem generating YAML content" 57 } 58 failf("%s", msg) 59 } 60 } 61 62 func (e *encoder) marshal(tag string, in reflect.Value) { 63 if !in.IsValid() { 64 e.nilv() 65 return 66 } 67 iface := in.Interface() 68 if m, ok := iface.(Marshaler); ok { 69 v, err := m.MarshalYAML() 70 if err != nil { 71 fail(err) 72 } 73 if v == nil { 74 e.nilv() 75 return 76 } 77 in = reflect.ValueOf(v) 78 } else 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 if sinfo.InlineMap >= 0 { 169 m := in.Field(sinfo.InlineMap) 170 if m.Len() > 0 { 171 e.flow = false 172 keys := keyList(m.MapKeys()) 173 sort.Sort(keys) 174 for _, k := range keys { 175 if _, found := sinfo.FieldsMap[k.String()]; found { 176 panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) 177 } 178 e.marshal("", k) 179 e.flow = false 180 e.marshal("", m.MapIndex(k)) 181 } 182 } 183 } 184 }) 185 } 186 187 func (e *encoder) mappingv(tag string, f func()) { 188 implicit := tag == "" 189 style := yaml_BLOCK_MAPPING_STYLE 190 if e.flow { 191 e.flow = false 192 style = yaml_FLOW_MAPPING_STYLE 193 } 194 e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 195 e.emit() 196 f() 197 e.must(yaml_mapping_end_event_initialize(&e.event)) 198 e.emit() 199 } 200 201 func (e *encoder) slicev(tag string, in reflect.Value) { 202 implicit := tag == "" 203 style := yaml_BLOCK_SEQUENCE_STYLE 204 if e.flow { 205 e.flow = false 206 style = yaml_FLOW_SEQUENCE_STYLE 207 } 208 e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 209 e.emit() 210 n := in.Len() 211 for i := 0; i < n; i++ { 212 e.marshal("", in.Index(i)) 213 } 214 e.must(yaml_sequence_end_event_initialize(&e.event)) 215 e.emit() 216 } 217 218 // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 219 // 220 // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 221 // in YAML 1.2 and by this package, but these should be marshalled quoted for 222 // the time being for compatibility with other parsers. 223 func isBase60Float(s string) (result bool) { 224 // Fast path. 225 if s == "" { 226 return false 227 } 228 c := s[0] 229 if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 230 return false 231 } 232 // Do the full match. 233 return base60float.MatchString(s) 234 } 235 236 // From http://yaml.org/type/float.html, except the regular expression there 237 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 238 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 239 240 func (e *encoder) stringv(tag string, in reflect.Value) { 241 var style yaml_scalar_style_t 242 s := in.String() 243 rtag, rs := resolve("", s) 244 if rtag == yaml_BINARY_TAG { 245 if tag == "" || tag == yaml_STR_TAG { 246 tag = rtag 247 s = rs.(string) 248 } else if tag == yaml_BINARY_TAG { 249 failf("explicitly tagged !!binary data must be base64-encoded") 250 } else { 251 failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 252 } 253 } 254 if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { 255 style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 256 } else if strings.Contains(s, "\n") { 257 style = yaml_LITERAL_SCALAR_STYLE 258 } else { 259 style = yaml_PLAIN_SCALAR_STYLE 260 } 261 e.emitScalar(s, "", tag, style) 262 } 263 264 func (e *encoder) boolv(tag string, in reflect.Value) { 265 var s string 266 if in.Bool() { 267 s = "true" 268 } else { 269 s = "false" 270 } 271 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 272 } 273 274 func (e *encoder) intv(tag string, in reflect.Value) { 275 s := strconv.FormatInt(in.Int(), 10) 276 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 277 } 278 279 func (e *encoder) uintv(tag string, in reflect.Value) { 280 s := strconv.FormatUint(in.Uint(), 10) 281 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 282 } 283 284 func (e *encoder) floatv(tag string, in reflect.Value) { 285 // FIXME: Handle 64 bits here. 286 s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) 287 switch s { 288 case "+Inf": 289 s = ".inf" 290 case "-Inf": 291 s = "-.inf" 292 case "NaN": 293 s = ".nan" 294 } 295 e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 296 } 297 298 func (e *encoder) nilv() { 299 e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 300 } 301 302 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 303 implicit := tag == "" 304 e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 305 e.emit() 306 }