diff --git a/Sources/XCDiffCore/Comparator/HeadersComparator.swift b/Sources/XCDiffCore/Comparator/HeadersComparator.swift index 2ba9d38..9bfbe53 100644 --- a/Sources/XCDiffCore/Comparator/HeadersComparator.swift +++ b/Sources/XCDiffCore/Comparator/HeadersComparator.swift @@ -30,50 +30,31 @@ final class HeadersComparator: Comparator { let firstHeaders = try targetsHelper.headers(from: firstTarget, sourceRoot: first.sourceRoot) let secondHeaders = try targetsHelper.headers(from: secondTarget, sourceRoot: second.sourceRoot) - let firstPaths = Set(firstHeaders.map { $0.path }) - let secondPaths = Set(secondHeaders.map { $0.path }) - - let commonHeaders = commonHeaderDescriptorPairs(first: firstHeaders, second: secondHeaders) - let differentValues = attributesDifferences(in: commonHeaders) - - return result(context: ["\"\(secondTarget.name)\" target"], - first: firstPaths, - second: secondPaths, - differentValues: differentValues) + return result( + context: ["\"\(secondTarget.name)\" target"], + first: firstHeaders, + second: secondHeaders, + diffCommonValues: { commonPairs in + attributesDifferences(in: commonPairs) + } + ) } } // MARK: - Private - /// Returns common header descriptors as a header descriptor pair - private func commonHeaderDescriptorPairs(first: [HeaderDescriptor], - second: [HeaderDescriptor]) -> [HeaderDescriptorPair] { - let firstHeaderDescriptorMap = headerPathMap(from: first) - let secondHeaderDescriptorMap = headerPathMap(from: second) - - let firstPaths = Set(firstHeaderDescriptorMap.keys) - let secondPaths = Set(secondHeaderDescriptorMap.keys) - - let commonSources = firstPaths - .intersection(secondPaths) - .map { (firstHeaderDescriptorMap[$0]!, secondHeaderDescriptorMap[$0]!) } - .sorted { left, right in left.0.path < right.0.path } - - return commonSources - } - /// Returns attributes differences between header pairs - private func attributesDifferences(in headerDescriptorPairs: [HeaderDescriptorPair]) - -> [CompareResult.DifferentValues] { + private func attributesDifferences( + in headerDescriptorPairs: [HeaderDescriptorPair] + ) -> [CompareResult.DifferentValues] { return headerDescriptorPairs .filter { $0.attributes != $1.attributes } - .map { first, second in CompareResult.DifferentValues(context: "\(first.path) attributes", - first: first.attributes ?? "nil (Project)", - second: second.attributes ?? "nil (Project)") } - } - - /// Returns a dictionary that maps header descriptors by their path `[path: HeaderDescriptor]` - private func headerPathMap(from headerDescriptors: [HeaderDescriptor]) -> [String: HeaderDescriptor] { - return Dictionary(headerDescriptors.map { ($0.path, $0) }, uniquingKeysWith: { first, _ in first }) + .map { first, second in + CompareResult.DifferentValues( + context: "\(first.path) attributes", + first: first.attributes ?? "nil (Project)", + second: second.attributes ?? "nil (Project)" + ) + } } } diff --git a/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift b/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift index 5667b05..77a7fcc 100644 --- a/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift +++ b/Sources/XCDiffCore/Comparator/LinkedDependenciesComparator.swift @@ -37,48 +37,22 @@ final class LinkedDependenciesComparator: Comparator { } private func createLinkedDependenciesResults(commonTarget: TargetPair) throws -> CompareResult { - let firstDependencies = try targetsHelper.linkedDependencies(from: commonTarget.first) - let secondDependencies = try targetsHelper.linkedDependencies(from: commonTarget.second) - - let firstPaths = Set(firstDependencies.compactMap { dependencyKey(dependency: $0) }) - let secondPaths = Set(secondDependencies.compactMap { dependencyKey(dependency: $0) }) - - let descriptorPairs = commonDependencyDescriptorPairs(first: firstDependencies, - second: secondDependencies) - - let attributesDifferences = self.attributesDifferences(in: descriptorPairs) - let packagesDifferences = packageDifferences(in: descriptorPairs) - - return result(context: ["\"\(commonTarget.first.name)\" target"], - first: firstPaths, - second: secondPaths, - differentValues: attributesDifferences + packagesDifferences) - } - - private func dependencyKey(dependency: LinkedDependencyDescriptor) -> String? { - if let key = dependency.name ?? dependency.path { return key } - return nil - } - - private func commonDependencyDescriptorPairs(first: [LinkedDependencyDescriptor], - second: [LinkedDependencyDescriptor]) -> [DependencyDescriptorPair] { - let firstDependencyDescriptorMap = dependencyPathMap(from: first) - let secondDependencyDescriptorMap = dependencyPathMap(from: second) - - let firstPaths = Set(firstDependencyDescriptorMap.keys) - let secondPaths = Set(secondDependencyDescriptorMap.keys) - - let commonSources = firstPaths - .intersection(secondPaths) - .map { (firstDependencyDescriptorMap[$0]!, secondDependencyDescriptorMap[$0]!) } - .sorted { left, right in - if let keyLeft = left.0.name ?? left.0.path, - let keyRight = right.0.name ?? right.0.path { - return keyLeft < keyRight - } - return false + let firstDependencies = try targetsHelper + .linkedDependencies(from: commonTarget.first) + .filter { $0.key != nil } + let secondDependencies = try targetsHelper + .linkedDependencies(from: commonTarget.second) + .filter { $0.key != nil } + + return result( + context: ["\"\(commonTarget.first.name)\" target"], + first: firstDependencies, + second: secondDependencies, + diffCommonValues: { commonPairs in + attributesDifferences(in: commonPairs) + + packageDifferences(in: commonPairs) } - return commonSources + ) } private func attributesDifferences(in dependencyDescriptorPairs: [DependencyDescriptorPair]) @@ -86,7 +60,7 @@ final class LinkedDependenciesComparator: Comparator { return dependencyDescriptorPairs .filter { $0.type != $1.type } .compactMap { first, second -> CompareResult.DifferentValues? in - if let key = dependencyKey(dependency: first) { + if let key = first.key { return .init(context: "\(key) attributes", first: first.type.rawValue, second: second.type.rawValue) @@ -100,7 +74,7 @@ final class LinkedDependenciesComparator: Comparator { return dependencyDescriptorPairs .filter { $0.package != $1.package } .compactMap { first, second -> CompareResult.DifferentValues? in - if let key = dependencyKey(dependency: first) { + if let key = first.key { return .init(context: "\(key) package reference", first: first.package?.difference(from: second.package), second: second.package?.difference(from: first.package)) @@ -108,13 +82,4 @@ final class LinkedDependenciesComparator: Comparator { return nil } } - - private func dependencyPathMap(from dependencyDescriptors: [LinkedDependencyDescriptor]) - -> [String: LinkedDependencyDescriptor] { - return Dictionary(dependencyDescriptors.compactMap { - if let key = dependencyKey(dependency: $0) { return (key, $0) } - return nil - }, - uniquingKeysWith: { first, _ in first }) - } } diff --git a/Sources/XCDiffCore/Comparator/SourcesComparator.swift b/Sources/XCDiffCore/Comparator/SourcesComparator.swift index cbfbaae..ecbfc58 100644 --- a/Sources/XCDiffCore/Comparator/SourcesComparator.swift +++ b/Sources/XCDiffCore/Comparator/SourcesComparator.swift @@ -22,58 +22,42 @@ final class SourcesComparator: Comparator { let tag = "sources" private let targetsHelper = TargetsHelper() - func compare(_ first: ProjectDescriptor, - _ second: ProjectDescriptor, - parameters: ComparatorParameters) throws -> [CompareResult] { + func compare( + _ first: ProjectDescriptor, + _ second: ProjectDescriptor, + parameters: ComparatorParameters + ) throws -> [CompareResult] { let commonTargets = try targetsHelper.commonTargets(first, second, parameters: parameters) return try commonTargets .map { firstTarget, secondTarget -> CompareResult in let firstSources = try targetsHelper.sources(from: firstTarget, sourceRoot: first.sourceRoot) let secondSources = try targetsHelper.sources(from: secondTarget, sourceRoot: second.sourceRoot) - let firstPaths = Set(firstSources.map { $0.path }) - let secondPaths = Set(secondSources.map { $0.path }) - - let commonSources = commonSourcesPair(first: firstSources, second: secondSources) - let difference = compilerFlagDifferences(in: commonSources) - - return result(context: ["\"\(firstTarget.name)\" target"], - first: firstPaths, - second: secondPaths, - differentValues: difference) + return result( + context: ["\"\(firstTarget.name)\" target"], + first: firstSources, + second: secondSources, + diffCommonValues: { commonPairs in + compilerFlagDifferences(in: commonPairs) + } + ) } } - /// Returns common sources as a source pair - private func commonSourcesPair(first: [SourceDescriptor], second: [SourceDescriptor]) -> [SourcePair] { - let firstSourcesMapping = sourcePathMapping(from: first) - let secondSourcesMapping = sourcePathMapping(from: second) - - let firstPaths = Set(firstSourcesMapping.keys) - let secondPaths = Set(secondSourcesMapping.keys) - - let commonSources: [SourcePair] = firstPaths.intersection(secondPaths).map { - (firstSourcesMapping[$0]!, secondSourcesMapping[$0]!) - } - - return commonSources - } - /// Returns compiler flag differences between source pairs - private func compilerFlagDifferences(in sourcePairs: [SourcePair]) -> [CompareResult.DifferentValues] { + private func compilerFlagDifferences( + in sourcePairs: [SourcePair] + ) -> [CompareResult.DifferentValues] { return sourcePairs.compactMap { sourcePair in let (first, second) = sourcePair if first.flags != second.flags { - return CompareResult.DifferentValues(context: "\(first.path) compiler flags", - first: first.flags, - second: second.flags) + return CompareResult.DifferentValues( + context: "\(first.path) compiler flags", + first: first.flags, + second: second.flags + ) } return nil } } - - /// Returns a dictionary that maps sources by their path `[path: SourceDescriptor]` - private func sourcePathMapping(from sources: [SourceDescriptor]) -> [String: SourceDescriptor] { - return Dictionary(sources.map { ($0.path, $0) }, uniquingKeysWith: { first, _ in first }) - } } diff --git a/Sources/XCDiffCore/Comparator/SwiftPackagesComparator.swift b/Sources/XCDiffCore/Comparator/SwiftPackagesComparator.swift index b5b8bc6..5f1f2cd 100644 --- a/Sources/XCDiffCore/Comparator/SwiftPackagesComparator.swift +++ b/Sources/XCDiffCore/Comparator/SwiftPackagesComparator.swift @@ -28,35 +28,21 @@ final class SwiftPackagesComparator: Comparator { let firstPackages = try targetsHelper.swiftPackages(in: first) let secondPackages = try targetsHelper.swiftPackages(in: second) - let firstDictionary = Dictionary(firstPackages.map { ($0.identifier, $0) }, uniquingKeysWith: { $1 }) - let secondDictionary = Dictionary(secondPackages.map { ($0.identifier, $0) }, uniquingKeysWith: { $1 }) - - let onlyInFirst = firstPackages.filter { secondDictionary[$0.identifier] == nil } - let onlyInSecond = secondPackages.filter { firstDictionary[$0.identifier] == nil } - let common = firstPackages.filter { secondDictionary[$0.identifier] != nil } - let differences = common.filter { - firstDictionary[$0.identifier] != secondDictionary[$0.identifier] - } - - let differencesValues: [CompareResult.DifferentValues] = differences.compactMap { - guard let first = firstDictionary[$0.identifier], - let second = secondDictionary[$0.identifier] else { - return nil - } - - return CompareResult.DifferentValues( - context: "\($0.name) (\($0.url))", - first: first.difference(from: second), - second: second.difference(from: first) - ) - } - return [ - CompareResult( - tag: tag, - onlyInFirst: onlyInFirst.map(\.description), - onlyInSecond: onlyInSecond.map(\.description), - differentValues: differencesValues + result( + first: firstPackages, + second: secondPackages, + diffCommonValues: { commonPairs in + commonPairs.filter { + $0 != $1 + }.map { first, second in + CompareResult.DifferentValues( + context: "\(first.name) (\(first.url))", + first: first.difference(from: second), + second: second.difference(from: first) + ) + } + } ), ] } diff --git a/Sources/XCDiffCore/Library/Comparator+Extensions.swift b/Sources/XCDiffCore/Library/Comparator+Extensions.swift index 7bbecf6..2050e5d 100644 --- a/Sources/XCDiffCore/Library/Comparator+Extensions.swift +++ b/Sources/XCDiffCore/Library/Comparator+Extensions.swift @@ -16,6 +16,15 @@ import Foundation +protocol DiffComparable: Hashable { + var diffKey: String { get } + var diffDescription: String { get } +} + +extension DiffComparable { + var diffDescription: String { diffKey } +} + extension Comparator { func result(context: [String] = [], description: String? = nil, @@ -78,4 +87,50 @@ extension Comparator { second: second, differentValues: differentValues)] } + + typealias DiffCommonValues = ( + [(first: T, second: T)] + ) -> [CompareResult.DifferentValues] + + func result( + context: [String] = [], + description: String? = nil, + first: [T] = [], + second: [T] = [], + diffCommonValues: DiffCommonValues = { _ in [] } + ) -> CompareResult { + let firstKeys = Set(first.map(\.diffKey)) + let secondKeys = Set(second.map(\.diffKey)) + + let firstMap = Dictionary( + first.map { ($0.diffKey, $0) }, + uniquingKeysWith: { $1 } + ) + + let secondMap = Dictionary( + second.map { ($0.diffKey, $0) }, + uniquingKeysWith: { $1 } + ) + + let common = firstKeys + .intersection(secondKeys) + .sorted() + .map { key in + let commonFirst = firstMap[key]! + let commonSecond = secondMap[key]! + return (first: commonFirst, second: commonSecond) + } + + return result( + context: context, + description: description, + onlyInFirst: firstKeys.subtractingAndSorted(secondKeys).compactMap { + firstMap[$0]?.diffDescription + }, + onlyInSecond: secondKeys.subtractingAndSorted(firstKeys).compactMap { + secondMap[$0]?.diffDescription + }, + differentValues: diffCommonValues(common) + ) + } } diff --git a/Sources/XCDiffCore/Library/Descriptors/SwiftPackageDescriptor.swift b/Sources/XCDiffCore/Library/Descriptors/SwiftPackageDescriptor.swift index ed2b9e3..7b3fcec 100644 --- a/Sources/XCDiffCore/Library/Descriptors/SwiftPackageDescriptor.swift +++ b/Sources/XCDiffCore/Library/Descriptors/SwiftPackageDescriptor.swift @@ -54,3 +54,13 @@ struct SwiftPackageDescriptor: Hashable, CustomStringConvertible, Comparable { return lhs.description < rhs.description } } + +extension SwiftPackageDescriptor: DiffComparable { + var diffKey: String { + identifier + } + + var diffDescription: String { + description + } +} diff --git a/Sources/XCDiffCore/Library/TargetsHelper.swift b/Sources/XCDiffCore/Library/TargetsHelper.swift index 6f0906d..5b78931 100644 --- a/Sources/XCDiffCore/Library/TargetsHelper.swift +++ b/Sources/XCDiffCore/Library/TargetsHelper.swift @@ -20,17 +20,33 @@ import XcodeProj typealias TargetPair = (first: PBXTarget, second: PBXTarget) -struct SourceDescriptor: Hashable { +struct SourceDescriptor: Hashable, DiffComparable { + var diffKey: String { + path + } + let path: String let flags: String? } -struct HeaderDescriptor: Hashable { +struct HeaderDescriptor: Hashable, DiffComparable { + var diffKey: String { + path + } + let path: String let attributes: String? } -struct LinkedDependencyDescriptor: Hashable { +struct LinkedDependencyDescriptor: Hashable, DiffComparable { + var diffKey: String { + key ?? "" + } + + var key: String? { + name ?? path + } + let name: String? let path: String? let package: SwiftPackageDescriptor?