1package dbus
2
3import (
4	"fmt"
5	"reflect"
6	"strings"
7)
8
9var sigToType = map[byte]reflect.Type{
10	'y': byteType,
11	'b': boolType,
12	'n': int16Type,
13	'q': uint16Type,
14	'i': int32Type,
15	'u': uint32Type,
16	'x': int64Type,
17	't': uint64Type,
18	'd': float64Type,
19	's': stringType,
20	'g': signatureType,
21	'o': objectPathType,
22	'v': variantType,
23	'h': unixFDIndexType,
24}
25
26// Signature represents a correct type signature as specified by the D-Bus
27// specification. The zero value represents the empty signature, "".
28type Signature struct {
29	str string
30}
31
32// SignatureOf returns the concatenation of all the signatures of the given
33// values. It panics if one of them is not representable in D-Bus.
34func SignatureOf(vs ...interface{}) Signature {
35	var s string
36	for _, v := range vs {
37		s += getSignature(reflect.TypeOf(v))
38	}
39	return Signature{s}
40}
41
42// SignatureOfType returns the signature of the given type. It panics if the
43// type is not representable in D-Bus.
44func SignatureOfType(t reflect.Type) Signature {
45	return Signature{getSignature(t)}
46}
47
48// getSignature returns the signature of the given type and panics on unknown types.
49func getSignature(t reflect.Type) string {
50	// handle simple types first
51	switch t.Kind() {
52	case reflect.Uint8:
53		return "y"
54	case reflect.Bool:
55		return "b"
56	case reflect.Int16:
57		return "n"
58	case reflect.Uint16:
59		return "q"
60	case reflect.Int, reflect.Int32:
61		if t == unixFDType {
62			return "h"
63		}
64		return "i"
65	case reflect.Uint, reflect.Uint32:
66		if t == unixFDIndexType {
67			return "h"
68		}
69		return "u"
70	case reflect.Int64:
71		return "x"
72	case reflect.Uint64:
73		return "t"
74	case reflect.Float64:
75		return "d"
76	case reflect.Ptr:
77		return getSignature(t.Elem())
78	case reflect.String:
79		if t == objectPathType {
80			return "o"
81		}
82		return "s"
83	case reflect.Struct:
84		if t == variantType {
85			return "v"
86		} else if t == signatureType {
87			return "g"
88		}
89		var s string
90		for i := 0; i < t.NumField(); i++ {
91			field := t.Field(i)
92			if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
93				s += getSignature(t.Field(i).Type)
94			}
95		}
96		return "(" + s + ")"
97	case reflect.Array, reflect.Slice:
98		return "a" + getSignature(t.Elem())
99	case reflect.Map:
100		if !isKeyType(t.Key()) {
101			panic(InvalidTypeError{t})
102		}
103		return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
104	case reflect.Interface:
105		return "v"
106	}
107	panic(InvalidTypeError{t})
108}
109
110// ParseSignature returns the signature represented by this string, or a
111// SignatureError if the string is not a valid signature.
112func ParseSignature(s string) (sig Signature, err error) {
113	if len(s) == 0 {
114		return
115	}
116	if len(s) > 255 {
117		return Signature{""}, SignatureError{s, "too long"}
118	}
119	sig.str = s
120	for err == nil && len(s) != 0 {
121		err, s = validSingle(s, 0)
122	}
123	if err != nil {
124		sig = Signature{""}
125	}
126
127	return
128}
129
130// ParseSignatureMust behaves like ParseSignature, except that it panics if s
131// is not valid.
132func ParseSignatureMust(s string) Signature {
133	sig, err := ParseSignature(s)
134	if err != nil {
135		panic(err)
136	}
137	return sig
138}
139
140// Empty retruns whether the signature is the empty signature.
141func (s Signature) Empty() bool {
142	return s.str == ""
143}
144
145// Single returns whether the signature represents a single, complete type.
146func (s Signature) Single() bool {
147	err, r := validSingle(s.str, 0)
148	return err != nil && r == ""
149}
150
151// String returns the signature's string representation.
152func (s Signature) String() string {
153	return s.str
154}
155
156// A SignatureError indicates that a signature passed to a function or received
157// on a connection is not a valid signature.
158type SignatureError struct {
159	Sig    string
160	Reason string
161}
162
163func (e SignatureError) Error() string {
164	return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
165}
166
167// Try to read a single type from this string. If it was successful, err is nil
168// and rem is the remaining unparsed part. Otherwise, err is a non-nil
169// SignatureError and rem is "". depth is the current recursion depth which may
170// not be greater than 64 and should be given as 0 on the first call.
171func validSingle(s string, depth int) (err error, rem string) {
172	if s == "" {
173		return SignatureError{Sig: s, Reason: "empty signature"}, ""
174	}
175	if depth > 64 {
176		return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
177	}
178	switch s[0] {
179	case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
180		return nil, s[1:]
181	case 'a':
182		if len(s) > 1 && s[1] == '{' {
183			i := findMatching(s[1:], '{', '}')
184			if i == -1 {
185				return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
186			}
187			i++
188			rem = s[i+1:]
189			s = s[2:i]
190			if err, _ = validSingle(s[:1], depth+1); err != nil {
191				return err, ""
192			}
193			err, nr := validSingle(s[1:], depth+1)
194			if err != nil {
195				return err, ""
196			}
197			if nr != "" {
198				return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
199			}
200			return nil, rem
201		}
202		return validSingle(s[1:], depth+1)
203	case '(':
204		i := findMatching(s, '(', ')')
205		if i == -1 {
206			return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
207		}
208		rem = s[i+1:]
209		s = s[1:i]
210		for err == nil && s != "" {
211			err, s = validSingle(s, depth+1)
212		}
213		if err != nil {
214			rem = ""
215		}
216		return
217	}
218	return SignatureError{Sig: s, Reason: "invalid type character"}, ""
219}
220
221func findMatching(s string, left, right rune) int {
222	n := 0
223	for i, v := range s {
224		if v == left {
225			n++
226		} else if v == right {
227			n--
228		}
229		if n == 0 {
230			return i
231		}
232	}
233	return -1
234}
235
236// typeFor returns the type of the given signature. It ignores any left over
237// characters and panics if s doesn't start with a valid type signature.
238func typeFor(s string) (t reflect.Type) {
239	err, _ := validSingle(s, 0)
240	if err != nil {
241		panic(err)
242	}
243
244	if t, ok := sigToType[s[0]]; ok {
245		return t
246	}
247	switch s[0] {
248	case 'a':
249		if s[1] == '{' {
250			i := strings.LastIndex(s, "}")
251			t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
252		} else {
253			t = reflect.SliceOf(typeFor(s[1:]))
254		}
255	case '(':
256		t = interfacesType
257	}
258	return
259}
260