Skip to content

Commit 09ed4bc

Browse files
committed
chore: Release version 1.1.0
Added Queue Processing Pattern and Merging/Upsert Pattern: - Queue Processing Pattern: Concurrent queue management with status tracking, progress monitoring, and retry support - Merging/Upsert Pattern: Configurable merge strategies for conflict resolution - Added 30 comprehensive unit tests (153 total) - Improved code coverage to 96.15% lines - Updated README.md with usage examples for new patterns Semantic versioning: MINOR bump (1.0.3 → 1.1.0) for new backwards-compatible features.
1 parent ef23d3b commit 09ed4bc

File tree

7 files changed

+1798
-1
lines changed

7 files changed

+1798
-1
lines changed

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
### Security
2626
- N/A
2727

28+
## [1.1.0] - 2025-12-04
29+
30+
### Added
31+
- **Queue Processing Pattern** - Concurrent queue management with status tracking, progress monitoring, and retry support
32+
- `QueueItem` protocol for items that can be processed in a queue
33+
- `QueueProcessor` protocol for processing queue items
34+
- `ProcessingQueue` actor for managing concurrent processing with configurable limits
35+
- Features: status tracking (pending, processing, completed, failed), progress monitoring (0.0 to 1.0), retry support, pause/resume functionality, cancellation, deduplication
36+
- Comprehensive unit tests (15 tests)
37+
- **Merging/Upsert Pattern** - Configurable merge strategies for conflict resolution
38+
- `Mergeable` protocol for types that can be merged
39+
- `MergeStrategy` enum with strategies: preferExisting, preferNew, combine, custom
40+
- `Merger` protocol for merging items with configurable strategies
41+
- `DefaultMerger` base class with default implementations
42+
- Comprehensive unit tests (32 tests)
43+
- Added comprehensive unit tests for new patterns
44+
- Queue Processing Pattern: 15 tests covering all features and edge cases
45+
- Merging/Upsert Pattern: 32 tests covering all strategies, edge cases, and protocol conformance
46+
- Total: 153 tests (was 123) ➕ +30 new tests
47+
48+
### Changed
49+
- Updated README.md with usage examples for Queue Processing Pattern and Merging/Upsert Pattern
50+
- Improved code coverage:
51+
- Overall: 96.15% lines (up from 96.06%)
52+
- Queue.swift: 89.23% regions, 91.50% lines
53+
- Merger.swift: 83.33% regions, 84.62% lines
54+
- Enhanced documentation with comprehensive examples for new patterns
55+
56+
### Fixed
57+
- N/A
58+
2859
## [1.0.3] - 2025-12-04
2960

3061
### Added

README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ All patterns and algorithms follow consistent implementation guidelines for main
3333
### Behavioral Patterns
3434
- **Strategy Pattern** - Interchangeable algorithms
3535
- **Observer Pattern** - Event notification and subscription
36+
- **Queue Processing Pattern** - Concurrent queue management with status tracking, progress monitoring, and retry support
37+
- **Merging/Upsert Pattern** - Configurable merge strategies for conflict resolution
3638
- **Command Pattern** - Encapsulating requests as objects
3739
- **State Pattern** - Object behavior based on state
3840
- **Template Method Pattern** - Defining algorithm skeleton
@@ -132,6 +134,113 @@ subject.addObserver(observer)
132134
subject.notifyObservers(event: .somethingHappened)
133135
```
134136

137+
### Queue Processing Pattern
138+
139+
```swift
140+
import DesignAlgorithmsKit
141+
142+
// Define your item type
143+
struct MyItem: QueueItem {
144+
let id: UUID
145+
var status: QueueItemStatus = .pending
146+
var progress: Double = 0.0
147+
let data: Data
148+
}
149+
150+
// Define your processor
151+
struct MyProcessor: QueueProcessor {
152+
typealias Item = MyItem
153+
154+
func process(_ item: MyItem) async throws {
155+
// Process the item
156+
// Update progress if needed
157+
}
158+
}
159+
160+
// Create and use the queue
161+
let queue = ProcessingQueue<MyItem, MyProcessor>(
162+
processor: MyProcessor(),
163+
maxConcurrent: 3
164+
)
165+
166+
// Add items
167+
let items = [MyItem(id: UUID(), data: data1), MyItem(id: UUID(), data: data2)]
168+
await queue.add(items)
169+
170+
// Monitor progress
171+
let pending = await queue.pendingItems
172+
let processing = await queue.processingItems
173+
let completed = await queue.completedItems
174+
let failed = await queue.failedItems
175+
176+
// Retry failed items
177+
if let failedItem = await queue.failedItems.first {
178+
await queue.retry(id: failedItem.id)
179+
}
180+
181+
// Pause/resume
182+
await queue.pause()
183+
await queue.resume()
184+
```
185+
186+
### Merging/Upsert Pattern
187+
188+
```swift
189+
import DesignAlgorithmsKit
190+
191+
// Define your item type
192+
struct MyItem: Mergeable {
193+
let id: UUID
194+
var name: String
195+
var metadata: [String: String]
196+
}
197+
198+
// Create a merger
199+
class MyMerger: DefaultMerger<MyItem> {
200+
var storage: [UUID: MyItem] = [:]
201+
202+
override func findExisting(by id: UUID) async -> MyItem? {
203+
return storage[id]
204+
}
205+
206+
override func upsert(_ item: MyItem, strategy: MergeStrategy) async throws -> MyItem {
207+
if let existing = await findExisting(by: item.id) {
208+
let merged = merge(existing: existing, with: item, strategy: strategy)
209+
storage[item.id] = merged
210+
return merged
211+
} else {
212+
storage[item.id] = item
213+
return item
214+
}
215+
}
216+
}
217+
218+
// Use the merger
219+
let merger = MyMerger()
220+
221+
// Upsert with prefer existing strategy
222+
let item1 = MyItem(id: UUID(), name: "Item", metadata: ["key": "value"])
223+
let upserted1 = try await merger.upsert(item1, strategy: .preferExisting)
224+
225+
// Upsert with prefer new strategy
226+
let item2 = MyItem(id: item1.id, name: "Updated", metadata: ["key": "new"])
227+
let upserted2 = try await merger.upsert(item2, strategy: .preferNew)
228+
229+
// Upsert with custom merge strategy
230+
let customStrategy: MergeStrategy = .custom { existing, new in
231+
let existingItem = existing as! MyItem
232+
let newItem = new as! MyItem
233+
var mergedMetadata = existingItem.metadata
234+
mergedMetadata.merge(newItem.metadata) { _, new in new }
235+
return MyItem(
236+
id: existingItem.id,
237+
name: newItem.name,
238+
metadata: mergedMetadata
239+
)
240+
}
241+
let upserted3 = try await merger.upsert(item2, strategy: customStrategy)
242+
```
243+
135244
### Merkle Tree
136245

137246
```swift
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//
2+
// Merger.swift
3+
// DesignAlgorithmsKit
4+
//
5+
// Merging/Upsert Pattern - Configurable merge strategies for conflict resolution
6+
//
7+
8+
import Foundation
9+
10+
/// Protocol for types that can be merged
11+
///
12+
/// Types conforming to this protocol can be merged with conflict resolution strategies.
13+
public protocol Mergeable: Sendable {
14+
/// Unique identifier for the item
15+
associatedtype ID: Hashable & Sendable
16+
/// Unique identifier for this item
17+
var id: ID { get }
18+
}
19+
20+
/// Strategy for merging items when conflicts occur
21+
public enum MergeStrategy: Sendable {
22+
/// Prefer existing values (keep existing, ignore new)
23+
case preferExisting
24+
/// Prefer new values (replace existing with new)
25+
case preferNew
26+
/// Combine values (merge dictionaries, arrays, etc.)
27+
case combine
28+
/// Custom merge function
29+
case custom(@Sendable (_ existing: any Mergeable, _ new: any Mergeable) -> any Mergeable)
30+
}
31+
32+
/// Protocol for merging items with configurable strategies
33+
///
34+
/// Implementations of this protocol define how items are found and merged.
35+
public protocol Merger: Sendable {
36+
/// Type of item being merged
37+
associatedtype Item: Mergeable
38+
/// Find an existing item by identifier
39+
/// - Parameter id: Identifier to search for
40+
/// - Returns: Existing item if found, nil otherwise
41+
func findExisting(by id: Item.ID) async -> Item?
42+
/// Merge existing item with new item using strategy
43+
/// - Parameters:
44+
/// - existing: Existing item
45+
/// - new: New item
46+
/// - strategy: Merge strategy to use
47+
/// - Returns: Merged item
48+
func merge(existing: Item, with new: Item, strategy: MergeStrategy) -> Item
49+
/// Upsert an item (update if exists, insert if not)
50+
/// - Parameters:
51+
/// - item: Item to upsert
52+
/// - strategy: Merge strategy to use if item exists
53+
/// - Returns: Upserted item (may be merged with existing)
54+
func upsert(_ item: Item, strategy: MergeStrategy) async throws -> Item
55+
}
56+
57+
/// Default merger implementation
58+
///
59+
/// Provides a base implementation of the merger protocol with default merge strategies.
60+
/// Subclasses can override methods to customize behavior.
61+
///
62+
/// ## Usage
63+
///
64+
/// ```swift
65+
/// struct MyItem: Mergeable {
66+
/// let id: UUID
67+
/// var name: String
68+
/// var metadata: [String: String]
69+
/// }
70+
///
71+
/// class MyMerger: DefaultMerger<MyItem> {
72+
/// override func findExisting(by id: UUID) async -> MyItem? {
73+
/// // Find in your storage
74+
/// return storage.find(id: id)
75+
/// }
76+
///
77+
/// override func upsert(_ item: MyItem, strategy: MergeStrategy) async throws -> MyItem {
78+
/// if let existing = await findExisting(by: item.id) {
79+
/// let merged = merge(existing: existing, with: item, strategy: strategy)
80+
/// // Update in storage
81+
/// return merged
82+
/// } else {
83+
/// // Insert in storage
84+
/// return item
85+
/// }
86+
/// }
87+
/// }
88+
/// ```
89+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
90+
open class DefaultMerger<Item: Mergeable>: @unchecked Sendable, Merger {
91+
public init() {}
92+
93+
/// Find an existing item by identifier
94+
/// - Parameter id: Identifier to search for
95+
/// - Returns: Existing item if found, nil otherwise
96+
/// - Note: Subclasses must override this method
97+
open func findExisting(by id: Item.ID) async -> Item? {
98+
fatalError("Subclasses must override findExisting(by:)")
99+
}
100+
101+
/// Merge existing item with new item using strategy
102+
/// - Parameters:
103+
/// - existing: Existing item
104+
/// - new: New item
105+
/// - strategy: Merge strategy to use
106+
/// - Returns: Merged item
107+
/// - Note: Default implementation handles basic strategies. Subclasses can override for custom behavior.
108+
open func merge(existing: Item, with new: Item, strategy: MergeStrategy) -> Item {
109+
switch strategy {
110+
case .preferExisting:
111+
return existing
112+
case .preferNew:
113+
return new
114+
case .combine:
115+
// Default combine strategy: prefer existing, but this should be overridden
116+
// for types that can actually combine (e.g., dictionaries, arrays)
117+
return existing
118+
case .custom(let mergeFunction):
119+
return mergeFunction(existing, new) as! Item
120+
}
121+
}
122+
123+
/// Upsert an item (update if exists, insert if not)
124+
/// - Parameters:
125+
/// - item: Item to upsert
126+
/// - strategy: Merge strategy to use if item exists
127+
/// - Returns: Upserted item (may be merged with existing)
128+
/// - Note: Subclasses must override this method
129+
open func upsert(_ item: Item, strategy: MergeStrategy) async throws -> Item {
130+
if let existing = await findExisting(by: item.id) {
131+
return merge(existing: existing, with: item, strategy: strategy)
132+
} else {
133+
return item
134+
}
135+
}
136+
}
137+

0 commit comments

Comments
 (0)