1package xmlrpc
2
3import (
4	"bytes"
5	"encoding/xml"
6	"fmt"
7	"reflect"
8	"strconv"
9	"time"
10)
11
12type encodeFunc func(reflect.Value) ([]byte, error)
13
14func marshal(v interface{}) ([]byte, error) {
15	if v == nil {
16		return []byte{}, nil
17	}
18
19	val := reflect.ValueOf(v)
20	return encodeValue(val)
21}
22
23func encodeValue(val reflect.Value) ([]byte, error) {
24	var b []byte
25	var err error
26
27	if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
28		if val.IsNil() {
29			return []byte("<value/>"), nil
30		}
31
32		val = val.Elem()
33	}
34
35	switch val.Kind() {
36	case reflect.Struct:
37		switch val.Interface().(type) {
38		case time.Time:
39			t := val.Interface().(time.Time)
40			b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
41		default:
42			b, err = encodeStruct(val)
43		}
44	case reflect.Map:
45		b, err = encodeMap(val)
46	case reflect.Slice:
47		b, err = encodeSlice(val)
48	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
49		b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
50	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
51		b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
52	case reflect.Float32, reflect.Float64:
53		b = []byte(fmt.Sprintf("<double>%s</double>",
54			strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
55	case reflect.Bool:
56		if val.Bool() {
57			b = []byte("<boolean>1</boolean>")
58		} else {
59			b = []byte("<boolean>0</boolean>")
60		}
61	case reflect.String:
62		var buf bytes.Buffer
63
64		xml.Escape(&buf, []byte(val.String()))
65
66		if _, ok := val.Interface().(Base64); ok {
67			b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
68		} else {
69			b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
70		}
71	default:
72		return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
73	}
74
75	if err != nil {
76		return nil, err
77	}
78
79	return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
80}
81
82func encodeStruct(val reflect.Value) ([]byte, error) {
83	var b bytes.Buffer
84
85	b.WriteString("<struct>")
86
87	t := val.Type()
88	for i := 0; i < t.NumField(); i++ {
89		b.WriteString("<member>")
90		f := t.Field(i)
91
92		name := f.Tag.Get("xmlrpc")
93		if name == "" {
94			name = f.Name
95		}
96		b.WriteString(fmt.Sprintf("<name>%s</name>", name))
97
98		p, err := encodeValue(val.FieldByName(f.Name))
99		if err != nil {
100			return nil, err
101		}
102		b.Write(p)
103
104		b.WriteString("</member>")
105	}
106
107	b.WriteString("</struct>")
108
109	return b.Bytes(), nil
110}
111
112func encodeMap(val reflect.Value) ([]byte, error) {
113	var t = val.Type()
114
115	if t.Key().Kind() != reflect.String {
116		return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
117	}
118
119	var b bytes.Buffer
120
121	b.WriteString("<struct>")
122
123	keys := val.MapKeys()
124
125	for i := 0; i < val.Len(); i++ {
126		key := keys[i]
127		kval := val.MapIndex(key)
128
129		b.WriteString("<member>")
130		b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
131
132		p, err := encodeValue(kval)
133
134		if err != nil {
135			return nil, err
136		}
137
138		b.Write(p)
139		b.WriteString("</member>")
140	}
141
142	b.WriteString("</struct>")
143
144	return b.Bytes(), nil
145}
146
147func encodeSlice(val reflect.Value) ([]byte, error) {
148	var b bytes.Buffer
149
150	b.WriteString("<array><data>")
151
152	for i := 0; i < val.Len(); i++ {
153		p, err := encodeValue(val.Index(i))
154		if err != nil {
155			return nil, err
156		}
157
158		b.Write(p)
159	}
160
161	b.WriteString("</data></array>")
162
163	return b.Bytes(), nil
164}
165