-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterface.go
156 lines (134 loc) · 4.6 KB
/
interface.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package dbus
import (
"cmp"
"context"
"errors"
"fmt"
"reflect"
"github.com/danderson/dbus/fragments"
)
// Interface is a set of methods, properties and signals offered by an
// [Object].
type Interface struct {
o Object
name string
}
// Conn returns the DBus connection associated with the interface.
func (f Interface) Conn() *Conn { return f.o.Conn() }
// Peer returns the Peer that is offering the interface.
func (f Interface) Peer() Peer { return f.o.Peer() }
// Object returns the Object that implements the interface.
func (f Interface) Object() Object { return f.o }
// Name returns the name of the interface.
func (f Interface) Name() string { return f.name }
func (f Interface) String() string {
if f.name == "" {
return fmt.Sprintf("%s:<no interface>", f.Object())
}
return fmt.Sprintf("%s:%s", f.Object(), f.name)
}
// Compare compares two interfaces, with the same convention as [cmp.Compare].
func (f Interface) Compare(other Interface) int {
if ret := f.Object().Compare(other.Object()); ret != 0 {
return ret
}
return cmp.Compare(f.Name(), other.Name())
}
// Call calls method on the interface with the given request body, and
// writes the response into response.
//
// This is a low-level calling API. It is the caller's responsibility
// to match the body and response types to the signature of the method
// being invoked. Body may be nil for methods that accept no
// parameters. Response may be nil for methods that return no values.
func (f Interface) Call(ctx context.Context, method string, body any, response any) error {
return f.Conn().call(ctx, f.Peer().Name(), f.Object().Path(), f.Name(), method, body, response, false)
}
// OneWay calls method on the interface with the given request body,
// and tells the peer not to send a reply.
//
// OneWay returns after the method call is successfully sent. Since
// the response is suppressed at the bus level, there is no way to
// know whether the call was delivered to anyone, or acted upon.
//
// This is a low-level calling API. It is the caller's responsibility
// to match the body to the signature of the method being
// invoked. Body may be nil for methods that accept no parameters.
func (f Interface) OneWay(ctx context.Context, method string, body any) error {
return f.Conn().call(ctx, f.Peer().Name(), f.Object().Path(), f.Name(), method, body, nil, true)
}
// GetProperty reads the value of the given property into val.
//
// It is the caller's responsibility to match the value's type to the
// type offered by the interface. val may also be of type *any to
// retrieve a property without knowing its type.
func (f Interface) GetProperty(ctx context.Context, name string, val any) error {
want := reflect.ValueOf(val)
if !want.IsValid() {
return errors.New("cannot read property into nil interface")
}
if want.Kind() != reflect.Pointer {
return errors.New("cannot read property into non-pointer")
}
if want.IsNil() {
return errors.New("cannot read property into nil pointer")
}
req := struct {
InterfaceName string
PropertyName string
}{f.name, name}
iface := f.Object().Interface(ifaceProps)
if want.Type().Elem() == reflect.TypeFor[any]() {
return iface.Call(ctx, "Get", req, val)
}
sig, err := signatureFor(want.Type(), nil)
if err != nil {
return fmt.Errorf("invalid property type %s: %w", want.Type(), err)
}
resp := propDecoder{
sig: sig,
out: val,
}
return iface.Call(ctx, "Get", req, &resp)
}
type propDecoder struct {
_ InlineLayout
sig Signature
out any
}
func (p *propDecoder) SignatureDBus() Signature { return p.sig }
func (p *propDecoder) UnmarshalDBus(ctx context.Context, d *fragments.Decoder) error {
var sig Signature
if err := d.Value(ctx, &sig); err != nil {
return err
}
if sig.String() != p.sig.String() {
return fmt.Errorf("property type %s is not assignable to %s", sig.Type(), p.sig.Type())
}
if err := d.Value(ctx, p.out); err != nil {
return err
}
return nil
}
// SetProperty sets the given property to value.
//
// It is the caller's responsibility to match the value's type to the
// type offered by the interface.
func (f Interface) SetProperty(ctx context.Context, name string, value any) error {
req := struct {
InterfaceName string
PropertyName string
Value any
}{f.name, name, value}
return f.Object().Interface(ifaceProps).Call(ctx, "Set", req, nil)
}
// GetAllProperties returns all the properties exported by the
// interface.
func (f Interface) GetAllProperties(ctx context.Context) (map[string]any, error) {
var resp map[string]any
err := f.Object().Interface(ifaceProps).Call(ctx, "GetAll", f.name, &resp)
if err != nil {
return nil, err
}
return resp, nil
}