Skip to content

Commit e71e466

Browse files
authored
Merge pull request #1 from Joannis/jo/stricter-unit-tests
Minimize use of Foundation and leverage typed throws
2 parents 4ce8084 + e2fb1f6 commit e71e466

File tree

8 files changed

+57
-127
lines changed

8 files changed

+57
-127
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ do {
9292

9393
```swift
9494
import CBOR
95-
import Foundation
9695

9796
// Define your data structures
9897
struct Person: Codable {

Sources/CBOR/CBOR.swift

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
#if canImport(FoundationEssentials)
2-
import FoundationEssentials
3-
#else
4-
import Foundation
5-
#endif
6-
71
#if canImport(Darwin)
82
import Darwin
93
#elseif canImport(Glibc)
104
import Glibc
5+
#elseif canImport(Musl)
6+
import Musl
117
#elseif os(Windows)
128
import ucrt
139
#endif
1410

1511
// MARK: - CBOR Type
1612

1713
/// A CBOR value
18-
public indirect enum CBOR: Equatable {
14+
public indirect enum CBOR: Equatable, Sendable {
1915
/// A positive unsigned integer
2016
case unsignedInt(UInt64)
2117
/// A negative integer
@@ -49,7 +45,7 @@ public indirect enum CBOR: Equatable {
4945
}
5046

5147
/// Decodes a CBOR value from bytes
52-
public static func decode(_ bytes: [UInt8]) throws -> CBOR {
48+
public static func decode(_ bytes: [UInt8]) throws(CBORError) -> CBOR {
5349
var reader = CBORReader(data: bytes)
5450
let value = try _decode(reader: &reader)
5551

@@ -67,7 +63,7 @@ public indirect enum CBOR: Equatable {
6763
/// - Parameters:
6864
/// - key: The key of the pair
6965
/// - value: The value of the pair
70-
public struct CBORMapPair: Equatable {
66+
public struct CBORMapPair: Equatable, Sendable {
7167
public let key: CBOR
7268
public let value: CBOR
7369

@@ -103,11 +99,9 @@ private func _encode(_ value: CBOR, into output: inout [UInt8]) {
10399
encodeUnsigned(major: 2, value: UInt64(bytes.count), into: &output)
104100
output.append(contentsOf: bytes)
105101
case .textString(let string):
106-
if let utf8 = string.data(using: .utf8) {
107-
let bytes = [UInt8](utf8)
108-
encodeUnsigned(major: 3, value: UInt64(bytes.count), into: &output)
109-
output.append(contentsOf: bytes)
110-
}
102+
let bytes = [UInt8](string.utf8)
103+
encodeUnsigned(major: 3, value: UInt64(bytes.count), into: &output)
104+
output.append(contentsOf: bytes)
111105
case .array(let array):
112106
encodeUnsigned(major: 4, value: UInt64(array.count), into: &output)
113107
for item in array {
@@ -138,8 +132,7 @@ private func _encode(_ value: CBOR, into output: inout [UInt8]) {
138132
case .float(let f):
139133
// Encode as IEEE 754 double-precision float
140134
output.append(0xfb)
141-
var value = f
142-
withUnsafeBytes(of: &value) { bytes in
135+
withUnsafeBytes(of: f) { bytes in
143136
// Append bytes in big-endian order
144137
for i in (0..<8).reversed() {
145138
output.append(bytes[i])
@@ -192,7 +185,7 @@ private func encodeUnsigned(major: UInt8, value: UInt64, into output: inout [UIn
192185
/// - reader: The reader to decode from
193186
/// - Returns: The decoded CBOR value
194187
/// - Throws: A `CBORError` if the decoding fails
195-
private func _decode(reader: inout CBORReader) throws -> CBOR {
188+
private func _decode(reader: inout CBORReader) throws(CBORError) -> CBOR {
196189
let initial = try reader.readByte()
197190

198191
// Check for break marker (0xff)
@@ -321,7 +314,7 @@ private func _decode(reader: inout CBORReader) throws -> CBOR {
321314
}
322315

323316
/// Reads an unsigned integer value based on the additional information.
324-
private func readUIntValue(additional: UInt8, reader: inout CBORReader) throws -> UInt64 {
317+
private func readUIntValue(additional: UInt8, reader: inout CBORReader) throws(CBORError) -> UInt64 {
325318
// Check for indefinite length first
326319
if additional == 31 {
327320
throw CBORError.indefiniteLengthNotSupported

Sources/CBOR/CBORCodable.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#if !hasFeature(Embedded)
12
#if canImport(FoundationEssentials)
23
import FoundationEssentials
34
#elseif canImport(Foundation)
@@ -155,3 +156,4 @@ struct CBORKey: CodingKey {
155156
self.intValue = index
156157
}
157158
}
159+
#endif

Sources/CBOR/CBORDecoder.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#if !hasFeature(Embedded)
12
#if canImport(FoundationEssentials)
23
import FoundationEssentials
34
#elseif canImport(Foundation)
@@ -1917,3 +1918,4 @@ private struct CBORSingleValueDecodingContainer: SingleValueDecodingContainer {
19171918
return try T(from: decoder)
19181919
}
19191920
}
1921+
#endif

Sources/CBOR/CBOREncoder.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#if !hasFeature(Embedded)
12
#if canImport(FoundationEssentials)
23
import FoundationEssentials
34
#elseif canImport(Foundation)
@@ -771,3 +772,4 @@ private struct CBOREncoderSingleValueContainer: SingleValueEncodingContainer {
771772
try encoder.push(encoder.encodeCBOR(value))
772773
}
773774
}
775+
#endif

Sources/CBOR/CBORError.swift

Lines changed: 9 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,24 @@
1-
#if canImport(FoundationEssentials)
2-
import FoundationEssentials
3-
#elseif canImport(Foundation)
4-
import Foundation
5-
#endif
6-
71
// MARK: - Error Types
82

93
/// Errors that can occur during CBOR encoding and decoding.
104
///
115
/// These errors provide detailed information about what went wrong during
126
/// CBOR processing operations, helping developers diagnose and fix issues
137
/// in their CBOR data or usage of the CBOR API.
14-
public enum CBORError: Error {
8+
public enum CBORError: Error, Equatable, Sendable {
159
/// The input data is not valid CBOR.
1610
///
1711
/// This error occurs when the decoder encounters data that doesn't conform to
1812
/// the CBOR specification (RFC 8949). This could be due to corrupted data,
1913
/// incomplete data, or data encoded with a different format entirely.
2014
case invalidCBOR
2115

22-
/// Expected a specific type but found another.
23-
///
24-
/// This error occurs when trying to decode a CBOR value as a specific type,
25-
/// but the actual type of the value doesn't match the expected type.
26-
/// - Parameters:
27-
/// - expected: The type that was expected (e.g., "String", "Int", "Array")
28-
/// - actual: The actual type that was found in the CBOR data
29-
case typeMismatch(expected: String, actual: String)
30-
31-
/// Array index out of bounds.
32-
///
33-
/// This error occurs when attempting to access an element in a CBOR array
34-
/// using an index that is outside the valid range for the array.
35-
/// - Parameters:
36-
/// - index: The requested index that was attempted to be accessed
37-
/// - count: The actual number of elements in the array (valid indices are 0..<count)
38-
case outOfBounds(index: Int, count: Int)
39-
40-
/// Required key missing from map.
41-
///
42-
/// This error occurs when trying to decode a CBOR map into a Swift struct or class,
43-
/// but a required key is not present in the map.
44-
/// - Parameter key: The name of the missing key
45-
case missingKey(String)
46-
47-
/// Value conversion failed.
48-
///
49-
/// This error occurs when a CBOR value cannot be converted to the requested Swift type,
50-
/// even though the CBOR type is compatible with the requested type.
51-
/// - Parameter message: A description of what went wrong during the conversion
52-
case valueConversionFailed(String)
53-
5416
/// Invalid UTF-8 string data.
5517
///
5618
/// This error occurs when decoding a CBOR text string that contains invalid UTF-8 sequences.
5719
/// All CBOR text strings must contain valid UTF-8 data according to the specification.
5820
case invalidUTF8
5921

60-
/// Integer overflow during encoding/decoding.
61-
///
62-
/// This error occurs when a CBOR integer value is too large to fit into the
63-
/// corresponding Swift integer type (e.g., trying to decode a UInt64.max into an Int).
64-
case integerOverflow
65-
66-
/// Tag value is not supported.
67-
///
68-
/// This error occurs when the decoder encounters a CBOR tag that is not supported
69-
/// by the current implementation.
70-
/// - Parameter tag: The unsupported tag number
71-
case unsupportedTag(UInt64)
72-
7322
/// Reached end of data while decoding.
7423
///
7524
/// This error occurs when the decoder unexpectedly reaches the end of the input data
@@ -105,26 +54,15 @@ public enum CBORError: Error {
10554
case extraDataFound
10655
}
10756

57+
@_unavailableInEmbedded
10858
extension CBORError: CustomStringConvertible {
10959
/// A human-readable description of the error.
11060
public var description: String {
11161
switch self {
11262
case .invalidCBOR:
11363
return "Invalid CBOR data: The input does not conform to the CBOR specification (RFC 8949)"
114-
case .typeMismatch(let expected, let actual):
115-
return "Type mismatch: expected \(expected), found \(actual)"
116-
case .outOfBounds(let index, let count):
117-
return "Array index out of bounds: attempted to access index \(index), but array only contains \(count) elements (valid indices are 0..<\(count))"
118-
case .missingKey(let key):
119-
return "Missing key: required key '\(key)' was not found in the CBOR map"
120-
case .valueConversionFailed(let message):
121-
return "Value conversion failed: \(message)"
12264
case .invalidUTF8:
12365
return "Invalid UTF-8 data: the CBOR text string contains invalid UTF-8 sequences"
124-
case .integerOverflow:
125-
return "Integer overflow: the CBOR integer value is too large for the target Swift integer type"
126-
case .unsupportedTag(let tag):
127-
return "Unsupported tag: tag \(tag) is not supported by this implementation"
12866
case .prematureEnd:
12967
return "Unexpected end of data: reached the end of input before completing the CBOR value"
13068
case .invalidInitialByte(let byte):
@@ -139,6 +77,13 @@ extension CBORError: CustomStringConvertible {
13977
}
14078
}
14179

80+
#if canImport(FoundationEssentials)
81+
import FoundationEssentials
82+
#elseif canImport(Foundation)
83+
import Foundation
84+
#endif
85+
86+
@_unavailableInEmbedded
14287
extension CBORError: LocalizedError {
14388
public var errorDescription: String? {
14489
return description

Sources/CBOR/CBORReader.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#if canImport(FoundationEssentials)
2-
import FoundationEssentials
3-
#elseif canImport(Foundation)
4-
import Foundation
5-
#endif
61

72
/// A helper struct for reading CBOR data byte by byte
83
struct CBORReader {
@@ -15,7 +10,7 @@ struct CBORReader {
1510
}
1611

1712
/// Read a single byte from the input
18-
mutating func readByte() throws -> UInt8 {
13+
mutating func readByte() throws(CBORError) -> UInt8 {
1914
guard index < data.count else {
2015
throw CBORError.prematureEnd
2116
}
@@ -25,7 +20,7 @@ struct CBORReader {
2520
}
2621

2722
/// Read a specified number of bytes from the input
28-
mutating func readBytes(_ count: Int) throws -> [UInt8] {
23+
mutating func readBytes(_ count: Int) throws(CBORError) -> [UInt8] {
2924
guard index + count <= data.count else {
3025
throw CBORError.prematureEnd
3126
}
@@ -50,7 +45,7 @@ struct CBORReader {
5045
}
5146

5247
/// Skip a specified number of bytes
53-
mutating func skip(_ count: Int) throws {
48+
mutating func skip(_ count: Int) throws(CBORError) {
5449
guard index + count <= data.count else {
5550
throw CBORError.prematureEnd
5651
}

0 commit comments

Comments
 (0)