Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,20 @@ jobs:
run: swift build --build-tests
- name: Test
run: swift test --skip-build

windows_swift_6_1:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Swift
uses: SwiftyLab/setup-swift@latest
with:
swift-version: "6.1.2"
visual-studio-components: Microsoft.VisualStudio.Component.Windows11SDK.22621
- name: Version
run: swift --version
- name: Build
run: swift build --build-tests
- name: Test
run: swift test --skip-build
53 changes: 37 additions & 16 deletions Sources/KeyValueDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
// SOFTWARE.
//

import CoreFoundation
import Foundation

/// Top level encoder that converts `[String: Any]`, `[Any]` or `Any` into `Codable` types.
Expand Down Expand Up @@ -779,22 +778,44 @@ extension NSNumber {
}
}

func getNumberTypeID() -> CFNumberType? {
guard CFGetTypeID(self as CFTypeRef) == CFNumberGetTypeID() else { return nil }
#if canImport(Darwin)
return CFNumberGetType(self as CFNumber)
#else
guard type(of: self) != type(of: NSNumber(true)) else { return nil }
enum NumberTypeID: Int, Sendable {
case sInt8Type = 1
case sInt16Type = 2
case sInt32Type = 3
case sInt64Type = 4
case float32Type = 5
case float64Type = 6
case charType = 7
case shortType = 8
case intType = 9
case longType = 10
case longLongType = 11
case floatType = 12
case doubleType = 13
case cfIndexType = 14
case nsIntegerType = 15
case cgFloatType = 16
}

func getNumberTypeID() -> NumberTypeID? {
// Prevent misclassifying Bool as charType
if type(of: self) == type(of: NSNumber(value: true)) { return nil }

switch String(cString: objCType) {
case "c": return CFNumberType.charType
case "s": return CFNumberType.shortType
case "i": return CFNumberType.intType
case "q": return CFNumberType.longLongType
case "d": return CFNumberType.doubleType
case "f": return CFNumberType.floatType
case "Q": return CFNumberType.longLongType
default: return nil
case "c": return .charType
case "C": return .sInt8Type
case "s": return .shortType
case "S": return .sInt16Type
case "i": return .intType
case "I": return .sInt32Type
case "l": return .longType
case "L": return .sInt32Type
case "q": return .longLongType
case "Q": return .sInt64Type
case "f": return .floatType
case "d": return .doubleType
case "B": return nil
default: return nil
}
#endif
}
}
45 changes: 44 additions & 1 deletion Tests/KeyValueDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,47 @@ struct KeyValueDecoderTests {
#expect((true as NSNumber).getDoubleValue() == nil)
}

@Test
#if compiler(>=6.1)
@Test()
func decodingErrors() throws {

var error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(Seafood.self, from: 10)
}
#expect(error?.debugDescription == "Expected String at SELF, found Int")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(Seafood.self, from: 10)
}
#expect(error?.debugDescription == "Expected String at SELF, found Int")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(Int.self, from: NSNull())
}
#expect(error?.debugDescription == "Expected BinaryInteger at SELF, found NSNull")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(Int.self, from: Optional<Int>.none)
}
#expect(error?.debugDescription == "Expected BinaryInteger at SELF, found nil")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode([Int].self, from: [0, 1, true] as [Any])
}
#expect(error?.debugDescription == "Expected BinaryInteger at SELF[2], found Bool")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(AllTypes.self, from: ["tArray": [["tString": 0]]] as [String: Any])
}
#expect(error?.debugDescription == "Expected String at SELF.tArray[0].tString, found Int")

error = #expect(throws: DecodingError.self) {
try KeyValueDecoder.decode(AllTypes.self, from: ["tArray": [["tString": 0]]] as [String: Any])
}
#expect(error?.debugDescription == "Expected String at SELF.tArray[0].tString, found Int")
}
#else
@Test()
func decodingErrors() throws {
expectDecodingError(try KeyValueDecoder.decode(Seafood.self, from: 10)) { error in
#expect(error.debugDescription == "Expected String at SELF, found Int")
Expand All @@ -913,6 +953,7 @@ struct KeyValueDecoderTests {
#expect(error.debugDescription == "Expected String at SELF.tArray[0].tString, found Int")
}
}
#endif

@Test
func int_ClampsDoubles() {
Expand Down Expand Up @@ -997,6 +1038,7 @@ struct KeyValueDecoderTests {
#endif
}

#if compiler(<6.1)
func expectDecodingError<T>(_ expression: @autoclosure () throws -> T,
file: String = #filePath,
line: Int = #line,
Expand All @@ -1012,6 +1054,7 @@ func expectDecodingError<T>(_ expression: @autoclosure () throws -> T,
Issue.record(error, "Expected DecodingError", sourceLocation: location)
}
}
#endif

extension DecodingError {

Expand Down
Loading