go-tg-screenshot-bot
150 строк · 3.6 Кб
1package dbus
2
3import (
4"bytes"
5"fmt"
6"reflect"
7"sort"
8"strconv"
9)
10
11// Variant represents the D-Bus variant type.
12type Variant struct {
13sig Signature
14value interface{}
15}
16
17// MakeVariant converts the given value to a Variant. It panics if v cannot be
18// represented as a D-Bus type.
19func MakeVariant(v interface{}) Variant {
20return MakeVariantWithSignature(v, SignatureOf(v))
21}
22
23// MakeVariantWithSignature converts the given value to a Variant.
24func MakeVariantWithSignature(v interface{}, s Signature) Variant {
25return Variant{s, v}
26}
27
28// ParseVariant parses the given string as a variant as described at
29// https://developer.gnome.org/glib/stable/gvariant-text.html. If sig is not
30// empty, it is taken to be the expected signature for the variant.
31func ParseVariant(s string, sig Signature) (Variant, error) {
32tokens := varLex(s)
33p := &varParser{tokens: tokens}
34n, err := varMakeNode(p)
35if err != nil {
36return Variant{}, err
37}
38if sig.str == "" {
39sig, err = varInfer(n)
40if err != nil {
41return Variant{}, err
42}
43}
44v, err := n.Value(sig)
45if err != nil {
46return Variant{}, err
47}
48return MakeVariant(v), nil
49}
50
51// format returns a formatted version of v and whether this string can be parsed
52// unambiguously.
53func (v Variant) format() (string, bool) {
54switch v.sig.str[0] {
55case 'b', 'i':
56return fmt.Sprint(v.value), true
57case 'n', 'q', 'u', 'x', 't', 'd', 'h':
58return fmt.Sprint(v.value), false
59case 's':
60return strconv.Quote(v.value.(string)), true
61case 'o':
62return strconv.Quote(string(v.value.(ObjectPath))), false
63case 'g':
64return strconv.Quote(v.value.(Signature).str), false
65case 'v':
66s, unamb := v.value.(Variant).format()
67if !unamb {
68return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
69}
70return "<" + s + ">", true
71case 'y':
72return fmt.Sprintf("%#x", v.value.(byte)), false
73}
74rv := reflect.ValueOf(v.value)
75switch rv.Kind() {
76case reflect.Slice:
77if rv.Len() == 0 {
78return "[]", false
79}
80unamb := true
81buf := bytes.NewBuffer([]byte("["))
82for i := 0; i < rv.Len(); i++ {
83// TODO: slooow
84s, b := MakeVariant(rv.Index(i).Interface()).format()
85unamb = unamb && b
86buf.WriteString(s)
87if i != rv.Len()-1 {
88buf.WriteString(", ")
89}
90}
91buf.WriteByte(']')
92return buf.String(), unamb
93case reflect.Map:
94if rv.Len() == 0 {
95return "{}", false
96}
97unamb := true
98var buf bytes.Buffer
99kvs := make([]string, rv.Len())
100for i, k := range rv.MapKeys() {
101s, b := MakeVariant(k.Interface()).format()
102unamb = unamb && b
103buf.Reset()
104buf.WriteString(s)
105buf.WriteString(": ")
106s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
107unamb = unamb && b
108buf.WriteString(s)
109kvs[i] = buf.String()
110}
111buf.Reset()
112buf.WriteByte('{')
113sort.Strings(kvs)
114for i, kv := range kvs {
115if i > 0 {
116buf.WriteString(", ")
117}
118buf.WriteString(kv)
119}
120buf.WriteByte('}')
121return buf.String(), unamb
122}
123return `"INVALID"`, true
124}
125
126// Signature returns the D-Bus signature of the underlying value of v.
127func (v Variant) Signature() Signature {
128return v.sig
129}
130
131// String returns the string representation of the underlying value of v as
132// described at https://developer.gnome.org/glib/stable/gvariant-text.html.
133func (v Variant) String() string {
134s, unamb := v.format()
135if !unamb {
136return "@" + v.sig.str + " " + s
137}
138return s
139}
140
141// Value returns the underlying value of v.
142func (v Variant) Value() interface{} {
143return v.value
144}
145
146// Store converts the variant into a native go type using the same
147// mechanism as the "Store" function.
148func (v Variant) Store(value interface{}) error {
149return storeInterfaces(v.value, value)
150}
151