package plist import ( "bytes" "errors" "io" "reflect" "runtime" ) type generator interface { generateDocument(cfValue) Indent(string) } // An Encoder writes a property list to an output stream. type Encoder struct { writer io.Writer format int indent string } // Encode writes the property list encoding of v to the stream. func (p *Encoder) Encode(v interface{}) (err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } err = r.(error) } }() pval := p.marshal(reflect.ValueOf(v)) if pval == nil { panic(errors.New("plist: no root element to encode")) } var g generator switch p.format { case XMLFormat: g = newXMLPlistGenerator(p.writer) case BinaryFormat, AutomaticFormat: g = newBplistGenerator(p.writer) case OpenStepFormat, GNUStepFormat: g = newTextPlistGenerator(p.writer, p.format) } g.Indent(p.indent) g.generateDocument(pval) return } // Indent turns on pretty-printing for the XML and Text property list formats. // Each element begins on a new line and is preceded by one or more copies of indent according to its nesting depth. func (p *Encoder) Indent(indent string) { p.indent = indent } // NewEncoder returns an Encoder that writes an XML property list to w. func NewEncoder(w io.Writer) *Encoder { return NewEncoderForFormat(w, XMLFormat) } // NewEncoderForFormat returns an Encoder that writes a property list to w in the specified format. // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat). func NewEncoderForFormat(w io.Writer, format int) *Encoder { return &Encoder{ writer: w, format: format, } } // NewBinaryEncoder returns an Encoder that writes a binary property list to w. func NewBinaryEncoder(w io.Writer) *Encoder { return NewEncoderForFormat(w, BinaryFormat) } // Marshal returns the property list encoding of v in the specified format. // // Pass AutomaticFormat to allow the library to choose the best encoding (currently BinaryFormat). // // Marshal traverses the value v recursively. // Any nil values encountered, other than the root, will be silently discarded as // the property list format bears no representation for nil values. // // Strings, integers of varying size, floats and booleans are encoded unchanged. // Strings bearing non-ASCII runes will be encoded differently depending upon the property list format: // UTF-8 for XML property lists and UTF-16 for binary property lists. // // Slice and Array values are encoded as property list arrays, except for // []byte values, which are encoded as data. // // Map values encode as dictionaries. The map's key type must be string; there is no provision for encoding non-string dictionary keys. // // Struct values are encoded as dictionaries, with only exported fields being serialized. Struct field encoding may be influenced with the use of tags. // The tag format is: // // `plist:"[,flags...]"` // // The following flags are supported: // // omitempty Only include the field if it is not set to the zero value for its type. // // If the key is "-", the field is ignored. // // Anonymous struct fields are encoded as if their exported fields were exposed via the outer struct. // // Pointer values encode as the value pointed to. // // Channel, complex and function values cannot be encoded. Any attempt to do so causes Marshal to return an error. func Marshal(v interface{}, format int) ([]byte, error) { return MarshalIndent(v, format, "") } // MarshalIndent works like Marshal, but each property list element // begins on a new line and is preceded by one or more copies of indent according to its nesting depth. func MarshalIndent(v interface{}, format int, indent string) ([]byte, error) { buf := &bytes.Buffer{} enc := NewEncoderForFormat(buf, format) enc.Indent(indent) if err := enc.Encode(v); err != nil { return nil, err } return buf.Bytes(), nil }