From 77f4fb17dc622ae605062f752b92d8f3f6a5010a Mon Sep 17 00:00:00 2001 From: Alex Guretzki Date: Wed, 30 Apr 2025 16:36:11 +0200 Subject: [PATCH] Consolidating changes on function/enum parameters --- .../EnumCaseDeclSyntax+SwiftInterface.swift | 1 + .../FunctionDeclSyntax+SwiftInterface.swift | 2 +- ...InitializerDeclSyntax+SwiftInterface.swift | 2 +- .../SubscriptDeclSyntax+SwiftInterface.swift | 3 +- .../SwiftInterfaceElement+EnumCase.swift | 39 +----- .../SwiftInterfaceElement+Function.swift | 47 +------ .../SwiftInterfaceElement+Initializer.swift | 8 +- .../SwiftInterfaceElement+Subscript.swift | 42 +------ ...nterfaceElement+DiffHelper+Parameter.swift | 116 ++++++++++++++++++ .../SwiftInterfaceElement+DiffHelper.swift | 37 +++--- ...nterfaceElement+ParameterDescription.swift | 2 +- .../SwiftInterfaceElementParameter.swift | 50 ++++++++ ...ference-changes-swift-interface-private.md | 9 +- ...eference-changes-swift-interface-public.md | 3 +- 14 files changed, 211 insertions(+), 150 deletions(-) create mode 100644 Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift create mode 100644 Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift index aff5b78..df57620 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/EnumCaseDeclSyntax+SwiftInterface.swift @@ -22,6 +22,7 @@ extension EnumCaseDeclSyntax { name: $0.name.trimmedDescription, parameters: $0.parameterClause?.parameters.map { .init( + attributes: [], firstName: $0.firstName?.trimmedDescription, secondName: $0.secondName?.trimmedDescription, type: $0.type.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift index a9f8da7..c5f200c 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/FunctionDeclSyntax+SwiftInterface.swift @@ -23,7 +23,7 @@ extension FunctionDeclSyntax { } } - let parameters: [SwiftInterfaceFunction.Parameter] = self.signature.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.signature.parameterClause.parameters.map { .init( attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift index 47afcd2..1d5360b 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/InitializerDeclSyntax+SwiftInterface.swift @@ -23,7 +23,7 @@ extension InitializerDeclSyntax { } } - let parameters: [SwiftInterfaceFunction.Parameter] = self.signature.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.signature.parameterClause.parameters.map { .init( attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift index 8c766f2..595ba95 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/DeclSyntax+SwiftInterface/SubscriptDeclSyntax+SwiftInterface.swift @@ -12,8 +12,9 @@ extension SubscriptDeclSyntax { func toInterfaceElement() -> SwiftInterfaceSubscript { - let parameters: [SwiftInterfaceSubscript.Parameter] = self.parameterClause.parameters.map { + let parameters: [SwiftInterfaceElementParameter] = self.parameterClause.parameters.map { .init( + attributes: $0.attributes.sanitizedList, firstName: $0.firstName.trimmedDescription, secondName: $0.secondName?.trimmedDescription, type: $0.type.trimmedDescription, diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift index e14a055..fe5cd40 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+EnumCase.swift @@ -6,39 +6,6 @@ import Foundation -extension SwiftInterfaceEnumCase { - - struct Parameter { - - let firstName: String? - - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - var description = [ - firstName, - secondName - ].compactMap { $0 }.joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceEnumCase: SwiftInterfaceElement { /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... @@ -49,7 +16,7 @@ class SwiftInterfaceEnumCase: SwiftInterfaceElement { let name: String - let parameters: [Parameter]? + let parameters: [SwiftInterfaceElementParameter]? let rawValue: String? @@ -72,7 +39,7 @@ class SwiftInterfaceEnumCase: SwiftInterfaceElement { attributes: [String], modifiers: [String], name: String, - parameters: [Parameter]?, + parameters: [SwiftInterfaceElementParameter]?, rawValue: String? ) { self.attributes = attributes @@ -90,7 +57,7 @@ extension SwiftInterfaceEnumCase { guard let other = otherElement as? Self else { return [] } changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters?.map(\.description), newValues: parameters?.map(\.description)) + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "raw value", oldValue: other.rawValue, newValue: rawValue) return changes.compactMap { $0 } } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift index 9d937d3..89f0aaa 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Function.swift @@ -6,45 +6,6 @@ import Foundation -extension SwiftInterfaceFunction { - - struct Parameter { - - /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... - let attributes: [String] - - let firstName: String - - /// optional second "internal" name - can be ignored - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - let names = [ - firstName, - secondName - ].compactMap { $0 } - - var description = (attributes + names).joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceFunction: SwiftInterfaceElement { /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... @@ -55,7 +16,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [Parameter] + let parameters: [SwiftInterfaceElementParameter] /// e.g. async, throws, rethrows let effectSpecifiers: [String] @@ -76,7 +37,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "\(name)(\(parameters.map { "\($0.firstName):" }.joined()))" + "\(name)(\(parameters.map(\.valueForDiffableSignature).joined()))" } var consolidatableName: String { name } @@ -90,7 +51,7 @@ class SwiftInterfaceFunction: SwiftInterfaceElement { modifiers: [String], name: String, genericParameterDescription: String?, - parameters: [Parameter], + parameters: [SwiftInterfaceElementParameter], effectSpecifiers: [String], returnType: String?, genericWhereClauseDescription: String? @@ -114,7 +75,7 @@ extension SwiftInterfaceFunction { changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "effect", oldValues: other.effectSpecifiers, newValues: effectSpecifiers) changes += diffDescription(propertyType: "return type", oldValue: other.returnType, newValue: returnType) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift index fafca00..6aa6de3 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Initializer.swift @@ -16,7 +16,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [SwiftInterfaceFunction.Parameter] + let parameters: [SwiftInterfaceElementParameter] /// e.g. async, throws, rethrows let effectSpecifiers: [String] @@ -35,7 +35,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "init(\(parameters.map { "\($0.firstName):" }.joined()))" + "init(\(parameters.map { $0.valueForDiffableSignature }.joined()))" } var consolidatableName: String { "init" } @@ -49,7 +49,7 @@ class SwiftInterfaceInitializer: SwiftInterfaceElement { modifiers: [String], optionalMark: String?, genericParameterDescription: String?, - parameters: [SwiftInterfaceFunction.Parameter], + parameters: [SwiftInterfaceElementParameter], effectSpecifiers: [String], genericWhereClauseDescription: String? ) { @@ -72,7 +72,7 @@ extension SwiftInterfaceInitializer { changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "optional mark", oldValue: other.optionalMark, newValue: optionalMark) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "effect", oldValues: other.effectSpecifiers, newValues: effectSpecifiers) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) return changes.compactMap { $0 } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift index 676aabe..7c295fd 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement+Declaration/SwiftInterfaceElement+Subscript.swift @@ -6,40 +6,6 @@ import Foundation -extension SwiftInterfaceSubscript { - - struct Parameter { - - let firstName: String - - /// optional second "internal" name - can be ignored - let secondName: String? - - let type: String - - let defaultValue: String? - - var description: String { - var description = [ - firstName, - secondName - ].compactMap { $0 }.joined(separator: " ") - - if description.isEmpty { - description += "\(type)" - } else { - description += ": \(type)" - } - - if let defaultValue { - description += " = \(defaultValue)" - } - - return description - } - } -} - class SwiftInterfaceSubscript: SwiftInterfaceElement { let name: String = "subscript" @@ -53,7 +19,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { /// e.g. let genericParameterDescription: String? - let parameters: [Parameter] + let parameters: [SwiftInterfaceElementParameter] let returnType: String @@ -69,7 +35,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { var parent: (any SwiftInterfaceElement)? var diffableSignature: String { - "\(name)(\(parameters.map { "\($0.firstName):" }.joined()))" + "\(name)(\(parameters.map(\.valueForDiffableSignature).joined()))" } var consolidatableName: String { name } @@ -82,7 +48,7 @@ class SwiftInterfaceSubscript: SwiftInterfaceElement { attributes: [String], modifiers: [String], genericParameterDescription: String?, - parameters: [Parameter], + parameters: [SwiftInterfaceElementParameter], returnType: String, genericWhereClauseDescription: String?, accessors: String? @@ -105,7 +71,7 @@ extension SwiftInterfaceSubscript { changes += diffDescription(propertyType: "attribute", oldValues: other.attributes, newValues: attributes) changes += diffDescription(propertyType: "modifier", oldValues: other.modifiers, newValues: modifiers) changes += diffDescription(propertyType: "generic parameter description", oldValue: other.genericParameterDescription, newValue: genericParameterDescription) - changes += diffDescription(propertyType: "parameter", oldValues: other.parameters.map(\.description), newValues: parameters.map(\.description)) // TODO: Maybe have a better way to show changes + changes += diffDescription(oldParameters: other.parameters, newParameters: parameters) changes += diffDescription(propertyType: "return type", oldValue: other.returnType, newValue: returnType) changes += diffDescription(propertyType: "generic where clause", oldValue: other.genericWhereClauseDescription, newValue: genericWhereClauseDescription) changes += diffDescription(propertyType: "accessors", oldValue: other.accessors, newValue: accessors) diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift new file mode 100644 index 0000000..f1f65a9 --- /dev/null +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper+Parameter.swift @@ -0,0 +1,116 @@ +// +// SwiftInterfaceElement+DiffHelper+Parameter.swift +// public-api-diff +// +// Created by Alexander Guretzki on 30/04/2025. +// + +import Foundation +import PADCore + +extension SwiftInterfaceElement { + + /// Returns a list of change descriptions for changes between the old and new values + /// - Parameters: + /// - propertyType: The property type name (e.g. "accessor", "modifier", "generic where clause", ...) + /// for additional information + /// - oldParameters: The (optional) old parameters + /// - newParameters: The (optional) new parameters + /// - Returns: A list of change descriptions caused by a value change + func diffDescription( + oldParameters: [SwiftInterfaceElementParameter]?, + newParameters: [SwiftInterfaceElementParameter]? + ) -> [String] { + let propertyType = "parameter" + + guard let oldParameters else { + guard let newParameters else { return [] } + return newParameters.map { "Added \(propertyType) `\($0.description)`" } + } + + guard let newParameters else { + return oldParameters.map { "Removed \(propertyType) `\($0.description)`" } + } + + let oldParametersByName = oldParameters.indexedByFirstName + let newParametersByName = newParameters.indexedByFirstName + + var changes = [String]() + + // Check for removed parameters + oldParameters.enumerated().forEach { index, oldParameter in + let oldFirstName = oldParameter.firstName ?? "Parameter \(index)" + if newParametersByName[oldFirstName] == nil { + changes.append("Removed \(propertyType) `\(oldParameter.description)`") + } + } + + // Check for added and modified parameters + newParameters.enumerated().forEach { index, newParameter in + + let newFirstName = newParameter.firstName ?? "Parameter \(index)" + + guard let oldParameter = oldParametersByName[newFirstName] else { + // Parameter was added + changes.append("Added \(propertyType) `\(newParameter.description)`") + return + } + + let modificationDiffPrefix = modificationDiffDescriptionPrefix( + propertyType: propertyType, + firstName: oldParameter.firstName, + index: index + ) + + // Check if the type has changed + if oldParameter.type != newParameter.type { + changes.append("\(modificationDiffPrefix): Changed type from `\(oldParameter.type)` to `\(newParameter.type)`") + } + + // Check if the default value has changed + changes += diffDescription( + propertyType: "default value", + oldValue: oldParameter.defaultValue, + newValue: newParameter.defaultValue + ).map { "\(modificationDiffPrefix)`: \($0)" } + + // Check if the attributes did change + changes += diffDescription( + propertyType: "attribute", + oldValues: oldParameter.attributes, + newValues: newParameter.attributes + ).map { "\(modificationDiffPrefix)`: \($0)" } + } + + return changes + } +} + +fileprivate extension SwiftInterfaceElement { + + func modificationDiffDescriptionPrefix( + propertyType: String, + firstName: String?, + index: Int + ) -> String { + let ordinalFormatter = NumberFormatter() + ordinalFormatter.numberStyle = .ordinal + + if let firstName { + return "Modified \(propertyType) `\(firstName)`" + } else { + return "Modified \(ordinalFormatter.string(from: NSNumber(value: index+1)) ?? "\(index + 1)") \(propertyType)" + } + } +} + +private extension [SwiftInterfaceElementParameter] { + + var indexedByFirstName: [String: SwiftInterfaceElementParameter] { + Dictionary( + uniqueKeysWithValues: self.enumerated().map { + ($0.element.firstName ?? "Parameter \($0.offset)", $0.element) + } + ) + } +} diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift index 88de350..71d2f8e 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+DiffHelper.swift @@ -44,29 +44,32 @@ extension SwiftInterfaceElement { /// Returns a list of change descriptions for changes between the old and new values /// - Parameters: - /// - propertyType: The (optional) property type name (e.g. "accessor", "modifier", "generic where clause", ...) for additional information - /// - oldValue: The (optional) old values - /// - newValue: The (optional) new values + /// - propertyType: The property type name (e.g. "accessor", "modifier", "generic where clause", ...) + /// for additional information + /// - oldValues: The (optional) old values + /// - newValues: The (optional) new values /// - Returns: A list of change descriptions caused by a value change - func diffDescription(propertyType: String, oldValues: [String]?, newValues: [String]?) -> [String] { + func diffDescription( + propertyType: String, + oldValues: [String]?, + newValues: [String]? + ) -> [String] { - if let oldValues, let newValues { - let old = Set(oldValues) - let new = Set(newValues) - return old.symmetricDifference(new).map { - "\(new.contains($0) ? "Added" : "Removed") \(propertyType) `\($0)`" - } + guard let oldValues else { + guard let newValues else { return [] } + return newValues.map { "Added \(propertyType) `\($0.description)`" } } - if let oldValues { - return oldValues.map { "Removed \(propertyType) `\($0)`" } + guard let newValues else { + return oldValues.map { "Removed \(propertyType) `\($0.description)`" } } - - if let newValues { - return newValues.map { "Added \(propertyType) `\($0)`" } + + let old = Set(oldValues) + let new = Set(newValues) + + return old.symmetricDifference(new).map { + "\(new.contains($0) ? "Added" : "Removed") \(propertyType) `\($0)`" } - - return [] } } diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift index 9875d9d..f99ace0 100644 --- a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElement+ParameterDescription.swift @@ -9,7 +9,7 @@ import Foundation extension SwiftInterfaceElement { func formattedParameterDescription(for parameterDescriptions: [String]) -> String { - // We're only doing multiline formatting if we have more than 1 character + // We're only doing multiline formatting if we have more than 1 parameter guard parameterDescriptions.count > 1 else { return parameterDescriptions.joined(separator: ", ") } let spacer = " " diff --git a/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift new file mode 100644 index 0000000..ba017f2 --- /dev/null +++ b/Sources/PublicModules/PADSwiftInterfaceDiff/SwiftInterfaceParser/SwiftInterfaceElement/SwiftInterfaceElementParameter.swift @@ -0,0 +1,50 @@ +// +// SwiftInterfaceElementParameter.swift +// public-api-diff +// +// Created by Alexander Guretzki on 30/04/2025. +// + + +struct SwiftInterfaceElementParameter { + + /// e.g. @discardableResult, @MainActor, @objc, @_spi(...), ... + let attributes: [String] + + let firstName: String? + + /// optional second "internal" name - can be ignored + let secondName: String? + + let type: String + + let defaultValue: String? + + var description: String { + let names = [ + firstName, + secondName + ].compactMap { $0 } + + var description = (attributes + names).joined(separator: " ") + + if description.isEmpty { + description += "\(type)" + } else { + description += ": \(type)" + } + + if let defaultValue { + description += " = \(defaultValue)" + } + + return description + } +} + +extension SwiftInterfaceElementParameter { + + var valueForDiffableSignature: String { + "\(firstName ?? "_"):" + } +} diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md index 4bba9d0..80a5c4a 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-private.md @@ -271,8 +271,7 @@ indirect case recursive(ReferencePackage.CustomEnum) /** Changes: -- Added parameter `ReferencePackage.CustomEnum` -- Removed parameter `ReferencePackage.CustomEnum` +- Modified 1st parameter: Changed type from `ReferencePackage.CustomEnum` to `ReferencePackage.CustomEnum` */ ``` #### ❌ Removed @@ -444,10 +443,8 @@ public init( /** Changes: -- Added parameter `getSetVar: T` -- Added parameter `getVar: T` -- Removed parameter `getSetVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` -- Removed parameter `getVar: ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` +- Modified parameter `getSetVar`: Changed type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` +- Modified parameter `getVar`: Changed type from `ReferencePackage.OpenSpiConformingClass.CustomAssociatedType` to `T` */ ``` ```javascript diff --git a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md index 3b453e3..901e6a1 100644 --- a/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md +++ b/Tests/IntegrationTests/Resources/expected-reference-changes-swift-interface-public.md @@ -256,8 +256,7 @@ indirect case recursive(ReferencePackage.CustomEnum) /** Changes: -- Added parameter `ReferencePackage.CustomEnum` -- Removed parameter `ReferencePackage.CustomEnum` +- Modified 1st parameter: Changed type from `ReferencePackage.CustomEnum` to `ReferencePackage.CustomEnum` */ ``` #### ❌ Removed