Skip to content

Commit d980833

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

2 files changed

Lines changed: 101 additions & 21 deletions

File tree

Sources/BluetoothGATT/ATTError.swift

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,94 +14,155 @@ 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+
enum Code: UInt8 {
22+
case invalidHandle = 0x01
23+
case readNotPermitted = 0x02
24+
case writeNotPermitted = 0x03
25+
case invalidPDU = 0x04
26+
case insufficientAuthentication = 0x05
27+
case requestNotSupported = 0x06
28+
case invalidOffset = 0x07
29+
case insufficientAuthorization = 0x08
30+
case prepareQueueFull = 0x09
31+
case attributeNotFound = 0x0A
32+
case attributeNotLong = 0x0B
33+
case insufficientEncryptionKeySize = 0x0C
34+
case invalidAttributeValueLength = 0x0D
35+
case unlikelyError = 0x0E
36+
case insufficientEncryption = 0x0F
37+
case unsupportedGroupType = 0x10
38+
case insufficientResources = 0x11
39+
40+
case writeRequestRejected = 0xFC
41+
case cccdImproperlyConfigured = 0xFD
42+
case procedureAlreadyInProgress = 0xFE
43+
case outOfRange = 0xFF
44+
}
45+
46+
public var rawValue: UInt8
47+
48+
public init?(rawValue: UInt8) {
49+
if rawValue == 0 { return nil } // This is the success case
50+
self.rawValue = rawValue
51+
}
52+
53+
init(code: Code) {
54+
self.rawValue = code.rawValue
55+
}
56+
1957

2058
/// Invalid Handle
2159
///
2260
/// The attribute handle given was not valid on this server.
23-
case invalidHandle = 0x01
61+
public static let invalidHandle = ATTError(code: .invalidHandle)
2462

2563
/// Read Not Permitted
2664
///
2765
/// The attribute cannot be read.
28-
case readNotPermitted = 0x02
66+
public static let readNotPermitted = ATTError(code: .readNotPermitted)
2967

3068
/// Write Not Permitted
3169
///
3270
/// The attribute cannot be written.
33-
case writeNotPermitted = 0x03
71+
public static let writeNotPermitted = ATTError(code: .writeNotPermitted)
3472

3573
/// Invalid PDU
3674
///
3775
/// The attribute PDU was invalid.
38-
case invalidPDU = 0x04
76+
public static let invalidPDU = ATTError(code: .invalidPDU)
3977

4078
/// Insufficient Authentication
4179
///
4280
/// The attribute requires authentication before it can be read or written.
43-
case insufficientAuthentication = 0x05
81+
public static let insufficientAuthentication = ATTError(code: .insufficientAuthentication)
4482

4583
/// Request Not Supported
4684
///
4785
/// Attribute server does not support the request received from the client.
48-
case requestNotSupported = 0x06
86+
public static let requestNotSupported = ATTError(code: .requestNotSupported)
4987

5088
/// Invalid Offset
5189
///
5290
/// Offset specified was past the end of the attribute.
53-
case invalidOffset = 0x07
91+
public static let invalidOffset = ATTError(code: .invalidOffset)
5492

5593
/// Insufficient Authorization
5694
///
5795
/// The attribute requires authorization before it can be read or written.
58-
case insufficientAuthorization = 0x08
96+
public static let insufficientAuthorization = ATTError(code: .insufficientAuthorization)
5997

6098
/// Prepare Queue Full
6199
///
62100
/// Too many prepare writes have been queued.
63-
case prepareQueueFull = 0x09
101+
public static let prepareQueueFull = ATTError(code: .prepareQueueFull)
64102

65103
/// Attribute Not Found
66104
///
67105
/// No attribute found within the given attribute handle range.
68-
case attributeNotFound = 0x0A
106+
public static let attributeNotFound = ATTError(code: .attributeNotFound)
69107

70108
/// Attribute Not Long
71109
///
72110
/// The attribute cannot be read or written using the *Read Blob Request*.
73-
case attributeNotLong = 0x0B
111+
public static let attributeNotLong = ATTError(code: .attributeNotLong)
74112

75113
/// Insufficient Encryption Key Size
76114
///
77115
/// The *Encryption Key Size* used for encrypting this link is insufficient.
78-
case insufficientEncryptionKeySize = 0x0C
116+
public static let insufficientEncryptionKeySize = ATTError(code: .insufficientEncryptionKeySize)
79117

80118
/// Invalid Attribute Value Length
81119
///
82120
/// The attribute value length is invalid for the operation.
83-
case invalidAttributeValueLength = 0x0D
121+
public static let invalidAttributeValueLength = ATTError(code: .invalidAttributeValueLength)
84122

85123
/// Unlikely Error
86124
///
87125
/// The attribute request that was requested has encountered an error that was unlikely,
88126
/// and therefore could not be completed as requested.
89-
case unlikelyError = 0x0E
127+
public static let unlikelyError = ATTError(code: .unlikelyError)
90128

91129
/// Insufficient Encryption
92130
///
93131
/// The attribute requires encryption before it can be read or written.
94-
case insufficientEncryption = 0x0F
132+
public static let insufficientEncryption = ATTError(code: .insufficientEncryption)
95133

96134
/// Unsupported Group Type
97135
///
98136
/// The attribute type is not a supported grouping attribute as defined by a higher layer specification.
99-
case unsupportedGroupType = 0x10
137+
public static let unsupportedGroupType = ATTError(code: .unsupportedGroupType)
100138

101139
/// Insufficient Resources
102140
///
103141
/// Insufficient Resources to complete the request.
104-
case insufficientResources = 0x11
142+
public static let insufficientResources = ATTError(code: .insufficientResources)
143+
144+
145+
// Common Profile and Service Error Code Descriptions
146+
147+
/// Write Request Rejected
148+
///
149+
/// The requested write operation cannot be fulfilled for reasons other than permissions.
150+
public static let writeRequestRejected = ATTError(code: .writeRequestRejected)
151+
152+
/// Client Characteristic Configuration Descriptor Improperly Configured
153+
///
154+
/// Client Characteristic Configuration descriptor is not configured according to the requirements of the profile or service.
155+
public static let cccdImproperlyConfigured = ATTError(code: .cccdImproperlyConfigured)
156+
157+
/// Procedure Already in Progress
158+
///
159+
/// The profile or service request cannot be serviced because an operation that has been previously triggered is still in progress.
160+
public static let procedureAlreadyInProgress = ATTError(code: .procedureAlreadyInProgress)
161+
162+
/// Out of Range
163+
///
164+
/// The attribute value is out of range as defined by a profile or service specification.
165+
public static let outOfRange = ATTError(code: .outOfRange)
105166
}
106167

107168
// MARK: - CustomStringConvertible
@@ -154,6 +215,16 @@ public extension ATTError {
154215
return "Unsupported Group Type"
155216
case .insufficientResources:
156217
return "Insufficient Resources"
218+
case .writeRequestRejected:
219+
return "Write Request Rejected"
220+
case .cccdImproperlyConfigured:
221+
return "Client Characteristic Configuration Descriptor Improperly Configured"
222+
case .procedureAlreadyInProgress:
223+
return "Procedure Already in Progress"
224+
case .outOfRange:
225+
return "Out of Range"
226+
default:
227+
return "ATTError 0x\(String(self.rawValue, radix: 16))"
157228
}
158229
}
159230

@@ -195,6 +266,16 @@ public extension ATTError {
195266
return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."
196267
case .insufficientResources:
197268
return "Insufficient Resources to complete the request."
269+
case .writeRequestRejected:
270+
return "The requested write operation could not be completed."
271+
case .cccdImproperlyConfigured:
272+
return "The Client Characteristic Configuration Descriptor is not configured correctly."
273+
case .procedureAlreadyInProgress:
274+
return "An operation is already in progress."
275+
case .outOfRange:
276+
return "The attribute value is out of range."
277+
default:
278+
return "ATTError error code \(String(self.rawValue, radix: 16))"
198279
}
199280
}
200281
#endif

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)