variant.go 3.4 KB

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