Skip to content

Commit 8146e47

Browse files
authored
Merge pull request #3 from renato-iar/feature/add-retries-and-fallbacks
Feature/add retries and fallbacks
2 parents 320fa78 + e013783 commit 8146e47

File tree

10 files changed

+714
-81
lines changed

10 files changed

+714
-81
lines changed

README.md

+65
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,32 @@ struct Book {
9292

9393
This will cause `synopsis` to not be safely decoded in the initializer.
9494

95+
The `@FallbackDecoding` macro can be used to grant fallback semantics to properties.
96+
`@FallbackDecoding` must be used with `@SafeDecoding`, and means decoding will never fail properties it is applied to (even if the type is not otherwise "safe-decodable"):
97+
98+
```swift
99+
@SafeDecoding
100+
struct Book {
101+
@FallbackDecoding(false)
102+
var isFavourite: Bool
103+
}
104+
```
105+
106+
The `RetryDecoding` macro can in turn be used to provide alternative decoding of a property; an alternative decoding type and a "mapper" between types must be provided.
107+
An example could be a backend that sometimes returns integers as strings, or booleans as integers:
108+
109+
```swift
110+
@SafeDecoding
111+
struct Book {
112+
@RetryDecoding(String.self, map: { $0.lowercased() == "true" })
113+
@RetryDecoding(Int.self, map: { $0 != 0 })
114+
var isFavourite: Bool
115+
}
116+
```
117+
118+
Retries will be performed in the same order as they are declared in the property.
119+
If `@FallbackDecoding` is used alongside retries, all retries will be attempted before the value specified for fallback is used.
120+
95121
## Installation
96122

97123
### Swift Package Manager
@@ -102,3 +128,42 @@ You can use the Swift Package Manager to install your package by adding it as a
102128
dependencies: [
103129
.package(url: "https://github.com/renato-iar/SafeDecoding.git", from: "1.0.0")
104130
]
131+
```
132+
133+
Finally, `@SafeDecoding` can report errors that occur (and are recovered from) during the decoding process.
134+
This is done by passing the `reporter:` parameter to the macro:
135+
136+
```swift
137+
@SafeDecoding(reporter: ...)
138+
struct Book {
139+
....
140+
}
141+
```
142+
143+
The reporter must conform the `SafeDecodingReporter` protocol.
144+
Upon recovery of decoding errors, the reporter will be called with information about said error.
145+
Remember that a reporter is local to its type, i.e. although the same type may be used everywhere **each @SafeDecoding usage must be given its reporter expression**.
146+
147+
# Versions
148+
149+
## Version 1.3.0
150+
151+
- Add `@RetryDecoding`, allowing individual properties to have associated retries
152+
- Add `@FallbackDecoding`, allowing individual properties to provide a last-resort value
153+
154+
## Version 1.2.1
155+
156+
- Bug: uses `decodeIfPresent` for optionals when using error reporting
157+
158+
## Version 1.2.0
159+
160+
- Accounts for access modifiers
161+
162+
## Version 1.1.0
163+
164+
- Add error reporting to `@SafeDecoding` macro
165+
- Remove sample client product
166+
167+
## Version 1.0.0
168+
169+
- Add `@SafeDecoding` and `@IgnoreSafeDecoding` macros

Sources/SafeDecoding/ExampleClient/main.swift

+17-5
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ struct SubModel {
4848
let strings: Array<String>
4949
}
5050

51+
// Expand macro to see code generated for the base usage of @SafeDecoding
52+
5153
@SafeDecoding
52-
struct NonReportedModel {
54+
struct ModelStandardExample {
5355
let integer: Int
54-
@IgnoreSafeDecoding
5556
let integerArray: [Int]
5657
let genericArray: Array<String>
5758
let string: String
@@ -64,15 +65,26 @@ struct NonReportedModel {
6465
let constantInferred = 0
6566
}
6667

68+
// Expand macro to see code generated for extended usage of @SafeDecoding
69+
// using the reporter, as well as @RetryDecoding, @FallbackDecoding and @IgnoreSafeDecoding
70+
// macros as decorators
71+
6772
@SafeDecoding(reporter: SafeDecodingErrorReporter.shared)
68-
struct ReportedModel {
73+
struct ModelFullExample {
74+
@RetryDecoding(String.self, map: { Int($0, radix: 10) })
75+
@RetryDecoding(Double.self, map: { Int($0) })
6976
let integer: Int
7077
@IgnoreSafeDecoding
7178
let integerArray: [Int]
7279
let genericArray: Array<String>
80+
@FallbackDecoding(UUID().uuidString)
7381
let string: String
7482
let dictionary: [String: Int]
83+
@FallbackDecoding(0)
84+
@RetryDecoding(String.self, map: { Int($0, radix: 10) })
85+
@RetryDecoding(Double.self, map: { Int($0) })
7586
let optionalInteger: Int?
87+
@FallbackDecoding(SubModel(strings: []))
7688
let subModel: SubModel?
7789
var numberOfIntegersInArray: Int { integerArray.count }
7890
let set: Set<Int>
@@ -83,7 +95,7 @@ struct ReportedModel {
8395
let input = """
8496
{
8597
"integerArray": [1, 2, 3],
86-
"integer": 0,
98+
"integer": "101",
8799
"string": "hello",
88100
"dictionary": {
89101
"a": 1,
@@ -99,7 +111,7 @@ let input = """
99111
do {
100112
if let data = input.data(using: .utf8) {
101113
let model = try JSONDecoder().decode(
102-
ReportedModel.self,
114+
ModelFullExample.self,
103115
from: data
104116
)
105117

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import SwiftSyntax
2+
import SwiftSyntaxMacros
3+
4+
public enum FallbackDecodingMacro: PeerMacro {
5+
public static func expansion(
6+
of node: AttributeSyntax,
7+
providingPeersOf declaration: some DeclSyntaxProtocol,
8+
in context: some MacroExpansionContext
9+
) throws -> [DeclSyntax] {
10+
[]
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import SwiftSyntax
2+
import SwiftSyntaxBuilder
3+
import SwiftSyntaxMacros
4+
5+
public enum RetryDecodingMacro: PeerMacro {
6+
public static func expansion(
7+
of node: AttributeSyntax,
8+
providingPeersOf declaration: some DeclSyntaxProtocol,
9+
in context: some MacroExpansionContext
10+
) throws -> [DeclSyntax] {
11+
[]
12+
}
13+
}

0 commit comments

Comments
 (0)