Skip to content

Commit 4ea6ff6

Browse files
committed
feat: added sequence coding helper
1 parent 535f446 commit 4ea6ff6

File tree

16 files changed

+682
-28
lines changed

16 files changed

+682
-28
lines changed

.github/config/spellcheck-wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ typealias
5353
customizable
5454
enum
5555
enums
56+
conformances

Package.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ let package = Package(
7070
.testTarget(
7171
name: "MetaCodableTests",
7272
dependencies: [
73-
"PluginCore", "ProtocolGen",
74-
"MacroPlugin", "MetaCodable", "HelperCoders",
73+
"PluginCore", "MacroPlugin", "MetaCodable", "HelperCoders",
7574
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
7675
],
7776
plugins: ["MetaProtocolCodable"]

Plugins/MetaProtocolCodable/Config.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ extension Config: Codable {
3838
/// - Parameter decoder: The decoder to read data from.
3939
init(from decoder: Decoder) throws {
4040
let container = try decoder.container(keyedBy: CodingKeys.self)
41-
self.scan = try container.decodeIfPresent(
42-
ScanMode.self, forKey: .scan
43-
) ?? .target
41+
self.scan =
42+
try container.decodeIfPresent(
43+
ScanMode.self, forKey: .scan
44+
) ?? .target
4445
}
4546
}

Plugins/MetaProtocolCodable/Plugin.swift

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,20 @@ struct MetaProtocolCodable: BuildToolPlugin {
3030
return name == "metacodableconfig"
3131
}
3232
guard let file else { return .init(scan: .target) }
33-
let path = if #available(macOS 13, *) {
34-
URL(filePath: target.directory.appending([file]).string)
35-
} else {
36-
URL(fileURLWithPath: target.directory.appending([file]).string)
37-
}
38-
let (conf, _) = try await URLSession.shared.data(from: path)
33+
let pathStr = target.directory.appending([file]).string
34+
#if canImport(Darwin)
35+
let path =
36+
if #available(macOS 13, *) {
37+
URL(filePath: pathStr)
38+
} else {
39+
URL(fileURLWithPath: pathStr)
40+
}
41+
#else
42+
let path = URL(fileURLWithPath: pathStr)
43+
#endif
44+
let conf = try Data(contentsOf: path)
3945
let pConf = try? PropertyListDecoder().decode(Config.self, from: conf)
40-
let config = try pConf ?? JSONDecoder().decode(Config.self, from: conf)
46+
let config = try pConf ?? JSONDecoder().decode(Config.self, from: conf)
4147
return config
4248
}
4349

@@ -77,8 +83,8 @@ struct MetaProtocolCodable: BuildToolPlugin {
7783
intermFiles.append(genFile)
7884
return Command.buildCommand(
7985
displayName: """
80-
Parse source file "\(fileName)" in module "\(moduleName)"
81-
""",
86+
Parse source file "\(fileName)" in module "\(moduleName)"
87+
""",
8288
executable: tool.path,
8389
arguments: [
8490
"parse",
@@ -103,15 +109,17 @@ struct MetaProtocolCodable: BuildToolPlugin {
103109
for file in intermFiles {
104110
genArgs.append(file.string)
105111
}
106-
buildCommands.append(.buildCommand(
112+
buildCommands.append(
113+
.buildCommand(
107114
displayName: """
108-
Generate protocol decoding/encoding syntax for "\(moduleName)"
109-
""",
115+
Generate protocol decoding/encoding syntax for "\(moduleName)"
116+
""",
110117
executable: tool.path,
111118
arguments: genArgs,
112119
inputFiles: intermFiles,
113120
outputFiles: [genPath]
114-
))
121+
)
122+
)
115123
return buildCommands
116124
}
117125
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ You can even create your own by conforming to `HelperCoder`.
242242
</details>
243243

244244
<details>
245-
<summary>Represent data with variations in the form of external/internal/adjacent tagging, with single enum with each case as a variation or a protocol type that varies with conformances accross modules.</summary>
245+
<summary>Represent data with variations in the form of external/internal/adjacent tagging, with single enum with each case as a variation or a protocol type that varies with conformances across modules.</summary>
246246

247247
i.e. while `Swift` compiler only generates implementation assuming external tagged enums, only following data:
248248

Sources/HelperCoders/HelperCoders.docc/HelperCoders.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ Level up ``/MetaCodable``'s generated implementations with helpers assisting com
6262
- ``PropertyWrapperCoder``
6363
- ``PropertyWrappable``
6464
- ``ConditionalCoder``
65+
66+
### Sequence
67+
68+
- ``SequenceCoder``
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import MetaCodable
2+
3+
/// An ``/MetaCodable/HelperCoder`` that performs default decoding/encoding.
4+
///
5+
/// This type doesn't provide any customization and used only to opt out of
6+
/// decoding/encoding customizations.
7+
@_documentation(visibility:internal)
8+
public struct DefaultSequenceElementCoding<Coded: Codable>: HelperCoder {
9+
/// Decodes value from the given `decoder`.
10+
///
11+
/// Decodes the data using type's `Decodable` implementation.
12+
///
13+
/// - Parameter decoder: The decoder to read data from.
14+
/// - Returns: The decoded value.
15+
/// - Throws: If decoding fails due to corrupted or invalid data.
16+
@inlinable
17+
public func decode(from decoder: Decoder) throws -> Coded {
18+
return try Coded(from: decoder)
19+
}
20+
21+
/// Decodes optional value from the given `decoder`.
22+
///
23+
/// Decodes the data using optional type's `Decodable` implementation.
24+
///
25+
/// - Parameter decoder: The decoder to read data from.
26+
/// - Returns: The optional decoded value.
27+
/// - Throws: If decoding fails due to corrupted or invalid data.
28+
@inlinable
29+
public func decodeIfPresent(from decoder: Decoder) throws -> Coded? {
30+
return try Coded?(from: decoder)
31+
}
32+
33+
/// Decodes value of the from the given `container` and specified `key`.
34+
///
35+
/// Uses container's default `decode(_:forKey:)` implementation
36+
/// to get value from the `container` at the specified `key`.
37+
///
38+
/// - Parameters:
39+
/// - container: The container to read data from.
40+
/// - key: The key for the value decoded.
41+
///
42+
/// - Returns: The decoded value.
43+
/// - Throws: If decoding fails due to corrupted or invalid data.
44+
@inlinable
45+
public func decode<DecodingContainer: KeyedDecodingContainerProtocol>(
46+
from container: DecodingContainer,
47+
forKey key: DecodingContainer.Key
48+
) throws -> Coded {
49+
return try container.decode(Coded.self, forKey: key)
50+
}
51+
52+
/// Decodes optional value of the from the given `container` and
53+
/// specified `key`.
54+
///
55+
/// Uses container's default `decodeIfPresent(_:forKey:)` implementation
56+
/// to get value from the `container` at the specified `key`.
57+
///
58+
/// - Parameters:
59+
/// - container: The container to read data from.
60+
/// - key: The key for the value decoded.
61+
///
62+
/// - Returns: The optional decoded value.
63+
/// - Throws: If decoding fails due to corrupted or invalid data.
64+
@inlinable
65+
public func decodeIfPresent<DecodingContainer>(
66+
from container: DecodingContainer, forKey key: DecodingContainer.Key
67+
) throws -> Coded? where DecodingContainer: KeyedDecodingContainerProtocol {
68+
return try container.decodeIfPresent(Coded.self, forKey: key)
69+
}
70+
71+
/// Encodes given value to the provided `encoder`.
72+
///
73+
/// Decodes the value using type's `Encodable` implementation.
74+
///
75+
/// - Parameters:
76+
/// - value: The value to encode.
77+
/// - encoder: The encoder to write data to.
78+
///
79+
/// - Throws: If any values are invalid for the given encoder’s format.
80+
@inlinable
81+
public func encode(_ value: Coded, to encoder: Encoder) throws {
82+
try value.encode(to: encoder)
83+
}
84+
85+
/// Encodes given optional value to the provided `encoder`.
86+
///
87+
/// Decodes the optional value using optional type's `Encodable`
88+
/// implementation.
89+
///
90+
/// - Parameters:
91+
/// - value: The value to encode.
92+
/// - encoder: The encoder to write data to.
93+
///
94+
/// - Throws: If any values are invalid for the given encoder’s format.
95+
@inlinable
96+
public func encodeIfPresent(_ value: Coded?, to encoder: Encoder) throws {
97+
try value.encode(to: encoder)
98+
}
99+
100+
/// Encodes given value of to the provided `container` at the specified
101+
/// `key`.
102+
///
103+
/// Uses container's default `encode(_:forKey:)` implementation
104+
/// to write value to the `container` at the specified `key`.
105+
///
106+
/// - Parameters:
107+
/// - value: The value to encode.
108+
/// - container: The container to write data to.
109+
/// - key: The key to write data at.
110+
///
111+
/// - Throws: If any values are invalid for the given encoder’s format.
112+
@inlinable
113+
public func encode<EncodingContainer: KeyedEncodingContainerProtocol>(
114+
_ value: Coded, to container: inout EncodingContainer,
115+
atKey key: EncodingContainer.Key
116+
) throws {
117+
try container.encode(value, forKey: key)
118+
}
119+
120+
/// Encodes given optional value of to the provided `container`
121+
/// at the specified `key`.
122+
///
123+
/// Uses container's default `encodeIfPresent(_:forKey:)` implementation
124+
/// to write optional value to the `container` at the specified `key`.
125+
///
126+
/// - Parameters:
127+
/// - value: The value to encode.
128+
/// - container: The container to write data to.
129+
/// - key: The key to write data at.
130+
///
131+
/// - Throws: If any values are invalid for the given encoder’s format.
132+
@inlinable
133+
public func encodeIfPresent<EncodingContainer>(
134+
_ value: Coded?, to container: inout EncodingContainer,
135+
atKey key: EncodingContainer.Key
136+
) throws where EncodingContainer: KeyedEncodingContainerProtocol {
137+
try container.encodeIfPresent(value, forKey: key)
138+
}
139+
}

0 commit comments

Comments
 (0)