Skip to content

Commit 9321cae

Browse files
authored
Merge branch 'vkuttyp:main' into TestOptimisation
2 parents b9c6e99 + 6be7100 commit 9321cae

3 files changed

Lines changed: 91 additions & 3 deletions

File tree

.github/workflows/swift.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
name: Build & Test
12+
runs-on: macos-14 # Apple Silicon runner, Xcode 15+
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
18+
- name: Select Xcode
19+
run: sudo xcode-select -s /Applications/Xcode_15.4.app
20+
21+
- name: Install FreeTDS
22+
run: brew install freetds
23+
24+
- name: Build
25+
run: swift build -v
26+
27+
- name: Test
28+
run: swift test -v
29+
env:
30+
# Integration tests are skipped automatically when these are not set.
31+
# To run them, add HOST / USERNAME / PASSWORD / DATABASE as
32+
# GitHub Actions secrets (Settings → Secrets → Actions).
33+
HOST: ${{ secrets.DB_HOST }}
34+
USERNAME: ${{ secrets.DB_USERNAME }}
35+
PASSWORD: ${{ secrets.DB_PASSWORD }}
36+
DATABASE: ${{ secrets.DB_DATABASE }}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Veeran Kutty
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Sources/SQLClientSwift/SQLRowDecoder.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,13 @@ private struct SQLRowKeyedContainer<Key: CodingKey>: KeyedDecodingContainerProto
149149
}
150150

151151
private func cast<T>(_ key: Key) throws -> T {
152+
// Validate key is in columns
152153
let value = try requireValue(for: key)
154+
155+
// Direct cast as type
153156
if let v = value as? T { return v }
157+
158+
// If type is NSNumber supported
154159
if let n = value as? NSNumber {
155160
switch T.self {
156161
case is Bool.Type: return (n.boolValue) as! T
@@ -169,14 +174,40 @@ private struct SQLRowKeyedContainer<Key: CodingKey>: KeyedDecodingContainerProto
169174
default: break
170175
}
171176
}
177+
// String → primitive conversions.
172178
if let s = value as? String {
173179
switch T.self {
174-
case is Int.Type: if let i = Int(s) { return i as! T }
175-
case is Double.Type: if let d = Double(s) { return d as! T }
176-
case is Bool.Type: return (["true","1","yes"].contains(s.lowercased())) as! T
180+
case is Int.Type:
181+
guard let i = Int(s) else {
182+
throw DecodingError.typeMismatch(T.self, .init(
183+
codingPath: codingPath + [key],
184+
debugDescription: "Cannot convert '\(s)' to Int for column '\(key.stringValue)'."))
185+
}
186+
return i as! T
187+
188+
case is Double.Type:
189+
guard let d = Double(s) else {
190+
throw DecodingError.typeMismatch(T.self, .init(
191+
codingPath: codingPath + [key],
192+
debugDescription: "Cannot convert '\(s)' to Double for column '\(key.stringValue)'."))
193+
}
194+
return d as! T
195+
196+
case is Bool.Type:
197+
switch s.lowercased() {
198+
case "true", "1", "yes": return true as! T
199+
case "false", "0", "no": return false as! T
200+
default:
201+
throw DecodingError.typeMismatch(T.self, .init(
202+
codingPath: codingPath + [key],
203+
debugDescription: "Cannot convert '\(s)' to Bool for column '\(key.stringValue)'. "
204+
+ "Expected one of: true, false, 1, 0, yes, no."))
205+
}
206+
177207
default: break
178208
}
179209
}
210+
// No Conversion path succeeded
180211
throw DecodingError.typeMismatch(T.self, .init(
181212
codingPath: codingPath + [key],
182213
debugDescription: "Expected \(T.self), got \(type(of: value)) for column '\(key.stringValue)'"))

0 commit comments

Comments
 (0)