Skip to content

Commit 1445e59

Browse files
Add custom encoding and decoding
1 parent 673c8ec commit 1445e59

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

Sources/LightTableDelta/Delta.swift

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public enum Delta<Element> {
2323
public typealias Element = Element
2424
public typealias Side = DeltaSide
2525

26+
enum CodingKeys: String, CodingKey {
27+
case source = "A"
28+
case target = "B"
29+
}
30+
2631
/// A source element.
2732
///
2833
/// Conceptually, this is a value that was deleted and thus no target element is available.
@@ -238,8 +243,47 @@ public enum Delta<Element> {
238243
}
239244

240245
extension Delta: Equatable where Element: Equatable {}
246+
241247
extension Delta: Hashable where Element: Hashable {}
242-
extension Delta: Encodable where Element: Encodable {}
243-
extension Delta: Decodable where Element: Decodable {}
248+
249+
extension Delta: Encodable where Element: Encodable {
250+
public func encode(to encoder: any Encoder) throws {
251+
switch self {
252+
case .deleted(let source):
253+
var container = encoder.container(keyedBy: CodingKeys.self)
254+
try container.encode(source, forKey: .source)
255+
case .added(let target):
256+
var container = encoder.container(keyedBy: CodingKeys.self)
257+
try container.encode(target, forKey: .target)
258+
case .modified(let source, let target):
259+
var container = encoder.container(keyedBy: CodingKeys.self)
260+
try container.encode(source, forKey: .source)
261+
try container.encode(target, forKey: .target)
262+
}
263+
}
264+
}
265+
266+
extension Delta: Decodable where Element: Decodable {
267+
public init(from decoder: any Decoder) throws {
268+
let container = try decoder.container(keyedBy: CodingKeys.self)
269+
let source = try container.decodeIfPresent(Element.self, forKey: .source)
270+
let target = try container.decodeIfPresent(Element.self, forKey: .target)
271+
272+
if let source, let target {
273+
self = .modified(source: source, target: target)
274+
}
275+
else if let source {
276+
self = .deleted(source: source)
277+
}
278+
else if let target {
279+
self = .added(target: target)
280+
}
281+
else {
282+
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "No source or target value."))
283+
}
284+
}
285+
}
286+
244287
extension Delta: Sendable where Element: Sendable {}
288+
245289
extension Delta: BitwiseCopyable where Element: BitwiseCopyable {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,39 @@
1+
import Testing
2+
import Foundation
3+
import LightTableDelta
14

5+
@Test func encoding() async throws {
6+
let encoder = JSONEncoder()
7+
encoder.outputFormatting = .sortedKeys
8+
9+
let jsonDataDeleted = try encoder.encode(Delta.deleted(source: 3))
10+
let jsonDeleted = String(decoding: jsonDataDeleted, as: UTF8.self)
11+
#expect(jsonDeleted == #"{"A":3}"#)
12+
13+
let jsonDataAdded = try encoder.encode(Delta.added(target: 5))
14+
let jsonAdded = String(decoding: jsonDataAdded, as: UTF8.self)
15+
#expect(jsonAdded == #"{"B":5}"#)
16+
17+
let jsonDataModified = try encoder.encode(Delta.modified(source: 3, target: 5))
18+
let jsonModified = String(decoding: jsonDataModified, as: UTF8.self)
19+
#expect(jsonModified == #"{"A":3,"B":5}"#)
20+
}
21+
22+
@Test func decoding() async throws {
23+
let decoder = JSONDecoder()
24+
25+
let jsonDataDeleted = Data(#"{"A":3}"#.utf8)
26+
let deltaDeleted = try decoder.decode(Delta<Int>.self, from: jsonDataDeleted)
27+
#expect(deltaDeleted == .deleted(source: 3))
28+
29+
let jsonDataAdded = Data( #"{"B":5}"#.utf8)
30+
let deltaAdded = try decoder.decode(Delta<Int>.self, from: jsonDataAdded)
31+
#expect(deltaAdded == .added(target: 5))
32+
33+
let jsonDataModified = Data(#"{"A":3,"B":5}"#.utf8)
34+
let deltaModified = try decoder.decode(Delta<Int>.self, from: jsonDataModified)
35+
#expect(deltaModified == .modified(source: 3, target: 5))
36+
37+
let jsonDataEmpty = Data("{}".utf8)
38+
#expect(throws: DecodingError.self, performing: { try decoder.decode(Delta<Int>.self, from: jsonDataEmpty) })
39+
}

0 commit comments

Comments
 (0)