ruby-dbus 0.17.0 → 0.18.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +38 -0
  3. data/README.md +1 -1
  4. data/Rakefile +3 -11
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +10 -3
  7. data/examples/doc/_extract_examples +2 -0
  8. data/examples/gdbus/gdbus +11 -5
  9. data/examples/no-introspect/nm-test.rb +2 -0
  10. data/examples/no-introspect/tracker-test.rb +3 -1
  11. data/examples/rhythmbox/playpause.rb +2 -1
  12. data/examples/service/call_service.rb +1 -0
  13. data/examples/service/complex-property.rb +21 -0
  14. data/examples/service/service_newapi.rb +1 -1
  15. data/examples/simple/call_introspect.rb +1 -0
  16. data/examples/simple/get_id.rb +2 -1
  17. data/examples/simple/properties.rb +2 -0
  18. data/examples/utils/listnames.rb +1 -0
  19. data/examples/utils/notify.rb +1 -0
  20. data/lib/dbus/api_options.rb +9 -0
  21. data/lib/dbus/auth.rb +12 -7
  22. data/lib/dbus/bus.rb +114 -70
  23. data/lib/dbus/bus_name.rb +12 -8
  24. data/lib/dbus/data.rb +744 -0
  25. data/lib/dbus/error.rb +4 -2
  26. data/lib/dbus/introspect.rb +30 -18
  27. data/lib/dbus/logger.rb +3 -1
  28. data/lib/dbus/marshall.rb +229 -293
  29. data/lib/dbus/matchrule.rb +16 -16
  30. data/lib/dbus/message.rb +44 -37
  31. data/lib/dbus/message_queue.rb +10 -5
  32. data/lib/dbus/object.rb +36 -15
  33. data/lib/dbus/object_path.rb +11 -6
  34. data/lib/dbus/proxy_object.rb +18 -4
  35. data/lib/dbus/proxy_object_factory.rb +11 -7
  36. data/lib/dbus/proxy_object_interface.rb +26 -22
  37. data/lib/dbus/raw_message.rb +91 -0
  38. data/lib/dbus/type.rb +164 -80
  39. data/lib/dbus/xml.rb +28 -17
  40. data/lib/dbus.rb +13 -7
  41. data/ruby-dbus.gemspec +4 -2
  42. data/spec/async_spec.rb +2 -0
  43. data/spec/binding_spec.rb +2 -0
  44. data/spec/bus_and_xml_backend_spec.rb +2 -0
  45. data/spec/bus_driver_spec.rb +2 -0
  46. data/spec/bus_name_spec.rb +3 -1
  47. data/spec/bus_spec.rb +2 -0
  48. data/spec/byte_array_spec.rb +2 -0
  49. data/spec/client_robustness_spec.rb +4 -2
  50. data/spec/data/marshall.yaml +1639 -0
  51. data/spec/data_spec.rb +353 -0
  52. data/spec/err_msg_spec.rb +2 -0
  53. data/spec/introspect_xml_parser_spec.rb +2 -0
  54. data/spec/introspection_spec.rb +2 -0
  55. data/spec/main_loop_spec.rb +2 -0
  56. data/spec/node_spec.rb +23 -0
  57. data/spec/object_path_spec.rb +3 -0
  58. data/spec/packet_marshaller_spec.rb +34 -0
  59. data/spec/packet_unmarshaller_spec.rb +262 -0
  60. data/spec/property_spec.rb +60 -2
  61. data/spec/proxy_object_spec.rb +2 -0
  62. data/spec/server_robustness_spec.rb +2 -0
  63. data/spec/server_spec.rb +2 -0
  64. data/spec/service_newapi.rb +37 -4
  65. data/spec/session_bus_spec.rb +3 -1
  66. data/spec/session_bus_spec_manual.rb +2 -0
  67. data/spec/signal_spec.rb +2 -0
  68. data/spec/spec_helper.rb +19 -3
  69. data/spec/thread_safety_spec.rb +2 -0
  70. data/spec/type_spec.rb +69 -6
  71. data/spec/value_spec.rb +16 -1
  72. data/spec/variant_spec.rb +4 -2
  73. data/spec/zzz_quit_spec.rb +16 -0
  74. metadata +16 -7
data/lib/dbus/marshall.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # dbus.rb - Module containing the low-level D-Bus implementation
2
4
  #
3
5
  # This file is part of the ruby-dbus project
@@ -10,6 +12,8 @@
10
12
 
11
13
  require "socket"
12
14
 
15
+ require_relative "../dbus/type"
16
+
13
17
  # = D-Bus main module
14
18
  #
15
19
  # Module containing all the D-Bus modules and classes.
@@ -21,211 +25,134 @@ module DBus
21
25
  # = D-Bus packet unmarshaller class
22
26
  #
23
27
  # Class that handles the conversion (unmarshalling) of payload data
24
- # to Array.
28
+ # to #{::Object}s (in **plain** mode) or to {Data::Base} (in **exact** mode)
29
+ #
30
+ # Spelling note: this codebase always uses a double L
31
+ # in the "marshall" word and its inflections.
25
32
  class PacketUnmarshaller
26
- # Index pointer that points to the byte in the data that is
27
- # currently being processed.
28
- #
29
- # Used to kown what part of the buffer has been consumed by unmarshalling.
30
- # FIXME: Maybe should be accessed with a "consumed_size" method.
31
- attr_reader :idx
32
-
33
- # Create a new unmarshaller for the given data _buffer_ and _endianness_.
33
+ # Create a new unmarshaller for the given data *buffer*.
34
+ # @param buffer [String]
35
+ # @param endianness [:little,:big]
34
36
  def initialize(buffer, endianness)
35
- @buffy = buffer.dup
36
- @endianness = endianness
37
- if @endianness == BIG_END
38
- @uint32 = "N"
39
- @uint16 = "n"
40
- @double = "G"
41
- elsif @endianness == LIL_END
42
- @uint32 = "V"
43
- @uint16 = "v"
44
- @double = "E"
45
- else
46
- raise InvalidPacketException, "Incorrect endianness #{@endianness}"
47
- end
48
- @idx = 0
37
+ # TODO: this dup can be avoided if we can prove
38
+ # that an IncompleteBufferException leaves the original *buffer* intact
39
+ buffer = buffer.dup
40
+ @raw_msg = RawMessage.new(buffer, endianness)
49
41
  end
50
42
 
51
43
  # Unmarshall the buffer for a given _signature_ and length _len_.
52
- # Return an array of unmarshalled objects
53
- def unmarshall(signature, len = nil)
54
- if !len.nil?
55
- if @buffy.bytesize < @idx + len
56
- raise IncompleteBufferException
57
- end
58
- end
44
+ # Return an array of unmarshalled objects.
45
+ # @param signature [Signature]
46
+ # @param len [Integer,nil] if given, and there is not enough data
47
+ # in the buffer, raise {IncompleteBufferException}
48
+ # @param mode [:plain,:exact]
49
+ # @return [Array<::Object,DBus::Data::Base>]
50
+ # Objects in `:plain` mode, {DBus::Data::Base} in `:exact` mode
51
+ # The array size corresponds to the number of types in *signature*.
52
+ # @raise IncompleteBufferException
53
+ # @raise InvalidPacketException
54
+ def unmarshall(signature, len = nil, mode: :plain)
55
+ @raw_msg.want!(len) if len
56
+
59
57
  sigtree = Type::Parser.new(signature).parse
60
58
  ret = []
61
59
  sigtree.each do |elem|
62
- ret << do_parse(elem)
60
+ ret << do_parse(elem, mode: mode)
63
61
  end
64
62
  ret
65
63
  end
66
64
 
67
- # Align the pointer index on a byte index of _a_, where a
68
- # must be 1, 2, 4 or 8.
69
- def align(a)
70
- case a
71
- when 1
72
- nil
73
- when 2, 4, 8
74
- bits = a - 1
75
- @idx = @idx + bits & ~bits
76
- raise IncompleteBufferException if @idx > @buffy.bytesize
77
- else
78
- raise "Unsupported alignment #{a}"
79
- end
65
+ # after the headers, the body starts 8-aligned
66
+ def align_body
67
+ @raw_msg.align(8)
80
68
  end
81
69
 
82
- ###############################################################
83
- # FIXME: does anyone except the object itself call the above methods?
84
- # Yes : Message marshalling code needs to align "body" to 8 byte boundary
85
- private
86
-
87
- # Retrieve the next _nbytes_ number of bytes from the buffer.
88
- def read(nbytes)
89
- raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
90
- ret = @buffy.slice(@idx, nbytes)
91
- @idx += nbytes
92
- ret
70
+ # @return [Integer]
71
+ def consumed_size
72
+ @raw_msg.pos
93
73
  end
94
74
 
95
- # Read the string length and string itself from the buffer.
96
- # Return the string.
97
- def read_string
98
- align(4)
99
- str_sz = read(4).unpack(@uint32)[0]
100
- ret = @buffy.slice(@idx, str_sz)
101
- raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
102
- @idx += str_sz
103
- if @buffy[@idx].ord != 0
104
- raise InvalidPacketException, "String is not nul-terminated"
105
- end
106
- @idx += 1
107
- # no exception, see check above
108
- ret
109
- end
75
+ private
110
76
 
111
- # Read the signature length and signature itself from the buffer.
112
- # Return the signature.
113
- def read_signature
114
- str_sz = read(1).unpack("C")[0]
115
- ret = @buffy.slice(@idx, str_sz)
116
- raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
117
- @idx += str_sz
118
- if @buffy[@idx].ord != 0
119
- raise InvalidPacketException, "Type is not nul-terminated"
120
- end
121
- @idx += 1
122
- # no exception, see check above
123
- ret
77
+ # @param data_class [Class] a subclass of Data::Base (specific?)
78
+ # @return [::Integer,::Float]
79
+ def aligned_read_value(data_class)
80
+ @raw_msg.align(data_class.alignment)
81
+ bytes = @raw_msg.read(data_class.alignment)
82
+ bytes.unpack1(data_class.format[@raw_msg.endianness])
124
83
  end
125
84
 
126
85
  # Based on the _signature_ type, retrieve a packet from the buffer
127
86
  # and return it.
128
- def do_parse(signature)
87
+ # @param signature [Type]
88
+ # @param mode [:plain,:exact]
89
+ # @return [Data::Base]
90
+ def do_parse(signature, mode: :plain)
91
+ # FIXME: better naming for packet vs value
129
92
  packet = nil
130
- case signature.sigtype
131
- when Type::BYTE
132
- packet = read(1).unpack("C")[0]
133
- when Type::UINT16
134
- align(2)
135
- packet = read(2).unpack(@uint16)[0]
136
- when Type::INT16
137
- align(2)
138
- packet = read(2).unpack(@uint16)[0]
139
- if (packet & 0x8000) != 0
140
- packet -= 0x10000
141
- end
142
- when Type::UINT32, Type::UNIX_FD
143
- align(4)
144
- packet = read(4).unpack(@uint32)[0]
145
- when Type::INT32
146
- align(4)
147
- packet = read(4).unpack(@uint32)[0]
148
- if (packet & 0x80000000) != 0
149
- packet -= 0x100000000
150
- end
151
- when Type::UINT64
152
- align(8)
153
- packet_l = read(4).unpack(@uint32)[0]
154
- packet_h = read(4).unpack(@uint32)[0]
155
- packet = if @endianness == LIL_END
156
- packet_l + packet_h * 2**32
157
- else
158
- packet_l * 2**32 + packet_h
159
- end
160
- when Type::INT64
161
- align(8)
162
- packet_l = read(4).unpack(@uint32)[0]
163
- packet_h = read(4).unpack(@uint32)[0]
164
- packet = if @endianness == LIL_END
165
- packet_l + packet_h * 2**32
166
- else
167
- packet_l * 2**32 + packet_h
168
- end
169
- if (packet & 0x8000000000000000) != 0
170
- packet -= 0x10000000000000000
171
- end
172
- when Type::DOUBLE
173
- align(8)
174
- packet = read(8).unpack(@double)[0]
175
- when Type::BOOLEAN
176
- align(4)
177
- v = read(4).unpack(@uint32)[0]
178
- raise InvalidPacketException if ![0, 1].member?(v)
179
- packet = (v == 1)
180
- when Type::ARRAY
181
- align(4)
182
- # checks please
183
- array_sz = read(4).unpack(@uint32)[0]
184
- raise InvalidPacketException if array_sz > 67_108_864
185
-
186
- align(signature.child.alignment)
187
- raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
188
-
189
- packet = []
190
- start_idx = @idx
191
- while @idx - start_idx < array_sz
192
- packet << do_parse(signature.child)
193
- end
93
+ data_class = Data::BY_TYPE_CODE[signature.sigtype]
194
94
 
195
- if signature.child.sigtype == Type::DICT_ENTRY
196
- packet = Hash[packet]
197
- end
198
- when Type::STRUCT
199
- align(8)
200
- packet = []
201
- signature.members.each do |elem|
202
- packet << do_parse(elem)
203
- end
204
- when Type::VARIANT
205
- string = read_signature
206
- # error checking please
207
- sig = Type::Parser.new(string).parse[0]
208
- align(sig.alignment)
209
- packet = do_parse(sig)
210
- when Type::OBJECT_PATH
211
- packet = read_string
212
- when Type::STRING
213
- packet = read_string
214
- packet.force_encoding("UTF-8")
215
- when Type::SIGNATURE
216
- packet = read_signature
217
- when Type::DICT_ENTRY
218
- align(8)
219
- key = do_parse(signature.members[0])
220
- value = do_parse(signature.members[1])
221
- packet = [key, value]
222
- else
95
+ if data_class.nil?
223
96
  raise NotImplementedError,
224
97
  "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
225
98
  end
99
+
100
+ if data_class.fixed?
101
+ value = aligned_read_value(data_class)
102
+ packet = data_class.from_raw(value, mode: mode)
103
+ elsif data_class.basic?
104
+ size = aligned_read_value(data_class.size_class)
105
+ # @raw_msg.align(data_class.alignment)
106
+ # ^ is not necessary because we've just read a suitably-aligned *size*
107
+ value = @raw_msg.read(size)
108
+ nul = @raw_msg.read(1)
109
+ if nul != "\u0000"
110
+ raise InvalidPacketException, "#{data_class} is not NUL-terminated"
111
+ end
112
+
113
+ packet = data_class.from_raw(value, mode: mode)
114
+ else
115
+ @raw_msg.align(data_class.alignment)
116
+ case signature.sigtype
117
+ when Type::STRUCT, Type::DICT_ENTRY
118
+ values = signature.members.map do |child_sig|
119
+ do_parse(child_sig, mode: mode)
120
+ end
121
+ packet = data_class.from_items(values, mode: mode, member_types: signature.members)
122
+
123
+ when Type::VARIANT
124
+ data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature
125
+ types = Type::Parser.new(data_sig.value).parse # -> Array<Type>
126
+ unless types.size == 1
127
+ raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found"
128
+ end
129
+
130
+ type = types.first
131
+ value = do_parse(type, mode: mode)
132
+ packet = data_class.from_items(value, mode: mode, member_type: type)
133
+
134
+ when Type::ARRAY
135
+ array_bytes = aligned_read_value(Data::UInt32)
136
+ if array_bytes > 67_108_864
137
+ raise InvalidPacketException, "ARRAY body longer than 64MiB"
138
+ end
139
+
140
+ # needed here because of empty arrays
141
+ @raw_msg.align(signature.child.alignment)
142
+
143
+ items = []
144
+ end_pos = @raw_msg.pos + array_bytes
145
+ while @raw_msg.pos < end_pos
146
+ item = do_parse(signature.child, mode: mode)
147
+ items << item
148
+ end
149
+ is_hash = signature.child.sigtype == Type::DICT_ENTRY
150
+ packet = data_class.from_items(items, mode: mode, member_type: signature.child, hash: is_hash)
151
+ end
152
+ end
226
153
  packet
227
- end # def do_parse
228
- end # class PacketUnmarshaller
154
+ end
155
+ end
229
156
 
230
157
  # D-Bus packet marshaller class
231
158
  #
@@ -234,40 +161,35 @@ module DBus
234
161
  class PacketMarshaller
235
162
  # The current or result packet.
236
163
  # FIXME: allow access only when marshalling is finished
164
+ # @return [String]
237
165
  attr_reader :packet
238
166
 
167
+ # @return [:little,:big]
168
+ attr_reader :endianness
169
+
239
170
  # Create a new marshaller, setting the current packet to the
240
171
  # empty packet.
241
- def initialize(offset = 0)
172
+ def initialize(offset = 0, endianness: HOST_ENDIANNESS)
173
+ @endianness = endianness
242
174
  @packet = ""
243
175
  @offset = offset # for correct alignment of nested marshallers
244
176
  end
245
177
 
246
- # Round _n_ up to the specified power of two, _a_
247
- def num_align(n, a)
248
- case a
178
+ # Round _num_ up to the specified power of two, _alignment_
179
+ def num_align(num, alignment)
180
+ case alignment
249
181
  when 1, 2, 4, 8
250
- bits = a - 1
251
- n + bits & ~bits
182
+ bits = alignment - 1
183
+ num + bits & ~bits
252
184
  else
253
- raise "Unsupported alignment"
185
+ raise ArgumentError, "Unsupported alignment #{alignment}"
254
186
  end
255
187
  end
256
188
 
257
- # Align the buffer with NULL (\0) bytes on a byte length of _a_.
258
- def align(a)
259
- @packet = @packet.ljust(num_align(@offset + @packet.bytesize, a) - @offset, 0.chr)
260
- end
261
-
262
- # Append the the string _str_ itself to the packet.
263
- def append_string(str)
264
- align(4)
265
- @packet += [str.bytesize].pack("L") + [str].pack("Z*")
266
- end
267
-
268
- # Append the the signature _signature_ itself to the packet.
269
- def append_signature(str)
270
- @packet += str.bytesize.chr + str + "\0"
189
+ # Align the buffer with NULL (\0) bytes on a byte length of _alignment_.
190
+ def align(alignment)
191
+ pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset
192
+ @packet = @packet.ljust(pad_count, 0.chr)
271
193
  end
272
194
 
273
195
  # Append the array type _type_ to the packet and allow for appending
@@ -282,7 +204,9 @@ module DBus
282
204
  yield
283
205
  sz = @packet.bytesize - contentidx
284
206
  raise InvalidPacketException if sz > 67_108_864
285
- @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
207
+
208
+ sz_data = Data::UInt32.new(sz)
209
+ @packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
286
210
  end
287
211
 
288
212
  # Align and allow for appending struct fields.
@@ -294,110 +218,122 @@ module DBus
294
218
  # Append a value _val_ to the packet based on its _type_.
295
219
  #
296
220
  # Host native endianness is used, declared in Message#marshall
221
+ #
222
+ # @param type [SingleCompleteType] (or Integer or {Type})
223
+ # @param val [::Object]
297
224
  def append(type, val)
298
225
  raise TypeException, "Cannot send nil" if val.nil?
299
226
 
300
227
  type = type.chr if type.is_a?(Integer)
301
228
  type = Type::Parser.new(type).parse[0] if type.is_a?(String)
302
- case type.sigtype
303
- when Type::BYTE
304
- @packet += val.chr
305
- when Type::UINT32, Type::UNIX_FD
306
- align(4)
307
- @packet += [val].pack("L")
308
- when Type::UINT64
309
- align(8)
310
- @packet += [val].pack("Q")
311
- when Type::INT64
312
- align(8)
313
- @packet += [val].pack("q")
314
- when Type::INT32
315
- align(4)
316
- @packet += [val].pack("l")
317
- when Type::UINT16
318
- align(2)
319
- @packet += [val].pack("S")
320
- when Type::INT16
321
- align(2)
322
- @packet += [val].pack("s")
323
- when Type::DOUBLE
324
- align(8)
325
- @packet += [val].pack("d")
326
- when Type::BOOLEAN
327
- align(4)
328
- @packet += if val
329
- [1].pack("L")
330
- else
331
- [0].pack("L")
332
- end
333
- when Type::OBJECT_PATH
334
- append_string(val)
335
- when Type::STRING
336
- append_string(val)
337
- when Type::SIGNATURE
338
- append_signature(val)
339
- when Type::VARIANT
340
- vartype = nil
341
- if val.is_a?(Array) && val.size == 2
342
- if val[0].is_a?(DBus::Type::Type)
343
- vartype, vardata = val
344
- elsif val[0].is_a?(String)
345
- begin
346
- parsed = Type::Parser.new(val[0]).parse
347
- vartype = parsed[0] if parsed.size == 1
348
- vardata = val[1]
349
- rescue Type::SignatureException
350
- # no assignment
229
+ # type is [Type] now
230
+ data_class = Data::BY_TYPE_CODE[type.sigtype]
231
+ if data_class.nil?
232
+ raise NotImplementedError,
233
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
234
+ end
235
+
236
+ if data_class.fixed?
237
+ align(data_class.alignment)
238
+ data = data_class.new(val)
239
+ @packet += data.marshall(endianness)
240
+ elsif data_class.basic?
241
+ val = val.value if val.is_a?(Data::Basic)
242
+ align(data_class.size_class.alignment)
243
+ size_data = data_class.size_class.new(val.bytesize)
244
+ @packet += size_data.marshall(endianness)
245
+ # Z* makes a binary string, as opposed to interpolation
246
+ @packet += [val].pack("Z*")
247
+ else
248
+ case type.sigtype
249
+
250
+ when Type::VARIANT
251
+ append_variant(val)
252
+ when Type::ARRAY
253
+ append_array(type.child, val)
254
+ when Type::STRUCT, Type::DICT_ENTRY
255
+ val = val.value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
256
+ unless val.is_a?(Array) || val.is_a?(Struct)
257
+ type_name = Type::TYPE_MAPPING[type.sigtype].first
258
+ raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
259
+ end
260
+
261
+ if type.sigtype == Type::DICT_ENTRY && val.size != 2
262
+ raise TypeException, "DICT_ENTRY expects a pair"
263
+ end
264
+
265
+ if type.members.size != val.size
266
+ type_name = Type::TYPE_MAPPING[type.sigtype].first
267
+ raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
268
+ end
269
+
270
+ struct do
271
+ type.members.zip(val).each do |t, v|
272
+ append(t, v)
351
273
  end
352
274
  end
275
+ else
276
+ raise NotImplementedError,
277
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
353
278
  end
354
- if vartype.nil?
355
- vartype, vardata = PacketMarshaller.make_variant(val)
356
- vartype = Type::Parser.new(vartype).parse[0]
357
- end
279
+ end
280
+ end
358
281
 
359
- append_signature(vartype.to_s)
360
- align(vartype.alignment)
361
- sub = PacketMarshaller.new(@offset + @packet.bytesize)
362
- sub.append(vartype, vardata)
363
- @packet += sub.packet
364
- when Type::ARRAY
365
- if val.is_a?(Hash)
366
- raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
367
- # Damn ruby rocks here
368
- val = val.to_a
369
- end
370
- # If string is recieved and ay is expected, explode the string
371
- if val.is_a?(String) && type.child.sigtype == Type::BYTE
372
- val = val.bytes
373
- end
374
- if !val.is_a?(Enumerable)
375
- raise TypeException, "Expected an Enumerable of #{type.child.inspect} but got a #{val.class}"
376
- end
377
- array(type.child) do
378
- val.each do |elem|
379
- append(type.child, elem)
282
+ def append_variant(val)
283
+ vartype = nil
284
+ if val.is_a?(DBus::Data::Base)
285
+ vartype = val.type # FIXME: box or unbox another variant?
286
+ vardata = val.value
287
+ elsif val.is_a?(Array) && val.size == 2
288
+ case val[0]
289
+ when Type
290
+ vartype, vardata = val
291
+ # Ambiguous but easy to use, because Type
292
+ # cannot construct "as" "a{sv}" easily
293
+ when String
294
+ begin
295
+ parsed = Type::Parser.new(val[0]).parse
296
+ vartype = parsed[0] if parsed.size == 1
297
+ vardata = val[1]
298
+ rescue Type::SignatureException
299
+ # no assignment
380
300
  end
381
301
  end
382
- when Type::STRUCT, Type::DICT_ENTRY
383
- # TODO: use duck typing, val.respond_to?
384
- raise TypeException, "Struct/DE expects an Array" if !val.is_a?(Array)
385
- if type.sigtype == Type::DICT_ENTRY && val.size != 2
386
- raise TypeException, "Dict entry expects a pair"
387
- end
388
- if type.members.size != val.size
389
- raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
390
- end
391
- struct do
392
- type.members.zip(val).each do |t, v|
393
- append(t, v)
394
- end
302
+ end
303
+ if vartype.nil?
304
+ vartype, vardata = PacketMarshaller.make_variant(val)
305
+ vartype = Type::Parser.new(vartype).parse[0]
306
+ end
307
+
308
+ append(Data::Signature.type, vartype.to_s)
309
+ align(vartype.alignment)
310
+ sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
311
+ sub.append(vartype, vardata)
312
+ @packet += sub.packet
313
+ end
314
+
315
+ # @param child_type [Type]
316
+ def append_array(child_type, val)
317
+ if val.is_a?(Hash)
318
+ raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
319
+
320
+ # Damn ruby rocks here
321
+ val = val.to_a
322
+ end
323
+ # If string is recieved and ay is expected, explode the string
324
+ if val.is_a?(String) && child_type.sigtype == Type::BYTE
325
+ val = val.bytes
326
+ end
327
+ if !val.is_a?(Enumerable)
328
+ raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}"
329
+ end
330
+
331
+ array(child_type) do
332
+ val.each do |elem|
333
+ append(child_type, elem)
395
334
  end
396
- else
397
- raise NotImplementedError,
398
- "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
399
335
  end
400
- end # def append
336
+ end
401
337
 
402
338
  # Make a [signature, value] pair for a variant
403
339
  def self.make_variant(value)
@@ -429,5 +365,5 @@ module DBus
429
365
  end
430
366
  end
431
367
  end
432
- end # class PacketMarshaller
433
- end # module DBus
368
+ end
369
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  #
@@ -27,7 +29,7 @@ module DBus
27
29
  attr_accessor :path
28
30
  # The destination filter.
29
31
  attr_accessor :destination
30
- # The type type that is matched.
32
+ # @return [String] The type type that is matched.
31
33
  attr_reader :type
32
34
 
33
35
  # Create a new match rule
@@ -35,13 +37,14 @@ module DBus
35
37
  @sender = @interface = @member = @path = @destination = @type = nil
36
38
  end
37
39
 
38
- # Set the message types to filter to type _t_.
40
+ # Set the message types to filter to type _typ_.
39
41
  # Possible message types are: signal, method_call, method_return, and error.
40
- def type=(t)
41
- if !["signal", "method_call", "method_return", "error"].member?(t)
42
- raise MatchRuleException, t
42
+ def type=(typ)
43
+ if !["signal", "method_call", "method_return", "error"].member?(typ)
44
+ raise MatchRuleException, typ
43
45
  end
44
- @type = t
46
+
47
+ @type = typ
45
48
  end
46
49
 
47
50
  # Returns a match rule string version of the object. E.g.:
@@ -58,11 +61,13 @@ module DBus
58
61
  def from_s(str)
59
62
  str.split(",").each do |eq|
60
63
  next unless eq =~ /^(.*)='([^']*)'$/
64
+
61
65
  # "
62
66
  name = Regexp.last_match(1)
63
67
  val = Regexp.last_match(2)
64
68
  raise MatchRuleException, name unless FILTERS.member?(name.to_sym)
65
- method(name + "=").call(val)
69
+
70
+ method("#{name}=").call(val)
66
71
  end
67
72
  self
68
73
  end
@@ -80,18 +85,13 @@ module DBus
80
85
 
81
86
  # Determines whether a message _msg_ matches the match rule.
82
87
  def match(msg)
83
- if @type
84
- if { Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
85
- Message::METHOD_RETURN => "method_return",
86
- Message::ERROR => "error" }[msg.message_type] != @type
87
- return false
88
- end
89
- end
88
+ return false if @type && @type != msg.message_type_s
90
89
  return false if @interface && @interface != msg.interface
91
90
  return false if @member && @member != msg.member
92
91
  return false if @path && @path != msg.path
92
+
93
93
  # FIXME: sender and destination are ignored
94
94
  true
95
95
  end
96
- end # class MatchRule
97
- end # module D-Bus
96
+ end
97
+ end