Skip to content

Change Graph's 'mapValues' APIs to pass key path in addition to value to transform closure #372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 24, 2024
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
8 changes: 5 additions & 3 deletions Sources/Testing/Running/Configuration.TestFilter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ extension Configuration.TestFilter.Kind {
case .unfiltered:
return testGraph
case let .precomputed(selection, membership):
return testGraph.mapValues { test in
return testGraph.mapValues { _, test in
guard let test else {
return nil
}
Expand Down Expand Up @@ -250,7 +250,9 @@ extension Configuration.TestFilter.Kind {
return zip(
lhs.apply(to: testGraph),
rhs.apply(to: testGraph)
).mapValues(op.functionValue)
).mapValues { _, value in
op.functionValue(value.0, value.1)
}
}
}
}
Expand All @@ -271,7 +273,7 @@ extension Configuration.TestFilter {
// combined test filters. It is only consulted on the outermost call to
// apply(to:), not in _apply(to:).
if !includeHiddenTests {
result = result.mapValues { test in
result = result.mapValues { _, test in
(test?.isHidden == true) ? nil : test
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Testing/Running/Runner.Plan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ extension Runner.Plan {

// Now that we have allowed all the traits to update their corresponding
// actions, recursively apply those actions to child tests in the graph.
actionGraph = actionGraph.mapValues { action in
actionGraph = actionGraph.mapValues { _, action in
(action, recursivelyApply: action.isRecursive)
}

// Zip the tests and actions together and return them.
return zip(testGraph, actionGraph).mapValues { test, action in
test.map { Step(test: $0, action: action) }
return zip(testGraph, actionGraph).mapValues { _, pair in
pair.0.map { Step(test: $0, action: pair.1) }
}
}

Expand Down
142 changes: 95 additions & 47 deletions Sources/Testing/Support/Graph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ extension Graph {
///
/// - Parameters:
/// - keyPath: The key path to use for the root node when passing it to
/// `body`.
/// `body`.
/// - body: A closure that is invoked once per element in the graph. The
/// key path and leaf value of each node are passed to the closure.
///
Expand Down Expand Up @@ -485,18 +485,18 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure and its result
/// is used as the corresponding value in the new graph. If the result is
/// `nil`, the node and all of its child nodes are omitted from the new
/// graph.
/// The key path and leaf value of each node are passed to this closure
/// and its result is used as the corresponding value in the new graph. If
/// the result is `nil`, the node and all of its child nodes are omitted
/// from the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths, with `nil` values omitted.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func compactMapValues<U>(_ transform: (V) throws -> U?) rethrows -> Graph<K, U>? {
func compactMapValues<U>(_ transform: (Element) throws -> U?) rethrows -> Graph<K, U>? {
try compactMapValues {
try transform($0).map { ($0, false) }
}
Expand All @@ -507,18 +507,18 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure and its result
/// is used as the corresponding value in the new graph. If the result is
/// `nil`, the node and all of its child nodes are omitted from the new
/// graph.
/// The key path and leaf value of each node are passed to this closure
/// and its result is used as the corresponding value in the new graph. If
/// the result is `nil`, the node and all of its child nodes are omitted
/// from the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths, with `nil` values omitted.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func compactMapValues<U>(_ transform: (V) async throws -> U?) async rethrows -> Graph<K, U>? {
func compactMapValues<U>(_ transform: (Element) async throws -> U?) async rethrows -> Graph<K, U>? {
try await compactMapValues {
try await transform($0).map { ($0, false) }
}
Expand All @@ -530,31 +530,54 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure. The result of
/// the closure is a tuple containing the new value and specifying whether
/// or not the new value should also be applied to each descendant node.
/// If `true`, `transform` is not invoked for those descendant nodes. If
/// the result is `nil`, the node and all of its child nodes are omitted
/// from the new graph.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes. If the result is `nil`, the node and all of its
/// child nodes are omitted from the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths, with `nil` values omitted.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func compactMapValues<U>(_ transform: (V) throws -> (U, recursivelyApply: Bool)?) rethrows -> Graph<K, U>? {
guard let (newValue, recursivelyApply) = try transform(value) else {
func compactMapValues<U>(_ transform: (Element) throws -> (U, recursivelyApply: Bool)?) rethrows -> Graph<K, U>? {
try _compactMapValues(keyPath: []) {
try transform(($0, $1))
}
}

/// The recursive implementation of `compactMapValues(_:)`.
///
/// - Parameters:
/// - keyPath: The key path to use for the root node when passing it to
/// `transform`.
/// - transform: A closure that is invoked once per element in the graph.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes. If the result is `nil`, the node and all of its
/// child nodes are omitted from the new graph.
///
/// - Throws: Whatever is thrown by `transform`.
private func _compactMapValues<U>(keyPath: [K], _ transform: (Element) throws -> (U, recursivelyApply: Bool)?) rethrows -> Graph<K, U>? {
guard let (newValue, recursivelyApply) = try transform((keyPath, value)) else {
return nil
}

var newChildren = [K: Graph<K,U>]()
newChildren.reserveCapacity(children.count)
for (key, child) in children {
var childKeyPath = keyPath
childKeyPath.append(key)

if recursivelyApply {
newChildren[key] = child.compactMapValues { _ in (newValue, true) }
newChildren[key] = child._compactMapValues(keyPath: childKeyPath) { _ in (newValue, true) }
} else {
newChildren[key] = try child.compactMapValues(transform)
newChildren[key] = try child._compactMapValues(keyPath: childKeyPath, transform)
}
}

Expand All @@ -567,31 +590,54 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure. The result of
/// the closure is a tuple containing the new value and specifying whether
/// or not the new value should also be applied to each descendant node.
/// If `true`, `transform` is not invoked for those descendant nodes. If
/// the result is `nil`, the node and all of its child nodes are omitted
/// from the new graph.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes. If the result is `nil`, the node and all of its
/// child nodes are omitted from the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths, with `nil` values omitted.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func compactMapValues<U>(_ transform: (V) async throws -> (U, recursivelyApply: Bool)?) async rethrows -> Graph<K, U>? {
guard let (newValue, recursivelyApply) = try await transform(value) else {
func compactMapValues<U>(_ transform: (Element) async throws -> (U, recursivelyApply: Bool)?) async rethrows -> Graph<K, U>? {
try await _compactMapValues(keyPath: []) {
try await transform(($0, $1))
}
}

/// The recursive implementation of `compactMapValues(_:)`.
///
/// - Parameters:
/// - keyPath: The key path to use for the root node when passing it to
/// `transform`.
/// - transform: A closure that is invoked once per element in the graph.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes. If the result is `nil`, the node and all of its
/// child nodes are omitted from the new graph.
///
/// - Throws: Whatever is thrown by `transform`.
private func _compactMapValues<U>(keyPath: [K], _ transform: (Element) async throws -> (U, recursivelyApply: Bool)?) async rethrows -> Graph<K, U>? {
guard let (newValue, recursivelyApply) = try await transform((keyPath, value)) else {
return nil
}

var newChildren = [K: Graph<K,U>]()
newChildren.reserveCapacity(children.count)
for (key, child) in children {
var childKeyPath = keyPath
childKeyPath.append(key)

if recursivelyApply {
newChildren[key] = child.compactMapValues { _ in (newValue, true) }
newChildren[key] = child._compactMapValues(keyPath: childKeyPath) { _ in (newValue, true) }
} else {
newChildren[key] = try await child.compactMapValues(transform)
newChildren[key] = try await child._compactMapValues(keyPath: childKeyPath, transform)
}
}

Expand All @@ -603,16 +649,16 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure and its result
/// is used as the corresponding value in the new graph.
/// The key path and leaf value of each node are passed to this closure
/// and its result is used as the corresponding value in the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func mapValues<U>(_ transform: (V) throws -> U) rethrows -> Graph<K, U> {
func mapValues<U>(_ transform: (Element) throws -> U) rethrows -> Graph<K, U> {
try compactMapValues(transform)!
}

Expand All @@ -621,16 +667,16 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure and its result
/// is used as the corresponding value in the new graph.
/// The key path and leaf value of each node are passed to this closure
/// and its result is used as the corresponding value in the new graph.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func mapValues<U>(_ transform: (V) async throws -> U) async rethrows -> Graph<K, U> {
func mapValues<U>(_ transform: (Element) async throws -> U) async rethrows -> Graph<K, U> {
try await compactMapValues(transform)!
}

Expand All @@ -640,18 +686,19 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure. The result of
/// the closure is a tuple containing the new value and specifying whether
/// or not the new value should also be applied to each descendant node.
/// If `true`, `transform` is not invoked for those descendant nodes.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func mapValues<U>(_ transform: (V) throws -> (U, recursivelyApply: Bool)) rethrows -> Graph<K, U> {
func mapValues<U>(_ transform: (Element) throws -> (U, recursivelyApply: Bool)) rethrows -> Graph<K, U> {
try compactMapValues(transform)!
}

Expand All @@ -661,18 +708,19 @@ extension Graph {
///
/// - Parameters:
/// - transform: A closure that is invoked once per element in the graph.
/// The leaf value of each node is passed to this closure. The result of
/// the closure is a tuple containing the new value and specifying whether
/// or not the new value should also be applied to each descendant node.
/// If `true`, `transform` is not invoked for those descendant nodes.
/// The key path and leaf value of each node are passed to this closure.
/// The result of the closure is a tuple containing the new value and
/// specifying whether or not the new value should also be applied to each
/// descendant node. If `true`, `transform` is not invoked for those
/// descendant nodes.
///
/// - Returns: A graph containing the transformed nodes of this graph at the
/// same key paths.
///
/// - Throws: Whatever is thrown by `transform`.
///
/// This function iterates depth-first.
func mapValues<U>(_ transform: (V) async throws -> (U, recursivelyApply: Bool)) async rethrows -> Graph<K, U> {
func mapValues<U>(_ transform: (Element) async throws -> (U, recursivelyApply: Bool)) async rethrows -> Graph<K, U> {
try await compactMapValues(transform)!
}

Expand Down
Loading