Skip to content

Commit 50eb415

Browse files
Support ~Copyable elements, more standard protocol conformances
1 parent 1445e59 commit 50eb415

File tree

2 files changed

+111
-82
lines changed

2 files changed

+111
-82
lines changed

Sources/LightTableDelta/Delta.swift

Lines changed: 103 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,11 @@
1818
//
1919

2020
/// A type representing a source element, a target element, or both a source and a target element.
21-
public enum Delta<Element> {
21+
public enum Delta<Element>: ~Copyable where Element: ~Copyable {
2222
/// The type of the elements.
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-
3126
/// A source element.
3227
///
3328
/// Conceptually, this is a value that was deleted and thus no target element is available.
@@ -41,25 +36,21 @@ public enum Delta<Element> {
4136
/// Conceptually, this is a value that was modified and both the source and the target element are available.
4237
/// The source and target elements can be different or equal.
4338
case modified(source: Element, target: Element)
44-
45-
/// Returns a modified delta where both the source and target share the same element.
46-
@inlinable @inline(__always)
47-
public static func equal(_ element: Element) -> Self {
48-
.modified(source: element, target: element)
49-
}
50-
39+
}
40+
41+
public extension Delta where Element: ~Copyable {
5142
/// Creates a modified delta from a source and a target element.
5243
@inlinable @inline(__always)
53-
public init(source: Element, target: Element) {
54-
self = .modified(source: source, target: target)
44+
init(source: consuming Element, target: consuming Element) {
45+
self = .transition(source: source, target: target)
5546
}
5647

5748
/// Creates a delta from a source and a target element.
5849
///
5950
/// If the source element is `nil`, the delta is `.added(target:)`.
6051
/// Otherwise, the delta is `.modified(source:target:)`.
6152
@inlinable
62-
public init(source: Element?, target: Element) {
53+
init(source: consuming Element?, target: consuming Element) {
6354
if let source {
6455
self = .modified(source: source, target: target)
6556
}
@@ -73,7 +64,7 @@ public enum Delta<Element> {
7364
/// If the target element is `nil`, the delta is `.deleted(source:)`.
7465
/// Otherwise, the delta is `.modified(source:target:)`.
7566
@inlinable
76-
public init(source: Element, target: Element?) {
67+
init(source: consuming Element, target: consuming Element?) {
7768
if let target {
7869
self = .modified(source: source, target: target)
7970
}
@@ -89,9 +80,10 @@ public enum Delta<Element> {
8980
/// If the target element is `nil`, the delta is `.deleted(source:)`.
9081
/// Otherwise, the delta is `.modified(source:target:)`.
9182
@inlinable
92-
public init?(source: Element?, target: Element?) {
93-
if let source, let target {
94-
self = .modified(source: source, target: target)
83+
init?(source: consuming Element?, target: consuming Element?) {
84+
if source != nil && target != nil {
85+
// `if let source, let target` does not work with non-copyable types here
86+
self = .modified(source: source!, target: target!)
9587
}
9688
else if let source {
9789
self = .deleted(source: source)
@@ -103,33 +95,15 @@ public enum Delta<Element> {
10395
return nil
10496
}
10597
}
106-
107-
/// The source element, if the delta value is not of type `.added`.
108-
@inlinable @inline(__always)
109-
public var source: Element? {
110-
switch self {
111-
case .deleted(let source): source
112-
case .added(_): nil
113-
case .modified(let source, _): source
114-
}
115-
}
116-
117-
/// The target element, if the delta value is not of type `.deleted`.
118-
@inlinable @inline(__always)
119-
public var target: Element? {
120-
switch self {
121-
case .deleted(_): nil
122-
case .added(let target): target
123-
case .modified(_, let target): target
124-
}
125-
}
126-
98+
}
99+
100+
public extension Delta where Element: ~Copyable {
127101
/// Returns a delta containing the results of mapping the given closure over the delta’s elements.
128102
@inlinable
129-
public func map<T, E>(
130-
_ transform: (Element) throws(E) -> T
103+
consuming func map<T: ~Copyable, E>(
104+
_ transform: (consuming Element) throws(E) -> T
131105
) throws(E) -> Delta<T> {
132-
switch self {
106+
switch consume self {
133107
case .deleted(let source):
134108
.deleted(source: try transform(source))
135109
case .added(let target):
@@ -141,10 +115,10 @@ public enum Delta<Element> {
141115

142116
/// Returns a delta containing the results of mapping the given closure over the delta’s elements, or `nil`, if the closure returns `nil` for any element.
143117
@inlinable
144-
public func flatMap<T, E>(
145-
_ transform: (Element) throws(E) -> T?
118+
consuming func compactMap<T: ~Copyable, E>(
119+
_ transform: (consuming Element) throws(E) -> T?
146120
) throws(E) -> Delta<T>? {
147-
switch self {
121+
switch consume self {
148122
case .deleted(let source):
149123
guard let source = try transform(source) else {
150124
return nil
@@ -157,20 +131,85 @@ public enum Delta<Element> {
157131
return .added(target: target)
158132
case .modified(let source, let target):
159133
guard let source = try transform(source),
160-
let target = try transform(target) else {
134+
let target = try transform(target) else {
161135
return nil
162136
}
163137
return .modified(source: source, target: target)
164138
}
165139
}
166140

141+
/// Returns a reduced view of the delta, favoring the given side.
142+
///
143+
/// If an element is available on the favored side, it is returned.
144+
/// Otherwise, the element on the other side is returned.
145+
@inlinable
146+
consuming func unified(favoring side: Side) -> Element {
147+
switch side {
148+
case .source:
149+
switch consume self {
150+
case .deleted(let source): source
151+
case .added(let target): target
152+
case .modified(let source, _): source
153+
}
154+
case .target:
155+
switch consume self {
156+
case .deleted(let source): source
157+
case .added(let target): target
158+
case .modified(_, let target): target
159+
}
160+
}
161+
}
162+
163+
/// Returns the combined value of the source and target element, if modified, otherwise returns the source or target value.
164+
@inlinable
165+
consuming func reduce<E>(
166+
combine: (consuming Element, consuming Element) throws(E) -> Element
167+
) throws(E) -> Element {
168+
switch consume self {
169+
case .deleted(let source):
170+
source
171+
case .added(let target):
172+
target
173+
case .modified(let source, let target):
174+
try combine(source, target)
175+
}
176+
}
177+
}
178+
179+
extension Delta: Copyable where Element: Copyable {
180+
/// Returns a modified delta where both the source and target share the same element.
181+
@inlinable @inline(__always)
182+
public static func equal(_ element: Element) -> Self {
183+
.modified(source: element, target: element)
184+
}
185+
186+
/// The source element, if the delta value is not of type `.added`.
187+
@inlinable @inline(__always)
188+
public var source: Element? {
189+
switch self {
190+
case .deleted(let source): source
191+
case .added(_): nil
192+
case .modified(let source, _): source
193+
}
194+
}
195+
196+
/// The target element, if the delta value is not of type `.deleted`.
197+
@inlinable @inline(__always)
198+
public var target: Element? {
199+
switch self {
200+
case .deleted(_): nil
201+
case .added(let target): target
202+
case .modified(_, let target): target
203+
}
204+
}
205+
167206
/// Returns a delta containing the results of mapping the given closure over the delta’s elements.
168207
///
169208
/// In the `.modified` case, `transform` is applied concurrently to both sides.
170209
@available(macOS 10.15, iOS 13, tvOS 13, visionOS 1, watchOS 6, *)
171210
@inlinable
172211
public func asyncMap<T>(
173-
_ transform: @Sendable (Element) async -> T
212+
_ transform: @Sendable (consuming Element) async -> T
174213
) async -> Delta<T> where Element: Sendable {
175214
switch self {
176215
case .deleted(let source):
@@ -190,7 +229,7 @@ public enum Delta<Element> {
190229
@available(macOS 10.15, iOS 13, tvOS 13, visionOS 1, watchOS 6, *)
191230
@inlinable
192231
public func asyncMap<T>(
193-
_ transform: @Sendable (Element) async throws -> T
232+
_ transform: @Sendable (consuming Element) async throws -> T
194233
) async throws -> Delta<T> where Element: Sendable {
195234
switch self {
196235
case .deleted(let source):
@@ -203,41 +242,17 @@ public enum Delta<Element> {
203242
return try await .modified(source: transformedSource, target: transformedTarget)
204243
}
205244
}
206-
207-
/// Returns a reduced view of the delta, favoring the given side.
208-
///
209-
/// If an element is available on the favored side, it is returned.
210-
/// Otherwise, the element on the other side is returned.
211-
@inlinable
212-
public func unified(favoring side: Side) -> Element {
213-
switch side {
214-
case .source:
215-
switch self {
216-
case .deleted(let source): source
217-
case .added(let target): target
218-
case .modified(let source, _): source
219-
}
220-
case .target:
221-
switch self {
222-
case .deleted(let source): source
223-
case .added(let target): target
224-
case .modified(_, let target): target
225-
}
226-
}
227-
}
228-
229-
/// Returns the combined value of the source and target element, if modified, otherwise returns the source or target value.
230-
@inlinable
231-
public func reduce<E>(
232-
combine: (Element, Element) throws(E) -> Element
233-
) throws(E) -> Element {
245+
}
246+
247+
extension Delta: CustomDebugStringConvertible {
248+
public var debugDescription: String {
234249
switch self {
235250
case .deleted(let source):
236-
source
251+
"Delta.deleted(\(source))"
237252
case .added(let target):
238-
target
253+
"Delta.added(\(target))"
239254
case .modified(let source, let target):
240-
try combine(source, target)
255+
"Delta.modified(\(source), \(target))"
241256
}
242257
}
243258
}
@@ -246,6 +261,13 @@ extension Delta: Equatable where Element: Equatable {}
246261

247262
extension Delta: Hashable where Element: Hashable {}
248263

264+
public extension Delta where Element: ~Copyable {
265+
enum CodingKeys: String, CodingKey {
266+
case source = "A"
267+
case target = "B"
268+
}
269+
}
270+
249271
extension Delta: Encodable where Element: Encodable {
250272
public func encode(to encoder: any Encoder) throws {
251273
switch self {

Sources/LightTableDelta/DeltaSide.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
//
1919

2020
/// A description of the two sides of a delta value.
21-
public enum DeltaSide: Hashable, Sendable {
21+
public enum DeltaSide: Hashable, Sendable, BitwiseCopyable, CustomStringConvertible {
2222
case source
2323
case target
2424

@@ -31,4 +31,11 @@ public enum DeltaSide: Hashable, Sendable {
3131
.source
3232
}
3333
}
34+
35+
public var description: String {
36+
switch self {
37+
case .source: "source"
38+
case .target: "target"
39+
}
40+
}
3441
}

0 commit comments

Comments
 (0)