Skip to content

Commit 31eff84

Browse files
g-cqdclaude
andcommitted
Fix protocol hierarchy to prevent duplicate conformance issue
Refactored CSVIndexedCodable protocol hierarchy to use a shared base protocol (CSVIndexedBase) that provides the common requirements: - CSVCodingKeys associatedtype - csvColumnOrder static property - _csvColumnOrder marker property Both CSVIndexedDecodable and CSVIndexedEncodable now refine CSVIndexedBase, eliminating the "multiple matching properties" error when a type conforms to both protocols (via CSVIndexedCodable). Also unified the internal marker protocol to _CSVIndexedMarker for consistent runtime detection. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a5b5b1a commit 31eff84

3 files changed

Lines changed: 29 additions & 44 deletions

File tree

Sources/CSVCoder/CSVDecoder+Streaming.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extension CSVDecoder {
3232
from url: URL
3333
) -> AsyncThrowingStream<T, Error> {
3434
// Runtime detection of CSVIndexedDecodable conformance
35-
let columnOrder = (T.self as? _CSVIndexedDecodableMarker.Type)?._csvColumnOrder
35+
let columnOrder = (T.self as? _CSVIndexedMarker.Type)?._csvColumnOrder
3636
return streamDecode(type, from: url, columnOrder: columnOrder)
3737
}
3838

@@ -51,7 +51,7 @@ extension CSVDecoder {
5151
from data: Data
5252
) -> AsyncThrowingStream<T, Error> {
5353
// Runtime detection of CSVIndexedDecodable conformance
54-
let columnOrder = (T.self as? _CSVIndexedDecodableMarker.Type)?._csvColumnOrder
54+
let columnOrder = (T.self as? _CSVIndexedMarker.Type)?._csvColumnOrder
5555
return streamDecode(type, from: data, columnOrder: columnOrder)
5656
}
5757

Sources/CSVCoder/CSVDecoder.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ public final class CSVDecoder: Sendable {
206206
/// - Returns: An array of decoded values.
207207
public func decode<T: Decodable>(_ type: [T].Type, from data: Data) throws -> [T] {
208208
// Runtime detection of CSVIndexedDecodable conformance
209-
let columnOrder = (T.self as? _CSVIndexedDecodableMarker.Type)?._csvColumnOrder
210-
209+
let columnOrder = (T.self as? _CSVIndexedMarker.Type)?._csvColumnOrder
210+
211211
// Fast path: Zero-copy decoding for UTF-8 data
212212
return try decodeRowsFromBytes(type, from: data, columnOrder: columnOrder)
213213
}
@@ -223,7 +223,7 @@ public final class CSVDecoder: Sendable {
223223
/// - Returns: An array of decoded values.
224224
public func decode<T: Decodable>(_ type: [T].Type, from string: String) throws -> [T] {
225225
// Runtime detection of CSVIndexedDecodable conformance
226-
let columnOrder = (T.self as? _CSVIndexedDecodableMarker.Type)?._csvColumnOrder
226+
let columnOrder = (T.self as? _CSVIndexedMarker.Type)?._csvColumnOrder
227227
return try decodeRows(type, from: string, columnOrder: columnOrder)
228228
}
229229

Sources/CSVCoder/CSVIndexedCodable.swift

Lines changed: 24 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,31 @@ import Foundation
1313
/// Internal marker protocol for runtime conformance detection.
1414
/// Has no associated types, enabling `as?` casting at runtime.
1515
/// Public to allow protocol refinement but prefixed with underscore to signal internal use.
16-
public protocol _CSVIndexedDecodableMarker {
16+
public protocol _CSVIndexedMarker {
1717
static var _csvColumnOrder: [String] { get }
1818
}
1919

20-
/// Internal marker for encodable types.
21-
public protocol _CSVIndexedEncodableMarker {
22-
static var _csvColumnOrder: [String] { get }
20+
// MARK: - Base Protocol
21+
22+
/// Base protocol providing the common requirements for CSV indexed types.
23+
/// Both CSVIndexedDecodable and CSVIndexedEncodable refine this protocol.
24+
public protocol CSVIndexedBase: _CSVIndexedMarker {
25+
/// The CodingKeys type, which must be CaseIterable to define column order.
26+
associatedtype CSVCodingKeys: CodingKey & CaseIterable
27+
28+
/// Returns the ordered column names derived from CodingKeys.
29+
/// Default implementation uses `CSVCodingKeys.allCases`.
30+
static var csvColumnOrder: [String] { get }
31+
}
32+
33+
extension CSVIndexedBase {
34+
/// Default implementation: extracts column names from CodingKeys.allCases in order.
35+
public static var csvColumnOrder: [String] {
36+
CSVCodingKeys.allCases.map { $0.stringValue }
37+
}
38+
39+
/// Internal marker implementation for runtime detection.
40+
public static var _csvColumnOrder: [String] { csvColumnOrder }
2341
}
2442

2543
// MARK: - CSVIndexedDecodable Protocol
@@ -49,47 +67,14 @@ public protocol _CSVIndexedEncodableMarker {
4967
///
5068
/// - Note: The decoder automatically detects conformance at runtime.
5169
/// You can use the standard `decode([T].self, from:)` method.
52-
public protocol CSVIndexedDecodable: Decodable, _CSVIndexedDecodableMarker {
53-
/// The CodingKeys type, which must be CaseIterable to define column order.
54-
associatedtype CSVCodingKeys: CodingKey & CaseIterable
55-
56-
/// Returns the ordered column names derived from CodingKeys.
57-
/// Default implementation uses `CSVCodingKeys.allCases`.
58-
static var csvColumnOrder: [String] { get }
59-
}
60-
61-
extension CSVIndexedDecodable {
62-
/// Default implementation: extracts column names from CodingKeys.allCases in order.
63-
public static var csvColumnOrder: [String] {
64-
CSVCodingKeys.allCases.map { $0.stringValue }
65-
}
66-
67-
/// Internal marker implementation for runtime detection.
68-
static var _csvColumnOrder: [String] { csvColumnOrder }
69-
}
70+
public protocol CSVIndexedDecodable: Decodable, CSVIndexedBase {}
7071

7172
// MARK: - CSVIndexedEncodable Protocol
7273

7374
/// A type that can be encoded to CSV with columns in the order of its CodingKeys.
7475
///
7576
/// The encoding order matches the order of cases in your `CodingKeys` enum.
76-
public protocol CSVIndexedEncodable: Encodable, _CSVIndexedEncodableMarker {
77-
/// The CodingKeys type, which must be CaseIterable to define column order.
78-
associatedtype CSVCodingKeys: CodingKey & CaseIterable
79-
80-
/// Returns the ordered column names derived from CodingKeys.
81-
static var csvColumnOrder: [String] { get }
82-
}
83-
84-
extension CSVIndexedEncodable {
85-
/// Default implementation: extracts column names from CodingKeys.allCases in order.
86-
public static var csvColumnOrder: [String] {
87-
CSVCodingKeys.allCases.map { $0.stringValue }
88-
}
89-
90-
/// Internal marker implementation for runtime detection.
91-
static var _csvColumnOrder: [String] { csvColumnOrder }
92-
}
77+
public protocol CSVIndexedEncodable: Encodable, CSVIndexedBase {}
9378

9479
// MARK: - Combined Protocol
9580

0 commit comments

Comments
 (0)