Skip to content

Commit

Permalink
feat: added sequence coding helper
Browse files Browse the repository at this point in the history
  • Loading branch information
soumyamahunt committed Jan 9, 2024
1 parent 535f446 commit 4ea6ff6
Show file tree
Hide file tree
Showing 16 changed files with 682 additions and 28 deletions.
1 change: 1 addition & 0 deletions .github/config/spellcheck-wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ typealias
customizable
enum
enums
conformances
3 changes: 1 addition & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ let package = Package(
.testTarget(
name: "MetaCodableTests",
dependencies: [
"PluginCore", "ProtocolGen",
"MacroPlugin", "MetaCodable", "HelperCoders",
"PluginCore", "MacroPlugin", "MetaCodable", "HelperCoders",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
],
plugins: ["MetaProtocolCodable"]
Expand Down
7 changes: 4 additions & 3 deletions Plugins/MetaProtocolCodable/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ extension Config: Codable {
/// - Parameter decoder: The decoder to read data from.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.scan = try container.decodeIfPresent(
ScanMode.self, forKey: .scan
) ?? .target
self.scan =
try container.decodeIfPresent(
ScanMode.self, forKey: .scan
) ?? .target
}
}
34 changes: 21 additions & 13 deletions Plugins/MetaProtocolCodable/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,20 @@ struct MetaProtocolCodable: BuildToolPlugin {
return name == "metacodableconfig"
}
guard let file else { return .init(scan: .target) }
let path = if #available(macOS 13, *) {
URL(filePath: target.directory.appending([file]).string)
} else {
URL(fileURLWithPath: target.directory.appending([file]).string)
}
let (conf, _) = try await URLSession.shared.data(from: path)
let pathStr = target.directory.appending([file]).string
#if canImport(Darwin)
let path =
if #available(macOS 13, *) {
URL(filePath: pathStr)
} else {
URL(fileURLWithPath: pathStr)
}
#else
let path = URL(fileURLWithPath: pathStr)
#endif
let conf = try Data(contentsOf: path)
let pConf = try? PropertyListDecoder().decode(Config.self, from: conf)
let config = try pConf ?? JSONDecoder().decode(Config.self, from: conf)
let config = try pConf ?? JSONDecoder().decode(Config.self, from: conf)
return config
}

Expand Down Expand Up @@ -77,8 +83,8 @@ struct MetaProtocolCodable: BuildToolPlugin {
intermFiles.append(genFile)
return Command.buildCommand(
displayName: """
Parse source file "\(fileName)" in module "\(moduleName)"
""",
Parse source file "\(fileName)" in module "\(moduleName)"
""",
executable: tool.path,
arguments: [
"parse",
Expand All @@ -103,15 +109,17 @@ struct MetaProtocolCodable: BuildToolPlugin {
for file in intermFiles {
genArgs.append(file.string)
}
buildCommands.append(.buildCommand(
buildCommands.append(
.buildCommand(
displayName: """
Generate protocol decoding/encoding syntax for "\(moduleName)"
""",
Generate protocol decoding/encoding syntax for "\(moduleName)"
""",
executable: tool.path,
arguments: genArgs,
inputFiles: intermFiles,
outputFiles: [genPath]
))
)
)
return buildCommands
}
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ You can even create your own by conforming to `HelperCoder`.
</details>

<details>
<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>
<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>

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

Expand Down
4 changes: 4 additions & 0 deletions Sources/HelperCoders/HelperCoders.docc/HelperCoders.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ Level up ``/MetaCodable``'s generated implementations with helpers assisting com
- ``PropertyWrapperCoder``
- ``PropertyWrappable``
- ``ConditionalCoder``

### Sequence

- ``SequenceCoder``
139 changes: 139 additions & 0 deletions Sources/HelperCoders/SequenceCoder/DefaultSequenceElementCoding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import MetaCodable

/// An ``/MetaCodable/HelperCoder`` that performs default decoding/encoding.
///
/// This type doesn't provide any customization and used only to opt out of
/// decoding/encoding customizations.
@_documentation(visibility:internal)
public struct DefaultSequenceElementCoding<Coded: Codable>: HelperCoder {
/// Decodes value from the given `decoder`.
///
/// Decodes the data using type's `Decodable` implementation.
///
/// - Parameter decoder: The decoder to read data from.
/// - Returns: The decoded value.
/// - Throws: If decoding fails due to corrupted or invalid data.
@inlinable
public func decode(from decoder: Decoder) throws -> Coded {
return try Coded(from: decoder)
}

/// Decodes optional value from the given `decoder`.
///
/// Decodes the data using optional type's `Decodable` implementation.
///
/// - Parameter decoder: The decoder to read data from.
/// - Returns: The optional decoded value.
/// - Throws: If decoding fails due to corrupted or invalid data.
@inlinable
public func decodeIfPresent(from decoder: Decoder) throws -> Coded? {
return try Coded?(from: decoder)
}

/// Decodes value of the from the given `container` and specified `key`.
///
/// Uses container's default `decode(_:forKey:)` implementation
/// to get value from the `container` at the specified `key`.
///
/// - Parameters:
/// - container: The container to read data from.
/// - key: The key for the value decoded.
///
/// - Returns: The decoded value.
/// - Throws: If decoding fails due to corrupted or invalid data.
@inlinable
public func decode<DecodingContainer: KeyedDecodingContainerProtocol>(
from container: DecodingContainer,
forKey key: DecodingContainer.Key
) throws -> Coded {
return try container.decode(Coded.self, forKey: key)
}

/// Decodes optional value of the from the given `container` and
/// specified `key`.
///
/// Uses container's default `decodeIfPresent(_:forKey:)` implementation
/// to get value from the `container` at the specified `key`.
///
/// - Parameters:
/// - container: The container to read data from.
/// - key: The key for the value decoded.
///
/// - Returns: The optional decoded value.
/// - Throws: If decoding fails due to corrupted or invalid data.
@inlinable
public func decodeIfPresent<DecodingContainer>(
from container: DecodingContainer, forKey key: DecodingContainer.Key
) throws -> Coded? where DecodingContainer: KeyedDecodingContainerProtocol {
return try container.decodeIfPresent(Coded.self, forKey: key)
}

/// Encodes given value to the provided `encoder`.
///
/// Decodes the value using type's `Encodable` implementation.
///
/// - Parameters:
/// - value: The value to encode.
/// - encoder: The encoder to write data to.
///
/// - Throws: If any values are invalid for the given encoder’s format.
@inlinable
public func encode(_ value: Coded, to encoder: Encoder) throws {
try value.encode(to: encoder)
}

/// Encodes given optional value to the provided `encoder`.
///
/// Decodes the optional value using optional type's `Encodable`
/// implementation.
///
/// - Parameters:
/// - value: The value to encode.
/// - encoder: The encoder to write data to.
///
/// - Throws: If any values are invalid for the given encoder’s format.
@inlinable
public func encodeIfPresent(_ value: Coded?, to encoder: Encoder) throws {
try value.encode(to: encoder)
}

/// Encodes given value of to the provided `container` at the specified
/// `key`.
///
/// Uses container's default `encode(_:forKey:)` implementation
/// to write value to the `container` at the specified `key`.
///
/// - Parameters:
/// - value: The value to encode.
/// - container: The container to write data to.
/// - key: The key to write data at.
///
/// - Throws: If any values are invalid for the given encoder’s format.
@inlinable
public func encode<EncodingContainer: KeyedEncodingContainerProtocol>(
_ value: Coded, to container: inout EncodingContainer,
atKey key: EncodingContainer.Key
) throws {
try container.encode(value, forKey: key)
}

/// Encodes given optional value of to the provided `container`
/// at the specified `key`.
///
/// Uses container's default `encodeIfPresent(_:forKey:)` implementation
/// to write optional value to the `container` at the specified `key`.
///
/// - Parameters:
/// - value: The value to encode.
/// - container: The container to write data to.
/// - key: The key to write data at.
///
/// - Throws: If any values are invalid for the given encoder’s format.
@inlinable
public func encodeIfPresent<EncodingContainer>(
_ value: Coded?, to container: inout EncodingContainer,
atKey key: EncodingContainer.Key
) throws where EncodingContainer: KeyedEncodingContainerProtocol {
try container.encodeIfPresent(value, forKey: key)
}
}

0 comments on commit 4ea6ff6

Please sign in to comment.