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