Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode
.DS_Store
/.build
/Packages
Expand Down
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.0-DEVELOPMENT-SNAPSHOT-2024-06-22-a
9 changes: 3 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version: 6.0

import PackageDescription

Expand All @@ -10,18 +9,16 @@ let package = Package(
.iOS(.v17)
],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "FunctionSpy",
targets: ["FunctionSpy"]),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "FunctionSpy"),
.testTarget(
name: "FunctionSpyTests",
dependencies: ["FunctionSpy"]),
]
],
swiftLanguageModes: [.v6]
)
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,3 @@ public extension SpyProtocol {
```

Feel free to open a PR if you'd like to add a helper function to the library!

## Note from the developer
When I started writing this, I thought I'd be able to use parameter packs, but they're very buggy as of Swift 5.9. (example [one](https://github.com/apple/swift/issues/69317), [two](https://github.com/apple/swift/issues/69313), [three](https://github.com/apple/swift/issues/69028)) Because of that, this library only supports functions with up to 4 parameters. If you need more, open an issue or a PR, and I'll be happy to add more overloads. For now I'm assuming [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it).
57 changes: 6 additions & 51 deletions Sources/FunctionSpy/EmptyClosure.swift
Original file line number Diff line number Diff line change
@@ -1,59 +1,14 @@
/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, Result>(_ closure: (A) -> Result, result: Result) -> @Sendable (A) -> Result {
{ (_: A) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, Result>(_ closure: (A, B) -> Result, result: Result) -> @Sendable (A, B) -> Result {
{ (_: A, _: B) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, C, Result>(_ closure: (A, B, C) -> Result, result: Result) -> @Sendable (A, B, C) -> Result {
{ (_: A, _: B, _: C) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, C, D, Result>(_ closure: (A, B, C, D) -> Result, result: Result) -> @Sendable (A, B, C, D) -> Result {
{ (_: A, _: B, _: C, _: D) in result }
public func emptyClosure<each A, Result: Sendable>(_ closure: @Sendable (repeat each A) -> Result, result: Result) -> @Sendable (repeat each A) -> Result {
{ (_: repeat each A) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing.
public func emptyClosure<A>(_ closure: (A) -> Void) -> @Sendable (A) -> Void {
{ (_: A) in }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing.
public func emptyClosure<A, B>(_ closure: (A, B) -> Void) -> @Sendable (A, B) -> Void {
{ (_: A, _: B) in }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing.
public func emptyClosure<A, B, C>(_ closure: (A, B, C) -> Void) -> @Sendable (A, B, C) -> Void {
{ (_: A, _: B, _: C) in }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing.
public func emptyClosure<A, B, C, D>(_ closure: (A, B, C, D) -> Void) -> @Sendable (A, B, C, D) -> Void {
{ (_: A, _: B, _: C, _: D) in }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, Result>(_ closure: (A) async throws -> Result, result: Result) -> @Sendable (A) async throws -> Result {
{ (_: A) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, Result>(_ closure: (A, B) async throws -> Result, result: Result) -> @Sendable (A, B) async throws -> Result {
{ (_: A, _: B) in result }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, C, Result>(_ closure: (A, B, C) async throws -> Result, result: Result) async throws -> @Sendable (A, B, C) -> Result {
{ (_: A, _: B, _: C) in result }
public func emptyClosure<each A>(_ closure: @Sendable (repeat each A) -> Void) -> @Sendable (repeat each A) -> Void {
{ (_: repeat each A) in }
}

/// Produces an empty closure with the same signature as the passed function. Resulting closure does nothing but return `result`.
public func emptyClosure<A, B, C, D, Result>(_ closure: (A, B, C, D) async throws -> Result, result: Result) -> @Sendable (A, B, C, D) async throws -> Result {
{ (_: A, _: B, _: C, _: D) in result }
public func emptyClosure<each A, Result: Sendable>(_ closure: @Sendable (repeat each A) async throws -> Result, result: Result) -> @Sendable (repeat each A) async throws -> Result {
{ (_: repeat each A) in result }
}
214 changes: 32 additions & 182 deletions Sources/FunctionSpy/FunctionSpy.swift
Original file line number Diff line number Diff line change
@@ -1,210 +1,60 @@
/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<Result>(
_ closure: @escaping @Sendable () -> Result
) -> (Spy, @Sendable () -> Result) {
let spy = Spy()
return (spy, {
public func spy<each A, Result>(
_ closure: @escaping @Sendable (repeat each A) -> Result
) -> (Spy<repeat each A>, @Sendable (repeat each A) -> Result) {
let spy = Spy<repeat each A>()

return (spy, { (a: repeat each A) -> Result in
spy.increment()
spy.recordCall()
return closure()
spy.recordCall(repeat each a)
return closure(repeat each a)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, Result>(
_ closure: @escaping @Sendable (A) -> Result
) -> (Spy1<A>, @Sendable (A) -> Result) {
let spy = Spy1<A>()
return (spy, { a in
public func spy<each A, Result>(
_ closure: @escaping @Sendable (repeat each A) throws -> Result
) -> (Spy<repeat each A>, @Sendable (repeat each A) throws -> Result) {
let spy = Spy<repeat each A>()

return (spy, { (a: repeat each A) throws -> Result in
spy.increment()
spy.recordCall(a)
return closure(a)
spy.recordCall(repeat each a)
return try closure(repeat each a)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, Result>(
_ closure: @escaping @Sendable (A, B) -> Result
) -> (Spy2<A, B>, @Sendable (A, B) -> Result) {
let spy = Spy2<A, B>()
return (spy, { a, b in
public func spy<each A, Result>(
_ closure: @escaping @Sendable (repeat each A) async -> Result
) -> (Spy<repeat each A>, @Sendable (repeat each A) async -> Result) {
let spy = Spy<repeat each A>()

return (spy, { (a: repeat each A) async -> Result in
spy.increment()
spy.recordCall(a, b)
return closure(a, b)
spy.recordCall(repeat each a)
return await closure(repeat each a)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, Result>(
_ closure: @escaping @Sendable (A, B, C) -> Result
) -> (Spy3<A, B, C>, @Sendable (A, B, C) -> Result) {
let spy = Spy3<A, B, C>()
return (spy, { a, b, c in
spy.increment()
spy.recordCall(a, b, c)
return closure(a, b, c)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, D, Result>(
_ closure: @escaping @Sendable (A, B, C, D) -> Result
) -> (Spy4<A, B, C, D>, @Sendable (A, B, C, D) -> Result) {
let spy = Spy4<A, B, C, D>()
return (spy, { a, b, c, d in
spy.increment()
spy.recordCall(a, b, c, d)
return closure(a, b, c, d)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<Result>(
_ closure: @escaping @Sendable () throws -> Result
) -> (Spy, @Sendable () throws -> Result) {
let spy = Spy()
return (spy, {
spy.increment()
spy.recordCall()
return try closure()
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, Result>(
_ closure: @escaping @Sendable (A) throws -> Result
) -> (Spy1<A>, @Sendable (A) throws -> Result) {
let spy = Spy1<A>()
return (spy, { a in
public func spy<each A, Result>(
_ closure: @escaping @Sendable (repeat each A) async throws -> Result
) -> (Spy<repeat each A>, @Sendable (repeat each A) async throws -> Result) {
let spy = Spy<repeat each A>()

return (spy, { (a: repeat each A) async throws -> Result in
spy.increment()
spy.recordCall(a)
return try closure(a)
spy.recordCall(repeat each a)
return try await closure(repeat each a)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, Result>(
_ closure: @escaping @Sendable (A, B) throws -> Result
) -> (Spy2<A, B>, @Sendable (A, B) throws -> Result) {
let spy = Spy2<A, B>()
return (spy, { a, b in
spy.increment()
spy.recordCall(a, b)
return try closure(a, b)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, Result>(
_ closure: @escaping @Sendable (A, B, C) throws -> Result
) -> (Spy3<A, B, C>, @Sendable (A, B, C) throws -> Result) {
let spy = Spy3<A, B, C>()
return (spy, { a, b, c in
spy.increment()
spy.recordCall(a, b, c)
return try closure(a, b, c)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, D, Result>(
_ closure: @escaping @Sendable (A, B, C, D) throws -> Result
) -> (Spy4<A, B, C, D>, @Sendable (A, B, C, D) throws -> Result) {
let spy = Spy4<A, B, C, D>()
return (spy, { a, b, c, d in
spy.increment()
spy.recordCall(a, b, c, d)
return try closure(a, b, c, d)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<Result>(
_ closure: @escaping @Sendable () async throws -> Result
) -> (Spy, @Sendable () async throws -> Result) {
let spy = Spy()
return (spy, {
spy.increment()
spy.recordCall()
return try await closure()
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, Result>(
_ closure: @escaping @Sendable (A) async throws -> Result
) -> (Spy1<A>, @Sendable (A) async throws -> Result) {
let spy = Spy1<A>()
return (spy, { a in
spy.increment()
spy.recordCall(a)
return try await closure(a)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, Result>(
_ closure: @escaping @Sendable (A, B) async throws -> Result
) -> (Spy2<A, B>, @Sendable (A, B) async throws -> Result) {
let spy = Spy2<A, B>()
return (spy, { a, b in
spy.increment()
spy.recordCall(a, b)
return try await closure(a, b)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, Result>(
_ closure: @escaping @Sendable (A, B, C) async throws -> Result
) -> (Spy3<A, B, C>, @Sendable (A, B, C) async throws -> Result) {
let spy = Spy3<A, B, C>()
return (spy, { a, b, c in
spy.increment()
spy.recordCall(a, b, c)
return try await closure(a, b, c)
})
}

/// Wraps a function in a spy
/// - Parameter closure: The function to wrap
/// - Returns: The spy and and the wrapped function
public func spy<A, B, C, D, Result>(
_ closure: @escaping @Sendable (A, B, C, D) async throws -> Result
) -> (Spy4<A, B, C, D>, @Sendable (A, B, C, D) async throws -> Result) {
let spy = Spy4<A, B, C, D>()
return (spy, { a, b, c, d in
spy.increment()
spy.recordCall(a, b, c, d)
return try await closure(a, b, c, d)
})
}

Loading