123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- package dbus
- import (
- "bytes"
- "fmt"
- "reflect"
- "sort"
- "strconv"
- )
- // Variant represents the D-Bus variant type.
- type Variant struct {
- sig Signature
- value interface{}
- }
- // MakeVariant converts the given value to a Variant. It panics if v cannot be
- // represented as a D-Bus type.
- func MakeVariant(v interface{}) Variant {
- return Variant{SignatureOf(v), v}
- }
- // ParseVariant parses the given string as a variant as described at
- // https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not
- // empty, it is taken to be the expected signature for the variant.
- func ParseVariant(s string, sig Signature) (Variant, error) {
- tokens := varLex(s)
- p := &varParser{tokens: tokens}
- n, err := varMakeNode(p)
- if err != nil {
- return Variant{}, err
- }
- if sig.str == "" {
- sig, err = varInfer(n)
- if err != nil {
- return Variant{}, err
- }
- }
- v, err := n.Value(sig)
- if err != nil {
- return Variant{}, err
- }
- return MakeVariant(v), nil
- }
- // format returns a formatted version of v and whether this string can be parsed
- // unambigously.
- func (v Variant) format() (string, bool) {
- switch v.sig.str[0] {
- case 'b', 'i':
- return fmt.Sprint(v.value), true
- case 'n', 'q', 'u', 'x', 't', 'd', 'h':
- return fmt.Sprint(v.value), false
- case 's':
- return strconv.Quote(v.value.(string)), true
- case 'o':
- return strconv.Quote(string(v.value.(ObjectPath))), false
- case 'g':
- return strconv.Quote(v.value.(Signature).str), false
- case 'v':
- s, unamb := v.value.(Variant).format()
- if !unamb {
- return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
- }
- return "<" + s + ">", true
- case 'y':
- return fmt.Sprintf("%#x", v.value.(byte)), false
- }
- rv := reflect.ValueOf(v.value)
- switch rv.Kind() {
- case reflect.Slice:
- if rv.Len() == 0 {
- return "[]", false
- }
- unamb := true
- buf := bytes.NewBuffer([]byte("["))
- for i := 0; i < rv.Len(); i++ {
- // TODO: slooow
- s, b := MakeVariant(rv.Index(i).Interface()).format()
- unamb = unamb && b
- buf.WriteString(s)
- if i != rv.Len()-1 {
- buf.WriteString(", ")
- }
- }
- buf.WriteByte(']')
- return buf.String(), unamb
- case reflect.Map:
- if rv.Len() == 0 {
- return "{}", false
- }
- unamb := true
- var buf bytes.Buffer
- kvs := make([]string, rv.Len())
- for i, k := range rv.MapKeys() {
- s, b := MakeVariant(k.Interface()).format()
- unamb = unamb && b
- buf.Reset()
- buf.WriteString(s)
- buf.WriteString(": ")
- s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
- unamb = unamb && b
- buf.WriteString(s)
- kvs[i] = buf.String()
- }
- buf.Reset()
- buf.WriteByte('{')
- sort.Strings(kvs)
- for i, kv := range kvs {
- if i > 0 {
- buf.WriteString(", ")
- }
- buf.WriteString(kv)
- }
- buf.WriteByte('}')
- return buf.String(), unamb
- }
- return `"INVALID"`, true
- }
- // Signature returns the D-Bus signature of the underlying value of v.
- func (v Variant) Signature() Signature {
- return v.sig
- }
- // String returns the string representation of the underlying value of v as
- // described at https://developer.gnome.org/glib/unstable/gvariant-text.html.
- func (v Variant) String() string {
- s, unamb := v.format()
- if !unamb {
- return "@" + v.sig.str + " " + s
- }
- return s
- }
- // Value returns the underlying value of v.
- func (v Variant) Value() interface{} {
- return v.value
- }
|