1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

core/server: add generic dbus service

- Add QML client with subscribe/introspect/getprop/setprop/call
- Add CLI helper `dms notify` that allows async calls with action
  handlers.
This commit is contained in:
bbedward
2026-01-17 22:04:58 -05:00
parent 53f5240d41
commit 162ec909da
20 changed files with 1403 additions and 287 deletions

View File

@@ -0,0 +1,69 @@
package dbusutil
import "github.com/godbus/dbus/v5"
func As[T any](v dbus.Variant) (T, bool) {
val, ok := v.Value().(T)
return val, ok
}
func AsOr[T any](v dbus.Variant, def T) T {
if val, ok := v.Value().(T); ok {
return val
}
return def
}
func Get[T any](m map[string]dbus.Variant, key string) (T, bool) {
v, ok := m[key]
if !ok {
var zero T
return zero, false
}
return As[T](v)
}
func GetOr[T any](m map[string]dbus.Variant, key string, def T) T {
v, ok := m[key]
if !ok {
return def
}
return AsOr(v, def)
}
func Normalize(v any) any {
switch val := v.(type) {
case dbus.Variant:
return Normalize(val.Value())
case dbus.ObjectPath:
return string(val)
case []dbus.ObjectPath:
result := make([]string, len(val))
for i, p := range val {
result[i] = string(p)
}
return result
case map[string]dbus.Variant:
result := make(map[string]any)
for k, vv := range val {
result[k] = Normalize(vv.Value())
}
return result
case []any:
result := make([]any, len(val))
for i, item := range val {
result[i] = Normalize(item)
}
return result
default:
return v
}
}
func NormalizeSlice(values []any) []any {
result := make([]any, len(values))
for i, v := range values {
result[i] = Normalize(v)
}
return result
}

View File

@@ -0,0 +1,155 @@
package dbusutil
import (
"testing"
"github.com/godbus/dbus/v5"
"github.com/stretchr/testify/assert"
)
func TestAs(t *testing.T) {
t.Run("string", func(t *testing.T) {
v := dbus.MakeVariant("hello")
val, ok := As[string](v)
assert.True(t, ok)
assert.Equal(t, "hello", val)
})
t.Run("bool", func(t *testing.T) {
v := dbus.MakeVariant(true)
val, ok := As[bool](v)
assert.True(t, ok)
assert.True(t, val)
})
t.Run("int32", func(t *testing.T) {
v := dbus.MakeVariant(int32(42))
val, ok := As[int32](v)
assert.True(t, ok)
assert.Equal(t, int32(42), val)
})
t.Run("wrong type", func(t *testing.T) {
v := dbus.MakeVariant("hello")
_, ok := As[int](v)
assert.False(t, ok)
})
}
func TestAsOr(t *testing.T) {
t.Run("exists", func(t *testing.T) {
v := dbus.MakeVariant("hello")
val := AsOr(v, "default")
assert.Equal(t, "hello", val)
})
t.Run("wrong type uses default", func(t *testing.T) {
v := dbus.MakeVariant(123)
val := AsOr(v, "default")
assert.Equal(t, "default", val)
})
}
func TestGet(t *testing.T) {
m := map[string]dbus.Variant{
"name": dbus.MakeVariant("test"),
"enabled": dbus.MakeVariant(true),
"count": dbus.MakeVariant(int32(5)),
}
t.Run("exists", func(t *testing.T) {
val, ok := Get[string](m, "name")
assert.True(t, ok)
assert.Equal(t, "test", val)
})
t.Run("missing key", func(t *testing.T) {
_, ok := Get[string](m, "missing")
assert.False(t, ok)
})
t.Run("wrong type", func(t *testing.T) {
_, ok := Get[int](m, "name")
assert.False(t, ok)
})
}
func TestGetOr(t *testing.T) {
m := map[string]dbus.Variant{
"name": dbus.MakeVariant("test"),
}
t.Run("exists", func(t *testing.T) {
val := GetOr(m, "name", "default")
assert.Equal(t, "test", val)
})
t.Run("missing uses default", func(t *testing.T) {
val := GetOr(m, "missing", "default")
assert.Equal(t, "default", val)
})
t.Run("wrong type uses default", func(t *testing.T) {
val := GetOr(m, "name", 42)
assert.Equal(t, 42, val)
})
}
func TestNormalize(t *testing.T) {
t.Run("variant unwrap", func(t *testing.T) {
v := dbus.MakeVariant("hello")
result := Normalize(v)
assert.Equal(t, "hello", result)
})
t.Run("nested variant", func(t *testing.T) {
v := dbus.MakeVariant(dbus.MakeVariant("nested"))
result := Normalize(v)
assert.Equal(t, "nested", result)
})
t.Run("object path", func(t *testing.T) {
v := dbus.ObjectPath("/org/test")
result := Normalize(v)
assert.Equal(t, "/org/test", result)
})
t.Run("object path slice", func(t *testing.T) {
v := []dbus.ObjectPath{"/org/a", "/org/b"}
result := Normalize(v)
assert.Equal(t, []string{"/org/a", "/org/b"}, result)
})
t.Run("variant map", func(t *testing.T) {
v := map[string]dbus.Variant{
"key": dbus.MakeVariant("value"),
}
result := Normalize(v)
expected := map[string]any{"key": "value"}
assert.Equal(t, expected, result)
})
t.Run("any slice", func(t *testing.T) {
v := []any{dbus.MakeVariant("a"), dbus.ObjectPath("/b")}
result := Normalize(v)
expected := []any{"a", "/b"}
assert.Equal(t, expected, result)
})
t.Run("passthrough primitives", func(t *testing.T) {
assert.Equal(t, "hello", Normalize("hello"))
assert.Equal(t, 42, Normalize(42))
assert.Equal(t, true, Normalize(true))
})
}
func TestNormalizeSlice(t *testing.T) {
input := []any{
dbus.MakeVariant("a"),
dbus.ObjectPath("/b"),
"c",
}
result := NormalizeSlice(input)
expected := []any{"a", "/b", "c"}
assert.Equal(t, expected, result)
}