Skip to content

Commit

Permalink
refactor!: migrated to extension macro (#21)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: use `MemberInit` macro for memberwise initializer(s)
  • Loading branch information
soumyamahunt committed Sep 20, 2023
1 parent 72389d9 commit 74e6a67
Show file tree
Hide file tree
Showing 85 changed files with 1,776 additions and 958 deletions.
118 changes: 72 additions & 46 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,54 @@ concurrency:
cancel-in-progress: true

jobs:
analyze:
name: Analyze
if: github.event_name != 'workflow_dispatch'
uses: SwiftyLab/ci/.github/workflows/analyze.yml@main
permissions:
actions: read
contents: read
security-events: write
with:
matrix: >
{
"include": [
{
"os": "ubuntu-latest",
"language": "swift",
"swift": "5.9"
},
{
"os": "macos-13",
"language": "swift",
"xcode": "15-beta"
}
]
}
build:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
swift: latest
- os: macos-13
xcode: 15-beta

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Setup repository
uses: SwiftyLab/ci/actions/setup@main
with:
swift: ${{ matrix.swift }}
xcode: ${{ matrix.xcode }}

- name: Build package products
run: swift build --build-tests

# analyze:
# name: Analyze
# if: github.event_name != 'workflow_dispatch'
# uses: SwiftyLab/ci/.github/workflows/analyze.yml@main
# permissions:
# actions: read
# contents: read
# security-events: write
# with:
# matrix: >
# {
# "include": [
# {
# "os": "ubuntu-latest",
# "language": "swift",
# "swift": "latest"
# },
# {
# "os": "macos-13",
# "language": "swift",
# "xcode": "15-beta"
# }
# ]
# }

# spell-check:
# name: Run spell check
Expand All @@ -60,24 +84,24 @@ jobs:
# with:
# config_path: .github/config/spellcheck.yml

swift-package-test:
name: Swift Package
uses: SwiftyLab/ci/.github/workflows/swift-package.yml@main
secrets: inherit
with:
matrix: >
{
"include": [
{
"os": "ubuntu-latest",
"swift": "5.9"
},
{
"os": ""macos-13",
"xcode": "15-beta"
}
]
}
# swift-package-test:
# name: Swift Package
# uses: SwiftyLab/ci/.github/workflows/swift-package.yml@main
# secrets: inherit
# with:
# matrix: >
# {
# "include": [
# {
# "os": "ubuntu-latest",
# "swift": "5.9"
# },
# {
# "os": ""macos-13",
# "xcode": "15-beta"
# }
# ]
# }
# {
# "os": "windows-latest",
# "swift": "5.9"
Expand All @@ -86,7 +110,8 @@ jobs:
ci:
name: CI
if: github.event_name == 'push'
needs: [analyze, swift-package-test]
needs: build
# needs: [analyze, swift-package-test]
uses: SwiftyLab/ci/.github/workflows/ci.yml@main

cd:
Expand All @@ -96,10 +121,11 @@ jobs:
(always() &&
github.event_name == 'workflow_dispatch' &&
github.event.inputs.release == 'true' &&
needs.swift-package-test.result == 'success' &&
(needs.ci.result == 'success' || needs.ci.result == 'skipped') &&
(needs.analyze.result == 'success' || needs.analyze.result == 'skipped'))
needs: [analyze, swift-package-test, ci]
(needs.build.result == 'success' || needs.build.result == 'skipped'))
# needs.swift-package-test.result == 'success' &&
needs: [build, ci]
# needs: [analyze, swift-package-test, ci]
uses: SwiftyLab/ci/.github/workflows/cd.yml@main
with:
version: ${{ github.event.inputs.version }}
Expand Down
18 changes: 18 additions & 0 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"version": 1,
"lineLength": 80,
"indentation": {
"spaces": 4
},
"maximumBlankLines": 1,
"respectsExistingLineBreaks": true,
"indentConditionalCompilationBlocks": false,
"rules": {
"AllPublicDeclarationsHaveDocumentation": true,
"NeverForceUnwrap": true,
"NeverUseForceTry": true,
"NoAccessLevelOnExtensionDeclaration": false,
"OrderedImports": true,
"ValidateDocumentationComments": true
}
}
35 changes: 19 additions & 16 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,6 @@
import PackageDescription
import CompilerPluginSupport

let macroDeps: [Target.Dependency] = [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "OrderedCollections", package: "swift-collections"),
]

let testDeps: [Target.Dependency] = [
"CodableMacroPlugin", "MetaCodable",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]

let package = Package(
name: "MetaCodable",
platforms: [
Expand All @@ -32,11 +18,28 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"),
.package(url: "https://github.com/apple/swift-format", from: "509.0.0"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
],
targets: [
.macro(name: "CodableMacroPlugin", dependencies: macroDeps),
.macro(
name: "CodableMacroPlugin",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.product(name: "OrderedCollections", package: "swift-collections"),
]
),
.target(name: "MetaCodable", dependencies: ["CodableMacroPlugin"]),
.testTarget(name: "MetaCodableTests", dependencies: testDeps),
.testTarget(
name: "MetaCodableTests",
dependencies: [
"CodableMacroPlugin", "MetaCodable",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
]
),
]
)
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Supercharge `Swift`'s `Codable` implementations with macros.
- Allows to create flattened model for nested `CodingKey` values with ``CodedAt(_:)`` and ``CodedIn(_:)``.
- Allows to create composition of multiple `Codable` types with ``CodedAt(_:)`` passing no arguments.
- Allows to provide default value in case of decoding failures with ``Default(_:)``.
- Generates member-wise initializer(s) considering the above default value syntax as well.
- Allows to create custom decoding/encoding strategies with ``HelperCoder`` and using them with ``CodedBy(_:)``. i.e. ``LossySequenceCoder`` etc.
- Allows to ignore specific properties from decoding/encoding with ``IgnoreCoding()``, ``IgnoreDecoding()`` and ``@IgnoreEncoding()``.
- Allows to use camel-case names for variables according to [Swift API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/#general-conventions), while enabling a type to work with different case style keys with ``CodingKeys(_:)``.
Expand Down Expand Up @@ -185,9 +184,9 @@ struct Coordinate {
</details>

<details>
<summary>Provide default value in case of decoding failures and member-wise initializer(s) generated considers these default values.</summary>
<summary>Provide default value in case of decoding failures.</summary>

Instead of throwing error in case of missing data or type mismatch, you can provide a default value that will be assigned in this case. The memberwise initializer generated also uses this default value for the field. The following definition with `MetaCodable`:
Instead of throwing error in case of missing data or type mismatch, you can provide a default value that will be assigned in this case. The following definition with `MetaCodable`:

```swift
@Codable
Expand All @@ -197,7 +196,20 @@ struct CodableData {
}
```

will not throw any error when empty JSON(`{}`) or JSON with type mismatch(`{ "field": 5 }`) is provided. The default value will be assigned in such case. Also, the memberwise initializer generated will look like this:
will not throw any error when empty JSON(`{}`) or JSON with type mismatch(`{ "field": 5 }`) is provided. The default value will be assigned in such case.

Also, memberwise initializer can be generated that uses this default value for the field.

```swift
@Codable
@MemberInit
struct CodableData {
@Default("some")
let field: String
}
```

The memberwise initializer generated will look like this:

```swift
init(field: String = "some") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ extension SyntaxProtocol {
let declaration = declaration as? AttributableDeclSyntax
else { return [] }

return declaration.attributes?.compactMap { attribute in
return declaration.attributes.compactMap { attribute in
guard case .attribute(let attribute) = attribute else { return nil }
return type.init(from: attribute)
} ?? []
}
}
}

Expand All @@ -45,12 +45,12 @@ extension SyntaxProtocol {
/// is for this attribute and perform validation of this attribute usage.
protocol AttributableDeclSyntax: DeclSyntaxProtocol {
/// The list of attributes attached to this declaration.
var attributes: AttributeListSyntax? { get }
var attributes: AttributeListSyntax { get }
}

extension AccessorDeclSyntax: AttributableDeclSyntax {}
extension ActorDeclSyntax: AttributableDeclSyntax {}
extension AssociatedtypeDeclSyntax: AttributableDeclSyntax {}
extension AssociatedTypeDeclSyntax: AttributableDeclSyntax {}
extension ClassDeclSyntax: AttributableDeclSyntax {}
extension DeinitializerDeclSyntax: AttributableDeclSyntax {}
extension EditorPlaceholderDeclSyntax: AttributableDeclSyntax {}
Expand All @@ -63,10 +63,9 @@ extension InitializerDeclSyntax: AttributableDeclSyntax {}
extension MacroDeclSyntax: AttributableDeclSyntax {}
extension MacroExpansionDeclSyntax: AttributableDeclSyntax {}
extension MissingDeclSyntax: AttributableDeclSyntax {}
extension OperatorDeclSyntax: AttributableDeclSyntax {}
extension PrecedenceGroupDeclSyntax: AttributableDeclSyntax {}
extension ProtocolDeclSyntax: AttributableDeclSyntax {}
extension StructDeclSyntax: AttributableDeclSyntax {}
extension SubscriptDeclSyntax: AttributableDeclSyntax {}
extension TypealiasDeclSyntax: AttributableDeclSyntax {}
extension TypeAliasDeclSyntax: AttributableDeclSyntax {}
extension VariableDeclSyntax: AttributableDeclSyntax {}
5 changes: 2 additions & 3 deletions Sources/CodableMacroPlugin/Attributes/Attribute.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SwiftSyntax
import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxMacros

/// A type indicating a macro-attribute.
Expand Down Expand Up @@ -62,8 +62,7 @@ extension Attribute {
/// provided declaration.
///
/// - Parameter declaration: The declaration this macro attribute
/// is attached to.
///
/// is attached to.
/// - Returns: Whether this attribute is applied more than once.
func isDuplicated(in declaration: some SyntaxProtocol) -> Bool {
return declaration.attributes(for: Self.self).count > 1
Expand Down

0 comments on commit 74e6a67

Please sign in to comment.