• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

subpop / swift-dbus / 20435689450

22 Dec 2025 03:04PM UTC coverage: 51.766% (-0.06%) from 51.826%
20435689450

push

github

subpop
Bump github.com/apple/swift-argument-parser from 1.6.2 to 1.7.0

Bumps [github.com/apple/swift-argument-parser](https://github.com/apple/swift-argument-parser) from 1.6.2 to 1.7.0.
- [Release notes](https://github.com/apple/swift-argument-parser/releases)
- [Commits](https://github.com/apple/swift-argument-parser/compare/1.6.2...1.7.0)

---
updated-dependencies:
- dependency-name: github.com/apple/swift-argument-parser
  dependency-version: 1.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

3459 of 6682 relevant lines covered (51.77%)

643283.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.82
/Sources/DBus/Message.swift
1
//
2
// Copyright 2025 Link Dupont
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License");
5
// you may not use this file except in compliance with the License.
6
// You may obtain a copy of the License at
7
//
8
//     http://www.apache.org/licenses/LICENSE-2.0
9
//
10
// Unless required by applicable law or agreed to in writing, software
11
// distributed under the License is distributed on an "AS IS" BASIS,
12
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
// See the License for the specific language governing permissions and
14
// limitations under the License.
15
//
16

17
import Foundation
18

19
/// D-Bus message type as defined in the specification
20
public enum DBusMessageType: UInt8, CaseIterable, Sendable {
21
    case methodCall = 1
22
    case methodReturn = 2
23
    case error = 3
24
    case signal = 4
25
}
26

27
/// D-Bus message flags as defined in the specification
28
public struct DBusMessageFlags: OptionSet, Sendable {
29
    public let rawValue: UInt8
30

31
    public init(rawValue: UInt8) {
941✔
32
        self.rawValue = rawValue
941✔
33
    }
941✔
34

35
    /// No reply is expected to this message
36
    public static let noReplyExpected = DBusMessageFlags(rawValue: 0x01)
37

38
    /// The bus must not launch an owner for the destination name
39
    public static let noAutoStart = DBusMessageFlags(rawValue: 0x02)
40

41
    /// This message may prompt the user for authorization
42
    public static let allowInteractiveAuthorization = DBusMessageFlags(rawValue: 0x04)
43
}
44

45
/// D-Bus header field codes as defined in the specification
46
public enum DBusHeaderField: UInt8, CaseIterable, Sendable {
47
    case path = 1
48
    case interface = 2
49
    case member = 3
50
    case errorName = 4
51
    case replySerial = 5
52
    case destination = 6
53
    case sender = 7
54
    case signature = 8
55
    case unixFDs = 9
56
}
57

58
/// Represents a D-Bus header field with its code and value
59
public struct DBusHeaderFieldEntry: Sendable {
60
    public let field: DBusHeaderField
61
    public let value: HeaderVariant
62

63
    public init(field: DBusHeaderField, value: HeaderVariant) {
3,899✔
64
        self.field = field
3,899✔
65
        self.value = value
3,899✔
66
    }
3,899✔
67
}
68

69
public enum Endianness: Sendable {
70
    case littleEndian
71
    case bigEndian
72
}
73

74
/// Represents a complete D-Bus message according to the specification
75
public struct Message: Sendable {
76
    /// Message endianness ('l' for little-endian, 'B' for big-endian)
77
    public let endianness: Endianness
78

79
    /// Type of the message
80
    public let messageType: DBusMessageType
81

82
    /// Message flags
83
    public let flags: DBusMessageFlags
84

85
    /// D-Bus protocol version (currently always 1)
86
    public let protocolVersion: UInt8
87

88
    /// Length of the message body in bytes
89
    public let bodyLength: UInt32
90

91
    /// Message serial number (must not be zero)
92
    public let serial: UInt32
93

94
    /// Array of header fields
95
    public let headerFields: [DBusHeaderFieldEntry]
96

97
    /// Message body data
98
    public let body: [UInt8]
99

100
    /// Message body signature (if any)
101
    public let bodySignature: Signature?
102

103
    // MARK: - Convenience Properties
104

105
    /// Object path from header fields
106
    public var path: ObjectPath? {
890✔
107
        return headerFields.first { $0.field == .path }?.value.objectPathValue
890✔
108
    }
890✔
109

110
    /// Interface name from header fields
111
    public var interface: String? {
875✔
112
        return headerFields.first { $0.field == .interface }?.value.stringValue
2,341✔
113
    }
875✔
114

115
    /// Member (method/signal) name from header fields
116
    public var member: String? {
878✔
117
        return headerFields.first { $0.field == .member }?.value.stringValue
2,037✔
118
    }
878✔
119

120
    /// Error name from header fields
121
    public var errorName: String? {
2✔
122
        return headerFields.first { $0.field == .errorName }?.value.stringValue
2✔
123
    }
2✔
124

125
    /// Reply serial from header fields
126
    public var replySerial: UInt32? {
3✔
127
        return headerFields.first { $0.field == .replySerial }?.value.uint32Value
3✔
128
    }
3✔
129

130
    /// Destination bus name from header fields
131
    public var destination: String? {
856✔
132
        return headerFields.first { $0.field == .destination }?.value.stringValue
3,419✔
133
    }
856✔
134

135
    /// Sender bus name from header fields
136
    public var sender: String? {
13✔
137
        return headerFields.first { $0.field == .sender }?.value.stringValue
57✔
138
    }
13✔
139

140
    // MARK: - Initializers
141

142
    /// Initialize a D-Bus message with all components
143
    ///
144
    /// Creates a new D-Bus message with the specified parameters. The message must have a non-zero serial
145
    /// and use protocol version 1 (the only currently supported version).
146
    ///
147
    /// - Parameters:
148
    ///   - endianness: Byte order for the message (default: little-endian)
149
    ///   - messageType: Type of D-Bus message (method call, return, error, or signal)
150
    ///   - flags: Message flags controlling behavior (default: no flags)
151
    ///   - protocolVersion: D-Bus protocol version (default: 1, only supported version)
152
    ///   - serial: Unique message serial number (must not be zero)
153
    ///   - headerFields: Array of header field entries containing message metadata
154
    ///   - body: Message body data as raw bytes
155
    ///   - bodySignature: Optional signature describing the body structure
156
    /// - Throws: `DBusMessageError.invalidSerial` if serial is zero,
157
    ///           `DBusMessageError.unsupportedProtocolVersion` if protocol version is not 1
158
    public init(
159
        endianness: Endianness = .littleEndian,
160
        messageType: DBusMessageType,
161
        flags: DBusMessageFlags = [],
162
        protocolVersion: UInt8 = 1,
163
        serial: UInt32,
164
        headerFields: [DBusHeaderFieldEntry] = [],
165
        body: [UInt8] = [],
166
        bodySignature: Signature? = nil
167
    ) throws {
918✔
168
        guard serial != 0 else {
918✔
169
            throw DBusMessageError.invalidSerial
1✔
170
        }
917✔
171

917✔
172
        guard protocolVersion == 1 else {
917✔
173
            throw DBusMessageError.unsupportedProtocolVersion(protocolVersion)
1✔
174
        }
916✔
175

916✔
176
        self.endianness = endianness
916✔
177
        self.messageType = messageType
916✔
178
        self.flags = flags
916✔
179
        self.protocolVersion = protocolVersion
916✔
180
        self.bodyLength = UInt32(body.count)
916✔
181
        self.serial = serial
916✔
182
        self.headerFields = headerFields
916✔
183
        self.body = body
916✔
184
        self.bodySignature = bodySignature
916✔
185
    }
916✔
186

187
    // MARK: - Convenience Initializers
188

189
    /// Create a method call message
190
    ///
191
    /// Creates a D-Bus method call message with the required path and member fields.
192
    /// Method calls are used to invoke methods on remote D-Bus objects.
193
    ///
194
    /// - Parameters:
195
    ///   - path: Object path identifying the target object
196
    ///   - interface: Optional interface name (recommended for disambiguation)
197
    ///   - member: Name of the method to call
198
    ///   - destination: Optional destination bus name
199
    ///   - serial: Unique message serial number
200
    ///   - body: Optional message body containing method arguments
201
    ///   - bodySignature: Optional signature describing the body structure
202
    ///   - flags: Message flags (default: no flags)
203
    /// - Returns: A new method call message
204
    /// - Throws: `DBusMessageError` if message construction fails
205
    public static func methodCall(
206
        path: ObjectPath,
207
        interface: String? = nil,
208
        member: String,
209
        destination: String? = nil,
210
        serial: UInt32,
211
        body: [UInt8] = [],
212
        bodySignature: Signature? = nil,
213
        flags: DBusMessageFlags = []
214
    ) throws -> Message {
346✔
215
        var headerFields: [DBusHeaderFieldEntry] = [
346✔
216
            DBusHeaderFieldEntry(
346✔
217
                field: .path, value: try HeaderVariant(path, signature: "o")),
346✔
218
            DBusHeaderFieldEntry(
346✔
219
                field: .member, value: try HeaderVariant(member, signature: "s")),
346✔
220
        ]
346✔
221

346✔
222
        if let interface = interface {
346✔
223
            headerFields.append(
339✔
224
                DBusHeaderFieldEntry(
339✔
225
                    field: .interface,
339✔
226
                    value: try HeaderVariant(interface, signature: "s")))
339✔
227
        }
346✔
228

346✔
229
        if let destination = destination {
346✔
230
            headerFields.append(
336✔
231
                DBusHeaderFieldEntry(
336✔
232
                    field: .destination,
336✔
233
                    value: try HeaderVariant(destination, signature: "s")))
336✔
234
        }
346✔
235

346✔
236
        if let bodySignature = bodySignature {
346✔
237
            headerFields.append(
295✔
238
                DBusHeaderFieldEntry(
295✔
239
                    field: .signature,
295✔
240
                    value: try HeaderVariant(bodySignature, signature: "g")))
295✔
241
        }
346✔
242

346✔
243
        return try Message(
346✔
244
            messageType: .methodCall,
346✔
245
            flags: flags,
346✔
246
            serial: serial,
346✔
247
            headerFields: headerFields,
346✔
248
            body: body,
346✔
249
            bodySignature: bodySignature
346✔
250
        )
346✔
251
    }
346✔
252

253
    /// Create a method return message
254
    ///
255
    /// Creates a D-Bus method return message in response to a method call.
256
    /// Method returns contain the result of a successful method invocation.
257
    ///
258
    /// - Parameters:
259
    ///   - replySerial: Serial number of the method call this is responding to
260
    ///   - destination: Optional destination bus name (usually the original sender)
261
    ///   - serial: Unique serial number for this return message
262
    ///   - body: Optional message body containing return values
263
    ///   - bodySignature: Optional signature describing the return value structure
264
    /// - Returns: A new method return message
265
    /// - Throws: `DBusMessageError` if message construction fails
266
    public static func methodReturn(
267
        replySerial: UInt32,
268
        destination: String? = nil,
269
        serial: UInt32,
270
        body: [UInt8] = [],
271
        bodySignature: Signature? = nil
272
    ) throws -> Message {
34✔
273
        var headerFields: [DBusHeaderFieldEntry] = [
34✔
274
            DBusHeaderFieldEntry(
34✔
275
                field: .replySerial,
34✔
276
                value: try HeaderVariant(replySerial, signature: "u"))
34✔
277
        ]
34✔
278

34✔
279
        if let destination = destination {
34✔
280
            headerFields.append(
22✔
281
                DBusHeaderFieldEntry(
22✔
282
                    field: .destination,
22✔
283
                    value: try HeaderVariant(destination, signature: "s")))
22✔
284
        }
34✔
285

34✔
286
        if let bodySignature = bodySignature {
34✔
287
            headerFields.append(
13✔
288
                DBusHeaderFieldEntry(
13✔
289
                    field: .signature,
13✔
290
                    value: try HeaderVariant(bodySignature, signature: "g")))
13✔
291
        }
34✔
292

34✔
293
        return try Message(
34✔
294
            messageType: .methodReturn,
34✔
295
            serial: serial,
34✔
296
            headerFields: headerFields,
34✔
297
            body: body,
34✔
298
            bodySignature: bodySignature
34✔
299
        )
34✔
300
    }
34✔
301

302
    /// Create an error message
303
    ///
304
    /// Creates a D-Bus error message in response to a failed method call.
305
    /// Error messages indicate that a method call could not be completed successfully.
306
    ///
307
    /// - Parameters:
308
    ///   - errorName: Well-known error name identifying the type of error
309
    ///   - replySerial: Serial number of the method call that failed
310
    ///   - destination: Optional destination bus name (usually the original sender)
311
    ///   - serial: Unique serial number for this error message
312
    ///   - body: Optional message body containing error details
313
    ///   - bodySignature: Optional signature describing the error detail structure
314
    /// - Returns: A new error message
315
    /// - Throws: `DBusMessageError` if message construction fails
316
    public static func error(
317
        errorName: String,
318
        replySerial: UInt32,
319
        destination: String? = nil,
320
        serial: UInt32,
321
        body: [UInt8] = [],
322
        bodySignature: Signature? = nil
323
    ) throws -> Message {
22✔
324
        var headerFields: [DBusHeaderFieldEntry] = [
22✔
325
            DBusHeaderFieldEntry(
22✔
326
                field: .errorName, value: try HeaderVariant(errorName, signature: "s")),
22✔
327
            DBusHeaderFieldEntry(
22✔
328
                field: .replySerial,
22✔
329
                value: try HeaderVariant(replySerial, signature: "u")),
22✔
330
        ]
22✔
331

22✔
332
        if let destination = destination {
22✔
333
            headerFields.append(
22✔
334
                DBusHeaderFieldEntry(
22✔
335
                    field: .destination,
22✔
336
                    value: try HeaderVariant(destination, signature: "s")))
22✔
337
        }
22✔
338

22✔
339
        if let bodySignature = bodySignature {
22✔
340
            headerFields.append(
1✔
341
                DBusHeaderFieldEntry(
1✔
342
                    field: .signature,
1✔
343
                    value: try HeaderVariant(bodySignature, signature: "g")))
1✔
344
        }
22✔
345

22✔
346
        return try Message(
22✔
347
            messageType: .error,
22✔
348
            serial: serial,
22✔
349
            headerFields: headerFields,
22✔
350
            body: body,
22✔
351
            bodySignature: bodySignature
22✔
352
        )
22✔
353
    }
22✔
354

355
    /// Create a signal message
356
    ///
357
    /// Creates a D-Bus signal message for broadcasting events or notifications.
358
    /// Signals are one-way messages that don't expect a response.
359
    ///
360
    /// - Parameters:
361
    ///   - path: Object path of the object emitting the signal
362
    ///   - interface: Interface name that defines the signal
363
    ///   - member: Name of the signal being emitted
364
    ///   - destination: Optional destination bus name (usually omitted for broadcasts)
365
    ///   - serial: Unique message serial number
366
    ///   - body: Optional message body containing signal data
367
    ///   - bodySignature: Optional signature describing the signal data structure
368
    /// - Returns: A new signal message
369
    /// - Throws: `DBusMessageError` if message construction fails
370
    public static func signal(
371
        path: ObjectPath,
372
        interface: String,
373
        member: String,
374
        destination: String? = nil,
375
        serial: UInt32,
376
        body: [UInt8] = [],
377
        bodySignature: Signature? = nil
378
    ) throws -> Message {
24✔
379
        var headerFields: [DBusHeaderFieldEntry] = [
24✔
380
            DBusHeaderFieldEntry(
24✔
381
                field: .path, value: try HeaderVariant(path, signature: "o")),
24✔
382
            DBusHeaderFieldEntry(
24✔
383
                field: .interface, value: try HeaderVariant(interface, signature: "s")),
24✔
384
            DBusHeaderFieldEntry(
24✔
385
                field: .member, value: try HeaderVariant(member, signature: "s")),
24✔
386
        ]
24✔
387

24✔
388
        if let destination = destination {
24✔
389
            headerFields.append(
22✔
390
                DBusHeaderFieldEntry(
22✔
391
                    field: .destination,
22✔
392
                    value: try HeaderVariant(destination, signature: "s")))
22✔
393
        }
24✔
394

24✔
395
        if let bodySignature = bodySignature {
24✔
396
            headerFields.append(
3✔
397
                DBusHeaderFieldEntry(
3✔
398
                    field: .signature,
3✔
399
                    value: try HeaderVariant(bodySignature, signature: "g")))
3✔
400
        }
24✔
401

24✔
402
        return try Message(
24✔
403
            messageType: .signal,
24✔
404
            serial: serial,
24✔
405
            headerFields: headerFields,
24✔
406
            body: body,
24✔
407
            bodySignature: bodySignature
24✔
408
        )
24✔
409
    }
24✔
410
}
411

412
// MARK: - Serialization
413

414
extension Message {
415
    /// Serialize the message to D-Bus wire format
416
    ///
417
    /// Converts the message to the binary wire format used by D-Bus for transmission
418
    /// over the message bus. The serialized format includes the fixed header, variable
419
    /// header fields, and message body with proper alignment and endianness.
420
    ///
421
    /// - Returns: Byte array containing the serialized message
422
    /// - Throws: `DBusMessageError.messageTooLarge` if the message exceeds 128 MiB,
423
    ///           `DBusMessageError.serializationFailed` if serialization fails
424
    public func serialize() throws -> [UInt8] {
438✔
425
        var result: [UInt8] = []
438✔
426

438✔
427
        // Serialize fixed header (12 bytes)
438✔
428
        result.append(endianness == .littleEndian ? UInt8(ascii: "l") : UInt8(ascii: "B"))
438✔
429
        result.append(messageType.rawValue)
438✔
430
        result.append(flags.rawValue)
438✔
431
        result.append(protocolVersion)
438✔
432

438✔
433
        // Body length (4 bytes, respecting endianness)
438✔
434
        let bodyLengthBytes: [UInt8]
438✔
435
        if endianness == .littleEndian {
438✔
436
            bodyLengthBytes = withUnsafeBytes(of: bodyLength.littleEndian) { Array($0) }
435✔
437
        } else {
435✔
438
            bodyLengthBytes = withUnsafeBytes(of: bodyLength.bigEndian) { Array($0) }
3✔
439
        }
3✔
440
        result.append(contentsOf: bodyLengthBytes)
438✔
441

438✔
442
        // Serial (4 bytes, respecting endianness)
438✔
443
        let serialBytes: [UInt8]
438✔
444
        if endianness == .littleEndian {
438✔
445
            serialBytes = withUnsafeBytes(of: serial.littleEndian) { Array($0) }
435✔
446
        } else {
435✔
447
            serialBytes = withUnsafeBytes(of: serial.bigEndian) { Array($0) }
3✔
448
        }
3✔
449
        result.append(contentsOf: serialBytes)
438✔
450

438✔
451
        // Serialize header fields array
438✔
452
        let headerFieldsData = try serializeHeaderFields()
438✔
453
        result.append(contentsOf: headerFieldsData)
438✔
454

438✔
455
        // Pad header to 8-byte boundary
438✔
456
        let paddingNeeded = (8 - (result.count % 8)) % 8
438✔
457
        result.append(contentsOf: Array(repeating: 0, count: paddingNeeded))
438✔
458

438✔
459
        // Combine header and body
438✔
460
        result.append(contentsOf: body)
438✔
461

438✔
462
        // Validate maximum message size (128 MiB)
438✔
463
        guard result.count <= 134_217_728 else {
438✔
464
            throw DBusMessageError.messageTooLarge
×
465
        }
438✔
466

438✔
467
        return result
438✔
468
    }
438✔
469

470
    /// Serialize the header fields array into D-Bus wire format
471
    ///
472
    /// Converts the message's header fields into a properly formatted array structure
473
    /// as required by D-Bus specification. Header fields are serialized as:
474
    /// Array of STRUCT(BYTE field_code, VARIANT value)
475
    ///
476
    /// Each STRUCT is 8-byte aligned and contains:
477
    /// - BYTE field_code (1 byte)
478
    /// - VARIANT (signature + aligned value)
479
    ///
480
    /// - Returns: Byte array containing the serialized header fields array
481
    /// - Throws: `DBusMessageError.serializationFailed` if any field cannot be serialized
482
    private func serializeHeaderFields() throws -> [UInt8] {
438✔
483
        var result: [UInt8] = []
438✔
484

438✔
485
        // Calculate total size of header fields
438✔
486
        var fieldsData: [UInt8] = []
438✔
487

438✔
488
        // Sort header fields by field code to ensure consistent ordering
438✔
489
        // Some D-Bus implementations expect fields in ascending order
438✔
490
        let sortedFields = headerFields.sorted { $0.field.rawValue < $1.field.rawValue }
1,678✔
491

438✔
492
        for field in sortedFields {
1,773✔
493
            // Start of new STRUCT - align to 8-byte boundary
1,773✔
494
            let structPadding = (8 - (fieldsData.count % 8)) % 8
1,773✔
495
            fieldsData.append(contentsOf: Array(repeating: 0, count: structPadding))
1,773✔
496

1,773✔
497
            // STRUCT content: (BYTE field_code, VARIANT value)
1,773✔
498

1,773✔
499
            // 1. Field code (1 byte)
1,773✔
500
            fieldsData.append(field.field.rawValue)
1,773✔
501

1,773✔
502
            // 2. VARIANT: signature (1 byte length + string + null) + aligned value
1,773✔
503
            let sigBytes = field.value.signature.rawValue.utf8
1,773✔
504
            fieldsData.append(UInt8(sigBytes.count))
1,773✔
505
            fieldsData.append(contentsOf: sigBytes)
1,773✔
506
            fieldsData.append(0)  // null terminator
1,773✔
507

1,773✔
508
            // Calculate alignment for the variant value within this STRUCT
1,773✔
509
            // We need to account for the current position within the message
1,773✔
510
            let currentMessageOffset = 16 + 4 + fieldsData.count  // fixed header + array length + current position
1,773✔
511
            let alignment = getAlignment(for: field.value.signature)
1,773✔
512
            let valuePadding = (alignment - (currentMessageOffset % alignment)) % alignment
1,773✔
513
            fieldsData.append(contentsOf: Array(repeating: 0, count: valuePadding))
1,773✔
514

1,773✔
515
            // Serialize the variant value
1,773✔
516
            let valueData = try serializeVariantValue(
1,773✔
517
                field.value.value.anyValue, signature: field.value.signature)
1,773✔
518
            fieldsData.append(contentsOf: valueData)
1,773✔
519
        }
1,773✔
520

438✔
521
        // Array length (4 bytes, respecting endianness)
438✔
522
        let arrayLength = UInt32(fieldsData.count)
438✔
523
        let lengthBytes: [UInt8]
438✔
524
        if endianness == .littleEndian {
438✔
525
            lengthBytes = withUnsafeBytes(of: arrayLength.littleEndian) { Array($0) }
435✔
526
        } else {
435✔
527
            lengthBytes = withUnsafeBytes(of: arrayLength.bigEndian) { Array($0) }
3✔
528
        }
3✔
529
        result.append(contentsOf: lengthBytes)
438✔
530

438✔
531
        // Array data
438✔
532
        result.append(contentsOf: fieldsData)
438✔
533

438✔
534
        return result
438✔
535
    }
438✔
536

537
    /// Serialize a header variant into D-Bus wire format
538
    ///
539
    /// Converts a header variant (signature + value) into the D-Bus wire format. The serialized
540
    /// format includes the signature length, signature string, null terminator, and the
541
    /// serialized value. Alignment is handled by the caller.
542
    ///
543
    /// - Parameter variant: The header variant to serialize
544
    /// - Returns: Byte array containing the serialized variant
545
    /// - Throws: `DBusMessageError.serializationFailed` if the variant cannot be serialized
546
    private func serializeVariant(_ variant: HeaderVariant) throws -> [UInt8] {
×
547
        var result: [UInt8] = []
×
548

×
549
        // Signature length and data
×
550
        let sigBytes = variant.signature.rawValue.utf8
×
551
        result.append(UInt8(sigBytes.count))
×
552
        result.append(contentsOf: sigBytes)
×
553
        result.append(0)  // null terminator
×
554

×
555
        // Serialize the value based on signature (no alignment here - handled by caller)
×
556
        let valueData = try serializeVariantValue(
×
557
            variant.value.anyValue, signature: variant.signature)
×
558
        result.append(contentsOf: valueData)
×
559

×
560
        return result
×
561
    }
×
562

563
    /// Serialize a variant value based on its signature
564
    ///
565
    /// Converts a value to its D-Bus wire format representation based on the provided
566
    /// signature. Currently supports basic types: strings (s), object paths (o),
567
    /// unsigned 32-bit integers (u), and signatures (g).
568
    ///
569
    /// - Parameters:
570
    ///   - value: The value to serialize
571
    ///   - signature: The D-Bus signature describing the value type
572
    /// - Returns: Byte array containing the serialized value
573
    /// - Throws: `DBusMessageError.serializationFailed` if the value cannot be serialized
574
    private func serializeVariantValue(_ value: Any, signature: Signature) throws -> [UInt8] {
1,771✔
575
        // Simple implementation for common types
1,771✔
576
        switch signature.rawValue {
1,771✔
577
        case "s":  // string
1,771✔
578
            if let str = value as? String {
1,128✔
579
                var result: [UInt8] = []
1,128✔
580
                let strBytes = str.utf8
1,128✔
581
                let lengthBytes: [UInt8]
1,128✔
582
                if endianness == .littleEndian {
1,128✔
583
                    lengthBytes = withUnsafeBytes(of: UInt32(strBytes.count).littleEndian) {
1,124✔
584
                        Array($0)
1,124✔
585
                    }
1,124✔
586
                } else {
1,124✔
587
                    lengthBytes = withUnsafeBytes(of: UInt32(strBytes.count).bigEndian) {
4✔
588
                        Array($0)
4✔
589
                    }
4✔
590
                }
4✔
591
                result.append(contentsOf: lengthBytes)
1,128✔
592
                result.append(contentsOf: strBytes)
1,128✔
593
                result.append(0)  // null terminator
1,128✔
594
                return result
1,128✔
595
            }
1,128✔
596
        case "o":  // object path
1,771✔
597
            if let path = value as? ObjectPath {
396✔
598
                let pathStr = path.fullPath
396✔
599
                var result: [UInt8] = []
396✔
600
                let strBytes = pathStr.utf8
396✔
601
                let lengthBytes: [UInt8]
396✔
602
                if endianness == .littleEndian {
396✔
603
                    lengthBytes = withUnsafeBytes(of: UInt32(strBytes.count).littleEndian) {
393✔
604
                        Array($0)
393✔
605
                    }
393✔
606
                } else {
393✔
607
                    lengthBytes = withUnsafeBytes(of: UInt32(strBytes.count).bigEndian) {
3✔
608
                        Array($0)
3✔
609
                    }
3✔
610
                }
3✔
611
                result.append(contentsOf: lengthBytes)
396✔
612
                result.append(contentsOf: strBytes)
396✔
613
                result.append(0)  // null terminator
396✔
614
                return result
396✔
615
            }
396✔
616
        case "u":  // uint32
1,771✔
617
            if let num = value as? UInt32 {
42✔
618
                if endianness == .littleEndian {
42✔
619
                    return withUnsafeBytes(of: num.littleEndian) { Array($0) }
42✔
620
                } else {
42✔
621
                    return withUnsafeBytes(of: num.bigEndian) { Array($0) }
×
622
                }
×
623
            }
42✔
624
        case "g":  // signature
1,771✔
625
            if let sig = value as? Signature {
208✔
626
                var result: [UInt8] = []
208✔
627
                let sigBytes = sig.rawValue.utf8
208✔
628
                result.append(UInt8(sigBytes.count))
208✔
629
                result.append(contentsOf: sigBytes)
208✔
630
                result.append(0)  // null terminator
208✔
631
                return result
208✔
632
            }
208✔
633
        default:
1,771✔
634
            break
×
635
        }
1,771✔
636

×
637
        throw DBusMessageError.serializationFailed
×
638
    }
1,771✔
639

640
    /// Get the alignment requirements for a D-Bus type signature
641
    ///
642
    /// Returns the byte alignment required for the given D-Bus type signature.
643
    /// This is used to ensure proper padding in the serialized message format.
644
    ///
645
    /// - Parameter signature: The D-Bus type signature
646
    /// - Returns: Alignment requirement in bytes (1, 2, 4, or 8)
647
    private func getAlignment(for signature: Signature) -> Int {
1,770✔
648
        // Return alignment requirements for different types
1,770✔
649
        switch signature.rawValue {
1,770✔
650
        case "y", "g": return 1
1,770✔
651
        case "n", "q": return 2
1,770✔
652
        case "b", "i", "u", "s", "o": return 4
1,770✔
653
        case "x", "t", "d": return 8
1,770✔
654
        default: return 1
1,770✔
655
        }
1,770✔
656
    }
1,770✔
657
}
658

659
// MARK: - Deserialization
660

661
extension Message {
662
    /// Deserialize a D-Bus message from wire format
663
    ///
664
    /// Parses a D-Bus message from its binary wire format representation. The method
665
    /// extracts the fixed header, variable header fields, and message body while
666
    /// respecting the message's endianness and alignment requirements.
667
    ///
668
    /// - Parameter data: Byte array containing the serialized message
669
    /// - Returns: A parsed D-Bus message
670
    /// - Throws: Various `DBusMessageError` cases for invalid format, endianness,
671
    ///           message type, protocol version, serial, or body length
672
    public static func deserialize(from data: [UInt8]) throws -> Message {
480✔
673
        guard data.count >= 16 else {
480✔
674
            throw DBusMessageError.invalidMessageFormat
1✔
675
        }
479✔
676

479✔
677
        var index = 0
479✔
678

479✔
679
        // Determine endianness
479✔
680
        let endiannessChar = data[0]
479✔
681
        let endianness: Endianness
479✔
682
        switch endiannessChar {
479✔
683
        case UInt8(ascii: "l"):
479✔
684
            endianness = .littleEndian
476✔
685
        case UInt8(ascii: "B"):
479✔
686
            endianness = .bigEndian
1✔
687
        default:
479✔
688
            throw DBusMessageError.invalidEndianness
2✔
689
        }
479✔
690
        index += 1
477✔
691

477✔
692
        // Parse fixed header
477✔
693
        let messageTypeRaw = data[index]
477✔
694
        index += 1
477✔
695

477✔
696
        let flagsRaw = data[index]
477✔
697
        index += 1
477✔
698

477✔
699
        let protocolVersion = data[index]
477✔
700
        index += 1
477✔
701

477✔
702
        // Body length (4 bytes)
477✔
703
        guard index + 4 <= data.count else {
477✔
704
            throw DBusMessageError.invalidMessageFormat
×
705
        }
477✔
706
        let bodyLength: UInt32
477✔
707
        if endianness == .littleEndian {
477✔
708
            bodyLength =
476✔
709
                UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
476✔
710
                | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
476✔
711
        } else {
476✔
712
            bodyLength =
1✔
713
                (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
1✔
714
                | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
1✔
715
        }
1✔
716
        index += 4
477✔
717

477✔
718
        // Serial (4 bytes)
477✔
719
        guard index + 4 <= data.count else {
477✔
720
            throw DBusMessageError.invalidMessageFormat
×
721
        }
477✔
722
        let serial: UInt32
477✔
723
        if endianness == .littleEndian {
477✔
724
            serial =
476✔
725
                UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
476✔
726
                | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
476✔
727
        } else {
476✔
728
            serial =
1✔
729
                (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
1✔
730
                | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
1✔
731
        }
1✔
732
        index += 4
477✔
733

477✔
734
        // Validate values
477✔
735
        guard let messageType = DBusMessageType(rawValue: messageTypeRaw) else {
477✔
736
            throw DBusMessageError.invalidMessageType(messageTypeRaw)
1✔
737
        }
476✔
738

476✔
739
        guard protocolVersion == 1 else {
476✔
740
            throw DBusMessageError.unsupportedProtocolVersion(protocolVersion)
×
741
        }
476✔
742

476✔
743
        guard serial != 0 else {
476✔
744
            throw DBusMessageError.invalidSerial
×
745
        }
476✔
746

476✔
747
        let flags = DBusMessageFlags(rawValue: flagsRaw)
476✔
748

476✔
749
        // Parse header fields array
476✔
750
        guard index + 4 <= data.count else {
476✔
751
            throw DBusMessageError.invalidMessageFormat
×
752
        }
476✔
753
        let headerFieldsLength: UInt32
476✔
754
        if endianness == .littleEndian {
476✔
755
            headerFieldsLength =
475✔
756
                UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
475✔
757
                | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
475✔
758
        } else {
475✔
759
            headerFieldsLength =
1✔
760
                (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
1✔
761
                | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
1✔
762
        }
1✔
763
        index += 4
476✔
764

476✔
765
        let headerFieldsEndIndex = index + Int(headerFieldsLength)
476✔
766
        guard headerFieldsEndIndex <= data.count else {
476✔
767
            throw DBusMessageError.invalidMessageFormat
×
768
        }
476✔
769

476✔
770
        let headerFields = try parseHeaderFields(
476✔
771
            from: Array(data[index..<headerFieldsEndIndex]), endianness: endianness)
476✔
772
        index = headerFieldsEndIndex
476✔
773

476✔
774
        // Pad to 8-byte boundary
476✔
775
        let paddingNeeded = (8 - (index % 8)) % 8
476✔
776
        index += paddingNeeded
476✔
777

476✔
778
        // Extract body
476✔
779
        let bodyEndIndex = index + Int(bodyLength)
476✔
780
        guard bodyEndIndex <= data.count else {
476✔
781
            throw DBusMessageError.invalidBodyLength
×
782
        }
476✔
783

476✔
784
        let body = Array(data[index..<bodyEndIndex])
476✔
785

476✔
786
        // Extract body signature if present
476✔
787
        let bodySignature =
476✔
788
            headerFields.first { $0.field == .signature }?.value.signatureValue
1,978✔
789

476✔
790
        return try Message(
476✔
791
            endianness: endianness,
476✔
792
            messageType: messageType,
476✔
793
            flags: flags,
476✔
794
            protocolVersion: protocolVersion,
476✔
795
            serial: serial,
476✔
796
            headerFields: headerFields,
476✔
797
            body: body,
476✔
798
            bodySignature: bodySignature
476✔
799
        )
476✔
800
    }
480✔
801

802
    /// Parse header fields from serialized D-Bus data
803
    ///
804
    /// Extracts header field entries from the header fields array section of a D-Bus message.
805
    /// Each field consists of a field code followed by a variant containing the field value.
806
    /// Unknown field codes are skipped to maintain forward compatibility.
807
    ///
808
    /// - Parameters:
809
    ///   - data: Byte array containing the header fields data
810
    ///   - endianness: Byte order for parsing multi-byte values
811
    /// - Returns: Array of parsed header field entries
812
    /// - Throws: `DBusMessageError.invalidMessageFormat` for malformed data
813
    private static func parseHeaderFields(from data: [UInt8], endianness: Endianness) throws
814
        -> [DBusHeaderFieldEntry]
815
    {
476✔
816
        var fields: [DBusHeaderFieldEntry] = []
476✔
817
        var index = 0
476✔
818

476✔
819
        while index < data.count {
2,457✔
820
            // Skip any padding bytes (0x00) at the current position
1,981✔
821
            while index < data.count && data[index] == 0 {
1,981✔
822
                index += 1
×
823
            }
1,981✔
824

1,981✔
825
            guard index < data.count else {
1,981✔
826
                break
×
827
            }
1,981✔
828

1,981✔
829
            // Field code (1 byte)
1,981✔
830
            let fieldCode = data[index]
1,981✔
831
            index += 1
1,981✔
832

1,981✔
833
            guard let field = DBusHeaderField(rawValue: fieldCode) else {
1,981✔
834
                // Skip unknown field - we need to parse the variant to skip it properly
×
835
                // For now, just break to avoid infinite loop
×
836
                break
×
837
            }
1,981✔
838

1,981✔
839
            // Parse variant
1,981✔
840
            let (variant, bytesRead) = try parseVariant(
1,981✔
841
                from: data, startingAt: index, endianness: endianness)
1,981✔
842
            index += bytesRead
1,981✔
843

1,981✔
844
            fields.append(DBusHeaderFieldEntry(field: field, value: variant))
1,981✔
845

1,981✔
846
            // Align to 8-byte boundary for next field
1,981✔
847
            let totalOffset = 16 + index  // 16 is the fixed header size
1,981✔
848
            let padding = (8 - (totalOffset % 8)) % 8
1,981✔
849
            index += padding
1,981✔
850
        }
1,981✔
851

476✔
852
        return fields
476✔
853
    }
476✔
854

855
    /// Parse a variant from serialized D-Bus data
856
    ///
857
    /// Extracts a variant (signature + value) from D-Bus wire format. The method parses
858
    /// the signature length, signature string, and then the aligned value data based
859
    /// on the signature's alignment requirements.
860
    ///
861
    /// - Parameters:
862
    ///   - data: Byte array containing the variant data
863
    ///   - startIndex: Starting position in the data array
864
    ///   - endianness: Byte order for parsing multi-byte values
865
    /// - Returns: Tuple containing the parsed variant and number of bytes consumed
866
    /// - Throws: `DBusMessageError.invalidMessageFormat` for malformed data
867
    private static func parseVariant(
868
        from data: [UInt8], startingAt startIndex: Int, endianness: Endianness
869
    ) throws -> (
870
        HeaderVariant, Int
871
    ) {
1,979✔
872
        var index = startIndex
1,979✔
873

1,979✔
874
        // Parse signature
1,979✔
875
        guard index < data.count else {
1,979✔
876
            throw DBusMessageError.invalidMessageFormat
×
877
        }
1,979✔
878

1,979✔
879
        let sigLength = Int(data[index])
1,979✔
880
        index += 1
1,979✔
881

1,979✔
882
        guard index + sigLength + 1 <= data.count else {
1,979✔
883
            throw DBusMessageError.invalidMessageFormat
×
884
        }
1,979✔
885

1,979✔
886
        let sigBytes = data[index..<index + sigLength]
1,979✔
887
        let sigString = String(bytes: sigBytes, encoding: .utf8) ?? ""
1,979✔
888
        index += sigLength + 1  // +1 for null terminator
1,979✔
889

1,979✔
890
        guard let signature = Signature(rawValue: sigString) else {
1,979✔
891
            throw DBusMessageError.invalidMessageFormat
×
892
        }
1,979✔
893

1,979✔
894
        // Align to value boundary
1,979✔
895
        let alignment = getStaticAlignment(for: signature)
1,979✔
896
        let padding = (alignment - (index % alignment)) % alignment
1,979✔
897
        index += padding
1,979✔
898

1,979✔
899
        // Parse value
1,979✔
900
        let (value, valueBytes) = try parseVariantValue(
1,979✔
901
            from: data, startingAt: index, signature: signature, endianness: endianness)
1,979✔
902
        index += valueBytes
1,979✔
903

1,979✔
904
        let headerValue = try HeaderValue(value)
1,979✔
905
        let variant = HeaderVariant(value: headerValue, signature: signature)
1,979✔
906
        return (variant, index - startIndex)
1,979✔
907
    }
1,979✔
908

909
    /// Parse a variant value from serialized D-Bus data based on its signature
910
    ///
911
    /// Extracts a typed value from D-Bus wire format according to the provided signature.
912
    /// Currently supports basic types: strings (s), object paths (o), unsigned 32-bit
913
    /// integers (u), and signatures (g).
914
    ///
915
    /// - Parameters:
916
    ///   - data: Byte array containing the value data
917
    ///   - startIndex: Starting position in the data array
918
    ///   - signature: D-Bus signature describing the value type
919
    ///   - endianness: Byte order for parsing multi-byte values
920
    /// - Returns: Tuple containing the parsed value and number of bytes consumed
921
    /// - Throws: `DBusMessageError.invalidMessageFormat` for malformed data or unsupported types
922
    private static func parseVariantValue(
923
        from data: [UInt8], startingAt startIndex: Int, signature: Signature, endianness: Endianness
924
    ) throws -> (Any, Int) {
1,981✔
925
        var index = startIndex
1,981✔
926

1,981✔
927
        switch signature.rawValue {
1,981✔
928
        case "s":  // string
1,981✔
929
            guard index + 4 <= data.count else {
1,251✔
930
                throw DBusMessageError.invalidMessageFormat
×
931
            }
1,251✔
932
            let length: UInt32
1,251✔
933
            if endianness == .littleEndian {
1,251✔
934
                length =
1,249✔
935
                    UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
1,249✔
936
                    | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
1,249✔
937
            } else {
1,249✔
938
                length =
2✔
939
                    (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
2✔
940
                    | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
2✔
941
            }
2✔
942
            index += 4
1,251✔
943

1,251✔
944
            guard index + Int(length) + 1 <= data.count else {
1,251✔
945
                throw DBusMessageError.invalidMessageFormat
×
946
            }
1,251✔
947

1,251✔
948
            let stringBytes = data[index..<index + Int(length)]
1,251✔
949
            let string = String(bytes: stringBytes, encoding: .utf8) ?? ""
1,251✔
950
            index += Int(length) + 1  // +1 for null terminator
1,251✔
951

1,251✔
952
            return (string, index - startIndex)
1,251✔
953

1,981✔
954
        case "o":  // object path
1,981✔
955
            guard index + 4 <= data.count else {
433✔
956
                throw DBusMessageError.invalidMessageFormat
×
957
            }
433✔
958
            let length: UInt32
433✔
959
            if endianness == .littleEndian {
433✔
960
                length =
432✔
961
                    UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
432✔
962
                    | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
432✔
963
            } else {
432✔
964
                length =
1✔
965
                    (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
1✔
966
                    | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
1✔
967
            }
1✔
968
            index += 4
433✔
969

433✔
970
            guard index + Int(length) + 1 <= data.count else {
433✔
971
                throw DBusMessageError.invalidMessageFormat
×
972
            }
433✔
973

433✔
974
            let pathBytes = data[index..<index + Int(length)]
433✔
975
            let pathString = String(bytes: pathBytes, encoding: .utf8) ?? ""
433✔
976
            index += Int(length) + 1  // +1 for null terminator
433✔
977

433✔
978
            let path = try ObjectPath(pathString)
433✔
979
            return (path, index - startIndex)
433✔
980

1,981✔
981
        case "u":  // uint32
1,981✔
982
            guard index + 4 <= data.count else {
42✔
983
                throw DBusMessageError.invalidMessageFormat
×
984
            }
42✔
985
            let value: UInt32
42✔
986
            if endianness == .littleEndian {
42✔
987
                value =
42✔
988
                    UInt32(data[index]) | (UInt32(data[index + 1]) << 8)
42✔
989
                    | (UInt32(data[index + 2]) << 16) | (UInt32(data[index + 3]) << 24)
42✔
990
            } else {
42✔
991
                value =
×
992
                    (UInt32(data[index]) << 24) | (UInt32(data[index + 1]) << 16)
×
993
                    | (UInt32(data[index + 2]) << 8) | UInt32(data[index + 3])
×
994
            }
×
995
            index += 4
42✔
996

42✔
997
            return (value, index - startIndex)
42✔
998

1,981✔
999
        case "g":  // signature
1,981✔
1000
            guard index < data.count else {
255✔
1001
                throw DBusMessageError.invalidMessageFormat
×
1002
            }
255✔
1003
            let length = Int(data[index])
255✔
1004
            index += 1
255✔
1005

255✔
1006
            guard index + length + 1 <= data.count else {
255✔
1007
                throw DBusMessageError.invalidMessageFormat
×
1008
            }
255✔
1009

255✔
1010
            let sigBytes = data[index..<index + length]
255✔
1011
            let sigString = String(bytes: sigBytes, encoding: .utf8) ?? ""
255✔
1012
            index += length + 1  // +1 for null terminator
255✔
1013

255✔
1014
            guard let sig = Signature(rawValue: sigString) else {
255✔
1015
                throw DBusMessageError.invalidMessageFormat
×
1016
            }
255✔
1017

255✔
1018
            return (sig, index - startIndex)
255✔
1019

1,981✔
1020
        default:
1,981✔
1021
            throw DBusMessageError.invalidMessageFormat
×
1022
        }
1,981✔
1023
    }
1,981✔
1024

1025
    /// Get the static alignment requirements for a D-Bus type signature
1026
    ///
1027
    /// Returns the byte alignment required for the given D-Bus type signature when
1028
    /// parsing values from serialized data. This is used to ensure proper alignment
1029
    /// when reading values from the wire format.
1030
    ///
1031
    /// - Parameter signature: The D-Bus type signature
1032
    /// - Returns: Alignment requirement in bytes (1, 2, 4, or 8)
1033
    private static func getStaticAlignment(for signature: Signature) -> Int {
1,980✔
1034
        // Return alignment requirements for different types
1,980✔
1035
        switch signature.rawValue {
1,980✔
1036
        case "y", "g": return 1
1,980✔
1037
        case "n", "q": return 2
1,980✔
1038
        case "b", "i", "u", "s", "o": return 4
1,980✔
1039
        case "x", "t", "d": return 8
1,980✔
1040
        default: return 1
1,980✔
1041
        }
1,980✔
1042
    }
1,980✔
1043
}
1044

1045
// MARK: - Validation
1046

1047
extension Message {
1048
    /// Validate message according to D-Bus specification
1049
    ///
1050
    /// Performs comprehensive validation of the message structure and content according
1051
    /// to the D-Bus specification. Checks serial number, protocol version, body length
1052
    /// consistency, required header fields for each message type, and message size limits.
1053
    ///
1054
    /// - Throws: Various `DBusMessageError` cases for validation failures including
1055
    ///           invalid serial, unsupported protocol version, body length mismatch,
1056
    ///           missing required header fields, or message too large
1057
    public func validate() throws {
35✔
1058
        // Check serial
35✔
1059
        guard serial != 0 else {
35✔
1060
            throw DBusMessageError.invalidSerial
×
1061
        }
35✔
1062

35✔
1063
        // Check protocol version
35✔
1064
        guard protocolVersion == 1 else {
35✔
1065
            throw DBusMessageError.unsupportedProtocolVersion(protocolVersion)
×
1066
        }
35✔
1067

35✔
1068
        // Check body length
35✔
1069
        guard bodyLength == UInt32(body.count) else {
35✔
1070
            throw DBusMessageError.invalidBodyLength
×
1071
        }
35✔
1072

35✔
1073
        // Validate required header fields for each message type
35✔
1074
        try validateRequiredHeaderFields()
35✔
1075

4✔
1076
        // Check maximum message size
4✔
1077
        let estimatedSize = body.count + (headerFields.count * 32) + 64  // rough estimate
4✔
1078
        guard estimatedSize <= 134_217_728 else {
4✔
1079
            throw DBusMessageError.messageTooLarge
×
1080
        }
4✔
1081
    }
4✔
1082

1083
    /// Validate that required header fields are present for the message type
1084
    ///
1085
    /// Checks that all mandatory header fields are present based on the message type:
1086
    /// - Method calls require path and member
1087
    /// - Method returns require replySerial
1088
    /// - Errors require errorName and replySerial
1089
    /// - Signals require path, interface, and member
1090
    ///
1091
    /// - Throws: `DBusMessageError.missingRequiredHeaderField` if a required field is missing
1092
    private func validateRequiredHeaderFields() throws {
35✔
1093
        switch messageType {
35✔
1094
        case .methodCall:
35✔
1095
            guard path != nil else {
30✔
1096
                throw DBusMessageError.missingRequiredHeaderField(.path)
26✔
1097
            }
26✔
1098
            guard member != nil else {
4✔
1099
                throw DBusMessageError.missingRequiredHeaderField(.member)
1✔
1100
            }
1✔
1101

35✔
1102
        case .methodReturn:
35✔
1103
            guard replySerial != nil else {
1✔
1104
                throw DBusMessageError.missingRequiredHeaderField(.replySerial)
1✔
1105
            }
1✔
1106

35✔
1107
        case .error:
35✔
1108
            guard errorName != nil else {
1✔
1109
                throw DBusMessageError.missingRequiredHeaderField(.errorName)
1✔
1110
            }
1✔
1111
            guard replySerial != nil else {
×
1112
                throw DBusMessageError.missingRequiredHeaderField(.replySerial)
×
1113
            }
×
1114

35✔
1115
        case .signal:
35✔
1116
            guard path != nil else {
3✔
1117
                throw DBusMessageError.missingRequiredHeaderField(.path)
1✔
1118
            }
2✔
1119
            guard interface != nil else {
2✔
1120
                throw DBusMessageError.missingRequiredHeaderField(.interface)
1✔
1121
            }
1✔
1122
            guard member != nil else {
1✔
1123
                throw DBusMessageError.missingRequiredHeaderField(.member)
×
1124
            }
×
1125
        }
35✔
1126
    }
4✔
1127
}
1128

1129
// MARK: - Errors
1130

1131
public enum DBusMessageError: Error, Equatable {
1132
    case invalidSerial
1133
    case unsupportedProtocolVersion(UInt8)
1134
    case invalidMessageFormat
1135
    case invalidEndianness
1136
    case invalidMessageType(UInt8)
1137
    case invalidBodyLength
1138
    case messageTooLarge
1139
    case serializationFailed
1140
    case missingRequiredHeaderField(DBusHeaderField)
1141
}
1142

1143
// MARK: - Equatable
1144

1145
extension Message: Equatable {
1146
    public static func == (lhs: Message, rhs: Message) -> Bool {
×
1147
        return lhs.endianness == rhs.endianness && lhs.messageType == rhs.messageType
×
1148
            && lhs.flags == rhs.flags && lhs.protocolVersion == rhs.protocolVersion
×
1149
            && lhs.bodyLength == rhs.bodyLength && lhs.serial == rhs.serial
×
1150
            && lhs.headerFields.count == rhs.headerFields.count && lhs.body == rhs.body
×
1151
            && lhs.bodySignature == rhs.bodySignature
×
1152
    }
×
1153
}
1154

1155
extension DBusHeaderFieldEntry: Equatable {
1156
    public static func == (lhs: DBusHeaderFieldEntry, rhs: DBusHeaderFieldEntry) -> Bool {
×
1157
        return lhs.field == rhs.field && lhs.value == rhs.value
×
1158
    }
×
1159
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc