Skip to content

Commit d06a666

Browse files
committed
Make ATTError an extensible struct
Closes #200.
1 parent f6e6982 commit d06a666

3 files changed

Lines changed: 73 additions & 23 deletions

File tree

Sources/BluetoothGATT/ATTError.swift

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,94 +14,125 @@ import Foundation
1414
///
1515
/// These error constants are based on the Bluetooth ATT error codes, defined in the Bluetooth 4.0 specification.
1616
/// For more information about these errors, see the Bluetooth 4.0 specification, Volume 3, Part F, Section 3.4.1.1.
17-
@frozen
18-
public enum ATTError: UInt8, Error {
17+
18+
public struct ATTError: RawRepresentable, Equatable, Hashable, Error {
19+
public typealias RawValue = UInt8
20+
21+
public var rawValue: UInt8
22+
23+
public init(rawValue: UInt8) {
24+
self.rawValue = rawValue
25+
}
26+
1927

2028
/// Invalid Handle
2129
///
2230
/// The attribute handle given was not valid on this server.
23-
case invalidHandle = 0x01
31+
public static var invalidHandle: ATTError { ATTError(rawValue: 0x01) }
2432

2533
/// Read Not Permitted
2634
///
2735
/// The attribute cannot be read.
28-
case readNotPermitted = 0x02
36+
public static var readNotPermitted: ATTError { ATTError(rawValue: 0x02) }
2937

3038
/// Write Not Permitted
3139
///
3240
/// The attribute cannot be written.
33-
case writeNotPermitted = 0x03
41+
public static var writeNotPermitted: ATTError { ATTError(rawValue: 0x03) }
3442

3543
/// Invalid PDU
3644
///
3745
/// The attribute PDU was invalid.
38-
case invalidPDU = 0x04
46+
public static var invalidPDU: ATTError { ATTError(rawValue: 0x04) }
3947

4048
/// Insufficient Authentication
4149
///
4250
/// The attribute requires authentication before it can be read or written.
43-
case insufficientAuthentication = 0x05
51+
public static var insufficientAuthentication: ATTError { ATTError(rawValue: 0x05) }
4452

4553
/// Request Not Supported
4654
///
4755
/// Attribute server does not support the request received from the client.
48-
case requestNotSupported = 0x06
56+
public static var requestNotSupported: ATTError { ATTError(rawValue: 0x06) }
4957

5058
/// Invalid Offset
5159
///
5260
/// Offset specified was past the end of the attribute.
53-
case invalidOffset = 0x07
61+
public static var invalidOffset: ATTError { ATTError(rawValue: 0x07) }
5462

5563
/// Insufficient Authorization
5664
///
5765
/// The attribute requires authorization before it can be read or written.
58-
case insufficientAuthorization = 0x08
66+
public static var insufficientAuthorization: ATTError { ATTError(rawValue: 0x08) }
5967

6068
/// Prepare Queue Full
6169
///
6270
/// Too many prepare writes have been queued.
63-
case prepareQueueFull = 0x09
71+
public static var prepareQueueFull: ATTError { ATTError(rawValue: 0x09) }
6472

6573
/// Attribute Not Found
6674
///
6775
/// No attribute found within the given attribute handle range.
68-
case attributeNotFound = 0x0A
76+
public static var attributeNotFound: ATTError { ATTError(rawValue: 0x0A) }
6977

7078
/// Attribute Not Long
7179
///
7280
/// The attribute cannot be read or written using the *Read Blob Request*.
73-
case attributeNotLong = 0x0B
81+
public static var attributeNotLong: ATTError { ATTError(rawValue: 0x0B) }
7482

7583
/// Insufficient Encryption Key Size
7684
///
7785
/// The *Encryption Key Size* used for encrypting this link is insufficient.
78-
case insufficientEncryptionKeySize = 0x0C
86+
public static var insufficientEncryptionKeySize: ATTError { ATTError(rawValue: 0x0C) }
7987

8088
/// Invalid Attribute Value Length
8189
///
8290
/// The attribute value length is invalid for the operation.
83-
case invalidAttributeValueLength = 0x0D
91+
public static var invalidAttributeValueLength: ATTError { ATTError(rawValue: 0x0D) }
8492

8593
/// Unlikely Error
8694
///
8795
/// The attribute request that was requested has encountered an error that was unlikely,
8896
/// and therefore could not be completed as requested.
89-
case unlikelyError = 0x0E
97+
public static var unlikelyError: ATTError { ATTError(rawValue: 0x0E) }
9098

9199
/// Insufficient Encryption
92100
///
93101
/// The attribute requires encryption before it can be read or written.
94-
case insufficientEncryption = 0x0F
102+
public static var insufficientEncryption: ATTError { ATTError(rawValue: 0x0F) }
95103

96104
/// Unsupported Group Type
97105
///
98106
/// The attribute type is not a supported grouping attribute as defined by a higher layer specification.
99-
case unsupportedGroupType = 0x10
107+
public static var unsupportedGroupType: ATTError { ATTError(rawValue: 0x10) }
100108

101109
/// Insufficient Resources
102110
///
103111
/// Insufficient Resources to complete the request.
104-
case insufficientResources = 0x11
112+
public static var insufficientResources: ATTError { ATTError(rawValue: 0x11) }
113+
114+
115+
// Common Profile and Service Error Code Descriptions
116+
117+
/// Write Request Rejected
118+
///
119+
/// The requested write operation cannot be fulfilled for reasons other than permissions.
120+
public static var writeRequestRejected: ATTError { ATTError(rawValue: 0xFC) }
121+
122+
/// Client Characteristic Configuration Descriptor Improperly Configured
123+
///
124+
/// Client Characteristic Configuration descriptor is not configured according to the requirements of the profile or service.
125+
public static var cccdImproperlyConfigured: ATTError { ATTError(rawValue: 0xFD) }
126+
127+
/// Procedure Already in Progress
128+
///
129+
/// The profile or service request cannot be serviced because an operation that has been previously triggered is still in progress.
130+
public static var procedureAlreadyInProgress: ATTError { ATTError(rawValue: 0xFE) }
131+
132+
/// Out of Range
133+
///
134+
/// The attribute value is out of range as defined by a profile or service specification.
135+
public static var outOfRange: ATTError { ATTError(rawValue: 0xFF) }
105136
}
106137

107138
// MARK: - CustomStringConvertible
@@ -154,6 +185,16 @@ public extension ATTError {
154185
return "Unsupported Group Type"
155186
case .insufficientResources:
156187
return "Insufficient Resources"
188+
case .writeRequestRejected:
189+
return "Write Request Rejected"
190+
case .cccdImproperlyConfigured:
191+
return "Client Characteristic Configuration Descriptor Improperly Configured"
192+
case .procedureAlreadyInProgress:
193+
return "Procedure Already in Progress"
194+
case .outOfRange:
195+
return "Out of Range"
196+
default:
197+
return "ATTError 0x\(String(self.rawValue, radix: 16))"
157198
}
158199
}
159200

@@ -195,6 +236,16 @@ public extension ATTError {
195236
return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."
196237
case .insufficientResources:
197238
return "Insufficient Resources to complete the request."
239+
case .writeRequestRejected:
240+
return "The requested write operation could not be completed."
241+
case .cccdImproperlyConfigured:
242+
return "The Client Characteristic Configuration Descriptor is not configured correctly."
243+
case .procedureAlreadyInProgress:
244+
return "An operation is already in progress."
245+
case .outOfRange:
246+
return "The attribute value is out of range."
247+
default:
248+
return "ATTError error code \(String(self.rawValue, radix: 16))"
198249
}
199250
}
200251
#endif

Sources/BluetoothGATT/ATTErrorResponse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ extension ATTErrorResponse: DataConvertible {
4747

4848
guard data.count == Self.length,
4949
Self.validateOpcode(data),
50-
let request = ATTOpcode(rawValue: data[1]),
51-
let error = ATTError(rawValue: data[4])
50+
let request = ATTOpcode(rawValue: data[1])
5251
else { return nil }
5352

53+
let error = ATTError(rawValue: data[4])
5454
let attributeHandle = UInt16(littleEndian: UInt16(bytes: (data[2], data[3])))
5555

5656
self.init(request: request, attributeHandle: attributeHandle, error: error)

Tests/BluetoothTests/AttributeProtocolTests.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ import Foundation
3131
#expect(ATTError.invalidHandle.errorDescription == "The attribute handle given was not valid on this server.")
3232
#expect(ATTError.invalidHandle.description == ATTError.invalidHandle.name)
3333

34-
let errors = (1 ... .max).compactMap { ATTError(rawValue: $0) }
35-
#expect(errors.count == 0x11)
34+
let errors = (1 ... 0x11).compactMap { ATTError(rawValue: $0) }
3635

3736
for error in errors {
3837

0 commit comments

Comments
 (0)