123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /*
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package candiedyaml
- import (
- "bytes"
- "encoding/base64"
- "io"
- "math"
- "reflect"
- "regexp"
- "sort"
- "strconv"
- "time"
- )
- var (
- timeTimeType = reflect.TypeOf(time.Time{})
- marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
- numberType = reflect.TypeOf(Number(""))
- nonPrintable = regexp.MustCompile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD]")
- multiline = regexp.MustCompile("\n|\u0085|\u2028|\u2029")
- shortTags = map[string]string{
- yaml_NULL_TAG: "!!null",
- yaml_BOOL_TAG: "!!bool",
- yaml_STR_TAG: "!!str",
- yaml_INT_TAG: "!!int",
- yaml_FLOAT_TAG: "!!float",
- yaml_TIMESTAMP_TAG: "!!timestamp",
- yaml_SEQ_TAG: "!!seq",
- yaml_MAP_TAG: "!!map",
- yaml_BINARY_TAG: "!!binary",
- }
- )
- type Marshaler interface {
- MarshalYAML() (tag string, value interface{}, err error)
- }
- // An Encoder writes JSON objects to an output stream.
- type Encoder struct {
- w io.Writer
- emitter yaml_emitter_t
- event yaml_event_t
- flow bool
- err error
- IgnoreOmitEmpty bool
- }
- func Marshal(v interface{}) ([]byte, error) {
- b := bytes.Buffer{}
- e := NewEncoder(&b)
- err := e.Encode(v)
- return b.Bytes(), err
- }
- // NewEncoder returns a new encoder that writes to w.
- func NewEncoder(w io.Writer) *Encoder {
- e := &Encoder{w: w}
- yaml_emitter_initialize(&e.emitter)
- yaml_emitter_set_output_writer(&e.emitter, e.w)
- yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
- e.emit()
- yaml_document_start_event_initialize(&e.event, nil, nil, true)
- e.emit()
- return e
- }
- func (e *Encoder) Encode(v interface{}) (err error) {
- defer recovery(&err)
- if e.err != nil {
- return e.err
- }
- e.marshal("", reflect.ValueOf(v), true)
- yaml_document_end_event_initialize(&e.event, true)
- e.emit()
- e.emitter.open_ended = false
- yaml_stream_end_event_initialize(&e.event)
- e.emit()
- return nil
- }
- func (e *Encoder) emit() {
- if !yaml_emitter_emit(&e.emitter, &e.event) {
- panic("bad emit")
- }
- }
- func (e *Encoder) marshal(tag string, v reflect.Value, allowAddr bool) {
- vt := v.Type()
- if vt.Implements(marshalerType) {
- e.emitMarshaler(tag, v)
- return
- }
- if vt.Kind() != reflect.Ptr && allowAddr {
- if reflect.PtrTo(vt).Implements(marshalerType) {
- e.emitAddrMarshaler(tag, v)
- return
- }
- }
- switch v.Kind() {
- case reflect.Interface:
- if v.IsNil() {
- e.emitNil()
- } else {
- e.marshal(tag, v.Elem(), allowAddr)
- }
- case reflect.Map:
- e.emitMap(tag, v)
- case reflect.Ptr:
- if v.IsNil() {
- e.emitNil()
- } else {
- e.marshal(tag, v.Elem(), true)
- }
- case reflect.Struct:
- e.emitStruct(tag, v)
- case reflect.Slice:
- e.emitSlice(tag, v)
- case reflect.String:
- e.emitString(tag, v)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- e.emitInt(tag, v)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- e.emitUint(tag, v)
- case reflect.Float32, reflect.Float64:
- e.emitFloat(tag, v)
- case reflect.Bool:
- e.emitBool(tag, v)
- default:
- panic("Can't marshal type yet: " + v.Type().String())
- }
- }
- func (e *Encoder) emitMap(tag string, v reflect.Value) {
- e.mapping(tag, func() {
- var keys stringValues = v.MapKeys()
- sort.Sort(keys)
- for _, k := range keys {
- e.marshal("", k, true)
- e.marshal("", v.MapIndex(k), true)
- }
- })
- }
- func (e *Encoder) emitStruct(tag string, v reflect.Value) {
- if v.Type() == timeTimeType {
- e.emitTime(tag, v)
- return
- }
- fields := cachedTypeFields(v.Type())
- e.mapping(tag, func() {
- for _, f := range fields {
- fv := fieldByIndex(v, f.index)
- if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) && !e.IgnoreOmitEmpty {
- continue
- }
- e.marshal("", reflect.ValueOf(f.name), true)
- e.flow = f.flow
- e.marshal("", fv, true)
- }
- })
- }
- func (e *Encoder) emitTime(tag string, v reflect.Value) {
- t := v.Interface().(time.Time)
- bytes, _ := t.MarshalText()
- e.emitScalar(string(bytes), "", tag, yaml_PLAIN_SCALAR_STYLE)
- }
- func isEmptyValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- return v.Len() == 0
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Interface, reflect.Ptr:
- return v.IsNil()
- }
- return false
- }
- func (e *Encoder) mapping(tag string, f func()) {
- implicit := tag == ""
- style := yaml_BLOCK_MAPPING_STYLE
- if e.flow {
- e.flow = false
- style = yaml_FLOW_MAPPING_STYLE
- }
- yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
- e.emit()
- f()
- yaml_mapping_end_event_initialize(&e.event)
- e.emit()
- }
- func (e *Encoder) emitSlice(tag string, v reflect.Value) {
- if v.Type() == byteSliceType {
- e.emitBase64(tag, v)
- return
- }
- implicit := tag == ""
- style := yaml_BLOCK_SEQUENCE_STYLE
- if e.flow {
- e.flow = false
- style = yaml_FLOW_SEQUENCE_STYLE
- }
- yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
- e.emit()
- n := v.Len()
- for i := 0; i < n; i++ {
- e.marshal("", v.Index(i), true)
- }
- yaml_sequence_end_event_initialize(&e.event)
- e.emit()
- }
- func (e *Encoder) emitBase64(tag string, v reflect.Value) {
- if v.IsNil() {
- e.emitNil()
- return
- }
- s := v.Bytes()
- dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
- base64.StdEncoding.Encode(dst, s)
- e.emitScalar(string(dst), "", yaml_BINARY_TAG, yaml_DOUBLE_QUOTED_SCALAR_STYLE)
- }
- func (e *Encoder) emitString(tag string, v reflect.Value) {
- var style yaml_scalar_style_t
- s := v.String()
- if nonPrintable.MatchString(s) {
- e.emitBase64(tag, v)
- return
- }
- if v.Type() == numberType {
- style = yaml_PLAIN_SCALAR_STYLE
- } else {
- event := yaml_event_t{
- implicit: true,
- value: []byte(s),
- }
- rtag, _ := resolveInterface(event, false)
- if tag == "" && rtag != yaml_STR_TAG {
- style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
- } else if multiline.MatchString(s) {
- style = yaml_LITERAL_SCALAR_STYLE
- } else {
- style = yaml_PLAIN_SCALAR_STYLE
- }
- }
- e.emitScalar(s, "", tag, style)
- }
- func (e *Encoder) emitBool(tag string, v reflect.Value) {
- s := strconv.FormatBool(v.Bool())
- e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
- }
- func (e *Encoder) emitInt(tag string, v reflect.Value) {
- s := strconv.FormatInt(v.Int(), 10)
- e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
- }
- func (e *Encoder) emitUint(tag string, v reflect.Value) {
- s := strconv.FormatUint(v.Uint(), 10)
- e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
- }
- func (e *Encoder) emitFloat(tag string, v reflect.Value) {
- f := v.Float()
- var s string
- switch {
- case math.IsNaN(f):
- s = ".nan"
- case math.IsInf(f, 1):
- s = "+.inf"
- case math.IsInf(f, -1):
- s = "-.inf"
- default:
- s = strconv.FormatFloat(f, 'g', -1, v.Type().Bits())
- }
- e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
- }
- func (e *Encoder) emitNil() {
- e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
- }
- func (e *Encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
- implicit := tag == ""
- if !implicit {
- style = yaml_PLAIN_SCALAR_STYLE
- }
- stag := shortTags[tag]
- if stag == "" {
- stag = tag
- }
- yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(stag), []byte(value), implicit, implicit, style)
- e.emit()
- }
- func (e *Encoder) emitMarshaler(tag string, v reflect.Value) {
- if v.Kind() == reflect.Ptr && v.IsNil() {
- e.emitNil()
- return
- }
- m := v.Interface().(Marshaler)
- if m == nil {
- e.emitNil()
- return
- }
- t, val, err := m.MarshalYAML()
- if err != nil {
- panic(err)
- }
- if val == nil {
- e.emitNil()
- return
- }
- e.marshal(t, reflect.ValueOf(val), false)
- }
- func (e *Encoder) emitAddrMarshaler(tag string, v reflect.Value) {
- if !v.CanAddr() {
- e.marshal(tag, v, false)
- return
- }
- va := v.Addr()
- if va.IsNil() {
- e.emitNil()
- return
- }
- m := v.Interface().(Marshaler)
- t, val, err := m.MarshalYAML()
- if err != nil {
- panic(err)
- }
- if val == nil {
- e.emitNil()
- return
- }
- e.marshal(t, reflect.ValueOf(val), false)
- }
|