From 3167c648b18b6de618e3649abb0c34ef39b92b6e Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Sun, 28 Jul 2024 15:29:29 +0200 Subject: [PATCH] Add `Hashable` conformance to generated type (#110) * Extract String(describing: key) helper into computed property for reuse * generate type with Hashable and Equatable conformance * Support older versions of Swift Syntax * Bump minimum version of swift-snapshot-testing to 1.17.0 * Remove redundant Equatable type from inheretence clause --- Package.swift | 2 +- .../Extensions/TokenSyntax+Types.swift | 4 ++ ...e_StringExtension_StringsTableStruct.swift | 6 ++ ...tringStringsTableArgumentEnumSnippet.swift | 2 +- ...tringsTableComparisonFunctionSnippet.swift | 59 +++++++++++++++++++ ...gStringsTableHashIntoFunctionSnippet.swift | 54 +++++++++++++++++ .../StringStringsTableStructSnippet.swift | 23 +++++++- ...nderscoredKeyComputedPropertySnippet.swift | 44 ++++++++++++++ ...LocalizedStringKeyOverrideKeySnippet.swift | 4 +- .../SwiftUI/TextInitializerSnippet.swift | 2 +- Sources/StringGenerator/StringGenerator.swift | 2 +- Tests/PluginTests/PluginTests.swift | 10 ++++ Tests/XCStringsToolTests/GenerateTests.swift | 8 ++- .../testGenerate.FormatSpecifiers.swift | 24 ++++++-- .../testGenerate.Localizable.swift | 24 ++++++-- .../testGenerate.Multiline.swift | 24 ++++++-- .../testGenerate.Positional.swift | 24 ++++++-- .../GenerateTests/testGenerate.Simple.swift | 24 ++++++-- .../testGenerate.Substitution.swift | 24 ++++++-- .../testGenerate.Variations.swift | 24 ++++++-- ...nerateWithLegacyFilesCombined.Legacy.swift | 24 ++++++-- ...testGenerateWithLegacyStrings.Legacy.swift | 24 ++++++-- ...GenerateWithLegacyStringsdict.Legacy.swift | 24 ++++++-- ...teWithPackageAccessLevel.Localizable.swift | 24 ++++++-- ...ateWithPublicAccessLevel.Localizable.swift | 24 ++++++-- 25 files changed, 439 insertions(+), 69 deletions(-) create mode 100644 Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableComparisonFunctionSnippet.swift create mode 100644 Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableHashIntoFunctionSnippet.swift create mode 100644 Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableUnderscoredKeyComputedPropertySnippet.swift diff --git a/Package.swift b/Package.swift index 1c55cb5..327d813 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.3"), .package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0" ..< "601.0.0-prerelease"), - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.13.0"), + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.17.0"), .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"), ], targets: [ diff --git a/Sources/StringGenerator/Extensions/TokenSyntax+Types.swift b/Sources/StringGenerator/Extensions/TokenSyntax+Types.swift index 45f98de..945d99d 100644 --- a/Sources/StringGenerator/Extensions/TokenSyntax+Types.swift +++ b/Sources/StringGenerator/Extensions/TokenSyntax+Types.swift @@ -22,6 +22,10 @@ extension TokenSyntax { case LocalizedStringKey case Text case Sendable + case Hasher + case Bool + case Equatable + case Hashable } static func type(_ value: MetaType) -> TokenSyntax { diff --git a/Sources/StringGenerator/Models/SourceFile_StringExtension_StringsTableStruct.swift b/Sources/StringGenerator/Models/SourceFile_StringExtension_StringsTableStruct.swift index d5f2365..3b6b51a 100644 --- a/Sources/StringGenerator/Models/SourceFile_StringExtension_StringsTableStruct.swift +++ b/Sources/StringGenerator/Models/SourceFile_StringExtension_StringsTableStruct.swift @@ -38,6 +38,10 @@ extension SourceFile.StringExtension { [keyProperty, argumentsProperty, tableProperty] } + var comparableProperties: [Property] { + [_keyProperty, argumentsProperty, tableProperty] + } + let keyProperty = Property(name: "key", type: .identifier(.StaticString)) let argumentsProperty = Property(name: "arguments", type: ArrayTypeSyntax(element: .identifier(.Argument))) @@ -46,6 +50,8 @@ extension SourceFile.StringExtension { let bundleProperty = Property(name: "bundle", type: .identifier(.Bundle)) + let _keyProperty = Property(name: "_key", type: .identifier(.String)) + let defaultValueProperty = Property( name: "defaultValue", type: MemberTypeSyntax( diff --git a/Sources/StringGenerator/Snippets/String/StringsTable/Argument/StringStringsTableArgumentEnumSnippet.swift b/Sources/StringGenerator/Snippets/String/StringsTable/Argument/StringStringsTableArgumentEnumSnippet.swift index 4bb0f57..7c17561 100644 --- a/Sources/StringGenerator/Snippets/String/StringsTable/Argument/StringStringsTableArgumentEnumSnippet.swift +++ b/Sources/StringGenerator/Snippets/String/StringsTable/Argument/StringStringsTableArgumentEnumSnippet.swift @@ -20,7 +20,7 @@ struct StringStringsTableArgumentEnumSnippet: Snippet { } var inheritanceClause: InheritanceClauseSyntax? { - InheritanceClauseSyntax(.Sendable) + InheritanceClauseSyntax(.Hashable, .Sendable) } } diff --git a/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableComparisonFunctionSnippet.swift b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableComparisonFunctionSnippet.swift new file mode 100644 index 0000000..7a42f37 --- /dev/null +++ b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableComparisonFunctionSnippet.swift @@ -0,0 +1,59 @@ +import SwiftSyntax +import SwiftSyntaxBuilder + +struct StringStringsTableComparisonFunctionSnippet: Snippet { + let stringsTable: SourceFile.StringExtension.StringsTableStruct + let leftArg: TokenSyntax = "lhs" + let rightArg: TokenSyntax = "rhs" + + var syntax: some DeclSyntaxProtocol { + FunctionDeclSyntax( + modifiers: modifiers, + name: .binaryOperator("=="), + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax { + FunctionParameterSyntax( + firstName: leftArg, + type: IdentifierTypeSyntax(name: stringsTable.type) + ) + FunctionParameterSyntax( + firstName: rightArg, + type: IdentifierTypeSyntax(name: stringsTable.type) + ) + }, + returnClause: ReturnClauseSyntax(type: .identifier(.Bool)) + ), + body: CodeBlockSyntax(statements: body) + ) + } + + @DeclModifierListBuilder + var modifiers: DeclModifierListSyntax { + DeclModifierSyntax(name: stringsTable.accessLevel.token) + DeclModifierSyntax(name: .keyword(.static)) + } + + var body: CodeBlockItemListSyntax { + // Array of `lhs.foo == rhs.foo` + var comparisons = stringsTable.comparableProperties.map { property in + InfixOperatorExprSyntax( + leftOperand: MemberAccessExprSyntax(leftArg, property.name), + operator: BinaryOperatorExprSyntax(operator: .binaryOperator("==")), + rightOperand: MemberAccessExprSyntax(rightArg, property.name) + ) + } + + var next = comparisons.removeLast() + while !comparisons.isEmpty { + let left = comparisons.removeLast() + + next = InfixOperatorExprSyntax( + leftOperand: left, + operator: BinaryOperatorExprSyntax(operator: .binaryOperator("&&")), + rightOperand: next + ) + } + + return [CodeBlockItemSyntax(item: .expr(ExprSyntax(next)))] + } +} diff --git a/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableHashIntoFunctionSnippet.swift b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableHashIntoFunctionSnippet.swift new file mode 100644 index 0000000..d7d97fb --- /dev/null +++ b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableHashIntoFunctionSnippet.swift @@ -0,0 +1,54 @@ +import SwiftSyntax +import SwiftSyntaxBuilder + +struct StringStringsTableHashIntoFunctionSnippet: Snippet { + let stringsTable: SourceFile.StringExtension.StringsTableStruct + let hasherToken: TokenSyntax = "hasher" + + var syntax: some DeclSyntaxProtocol { + FunctionDeclSyntax( + modifiers: modifiers, + name: "hash", + signature: FunctionSignatureSyntax( + parameterClause: FunctionParameterClauseSyntax { + FunctionParameterSyntax( + firstName: "into", + secondName: hasherToken, + type: hasherType + ) + } + ), + body: CodeBlockSyntax(statements: body) + ) + } + + var hasherType: AttributedTypeSyntax { + #if canImport(SwiftSyntax600) + AttributedTypeSyntax( + specifiers: TypeSpecifierListSyntax { + SimpleTypeSpecifierSyntax(specifier: .keyword(.inout)) + }, + baseType: IdentifierTypeSyntax(name: .type(.Hasher)) + ) + #else + AttributedTypeSyntax( + specifier: .keyword(.inout), + baseType: IdentifierTypeSyntax(name: .type(.Hasher)) + ) + #endif + } + + @DeclModifierListBuilder + var modifiers: DeclModifierListSyntax { + DeclModifierSyntax(name: stringsTable.accessLevel.token) + } + + @CodeBlockItemListBuilder + var body: CodeBlockItemListSyntax { + for property in stringsTable.comparableProperties { + FunctionCallExprSyntax(callee: MemberAccessExprSyntax(hasherToken, "combine")) { + LabeledExprSyntax(expression: DeclReferenceExprSyntax(baseName: property.name)) + } + } + } +} diff --git a/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableStructSnippet.swift b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableStructSnippet.swift index 7050291..ff269bd 100644 --- a/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableStructSnippet.swift +++ b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableStructSnippet.swift @@ -22,7 +22,7 @@ struct StringStringsTableStructSnippet: Snippet { } var inheritanceClause: InheritanceClauseSyntax? { - InheritanceClauseSyntax(.Sendable) + InheritanceClauseSyntax(.Hashable, .Sendable) } var memberBlock: MemberBlockSyntax { @@ -105,6 +105,27 @@ struct StringStringsTableStructSnippet: Snippet { StringStringsTableDefaultValueComputedPropertySnippet( stringsTable: stringsTable ) + .syntax + .with(\.trailingTrivia, .newlines(2)) + + // fileprivate var _key: String { String(describing: key) } + StringStringsTableUnderscoredKeyComputedPropertySnippet( + stringsTable: stringsTable + ) + .syntax + .with(\.trailingTrivia, .newlines(2)) + + // func hash(into hasher: Hasher) { ... } + StringStringsTableHashIntoFunctionSnippet( + stringsTable: stringsTable + ) + .syntax + .with(\.trailingTrivia, .newlines(2)) + + // static func ==(lhs: Localizable, rhs: Localizable) -> Bool { ... } + StringStringsTableComparisonFunctionSnippet( + stringsTable: stringsTable + ) } } diff --git a/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableUnderscoredKeyComputedPropertySnippet.swift b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableUnderscoredKeyComputedPropertySnippet.swift new file mode 100644 index 0000000..7ed84ab --- /dev/null +++ b/Sources/StringGenerator/Snippets/String/StringsTable/StringStringsTableUnderscoredKeyComputedPropertySnippet.swift @@ -0,0 +1,44 @@ +import SwiftSyntax +import SwiftSyntaxBuilder + +struct StringStringsTableUnderscoredKeyComputedPropertySnippet: Snippet { + let stringsTable: SourceFile.StringExtension.StringsTableStruct + + var syntax: some DeclSyntaxProtocol { + // fileprivate var _key: String { ... } + VariableDeclSyntax( + modifiers: modifiers, + bindingSpecifier: .keyword(.var) + ) { + PatternBindingSyntax( + pattern: IdentifierPatternSyntax(identifier: stringsTable._keyProperty.name), + typeAnnotation: typeAnnotation, + accessorBlock: AccessorBlockSyntax(accessors: .getter(body)) + ) + } + } + + @DeclModifierListBuilder + var modifiers: DeclModifierListSyntax { + DeclModifierSyntax(name: .keyword(.fileprivate)) + } + + var typeAnnotation: TypeAnnotationSyntax { + TypeAnnotationSyntax( + type: stringsTable._keyProperty.type + ) + } + + @CodeBlockItemListBuilder + var body: CodeBlockItemListSyntax { + // String(describing: key) + FunctionCallExprSyntax( + callee: DeclReferenceExprSyntax(baseName: .type(.String)) + ) { + LabeledExprSyntax( + label: "describing", + expression: DeclReferenceExprSyntax(baseName: stringsTable.keyProperty.name) + ) + } + } +} diff --git a/Sources/StringGenerator/Snippets/SwiftUI/LocalizedStringKeyOverrideKeySnippet.swift b/Sources/StringGenerator/Snippets/SwiftUI/LocalizedStringKeyOverrideKeySnippet.swift index 07f5079..3d60d72 100644 --- a/Sources/StringGenerator/Snippets/SwiftUI/LocalizedStringKeyOverrideKeySnippet.swift +++ b/Sources/StringGenerator/Snippets/SwiftUI/LocalizedStringKeyOverrideKeySnippet.swift @@ -12,11 +12,11 @@ struct LocalizedStringKeyOverrideKeySnippet: Snippet { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } """) diff --git a/Sources/StringGenerator/Snippets/SwiftUI/TextInitializerSnippet.swift b/Sources/StringGenerator/Snippets/SwiftUI/TextInitializerSnippet.swift index 334ffb7..e2c0645 100644 --- a/Sources/StringGenerator/Snippets/SwiftUI/TextInitializerSnippet.swift +++ b/Sources/StringGenerator/Snippets/SwiftUI/TextInitializerSnippet.swift @@ -91,7 +91,7 @@ extension TextInitializerSnippet: Snippet { ) { LabeledExprSyntax( label: "using", - expression: MemberAccessExprSyntax(variableToken, stringsTable.keyProperty.name) + expression: MemberAccessExprSyntax(variableToken, stringsTable._keyProperty.name) ) } .with(\.trailingTrivia, .newlines(2)) diff --git a/Sources/StringGenerator/StringGenerator.swift b/Sources/StringGenerator/StringGenerator.swift index 3284532..09963b3 100644 --- a/Sources/StringGenerator/StringGenerator.swift +++ b/Sources/StringGenerator/StringGenerator.swift @@ -32,7 +32,7 @@ private extension String { // https://github.com/liamnichols/xcstrings-tool/issues/97 func patchingSwift6CompatibilityIssuesIfNeeded() -> String { #if !canImport(SwiftSyntax600) - replacing(/[#@]available\s\(/, with: { match in + replacing(/(?:[#@]available|==)\s\(/, with: { match in match.output.filter { !$0.isWhitespace } }) #else diff --git a/Tests/PluginTests/PluginTests.swift b/Tests/PluginTests/PluginTests.swift index 5b46e7b..01f3add 100644 --- a/Tests/PluginTests/PluginTests.swift +++ b/Tests/PluginTests/PluginTests.swift @@ -65,4 +65,14 @@ final class PluginTests: XCTestCase { "Hello John, I have 1 plural" ) } + + func testComparisons() { + let foo1 = String.Localizable.demoBasic + let foo2 = String.Localizable.demoBasic + XCTAssertEqual(foo1, foo2) + + let bar1 = String.Localizable.multiline(1) + let bar2 = String.Localizable.multiline(2) + XCTAssertNotEqual(bar1, bar2) + } } diff --git a/Tests/XCStringsToolTests/GenerateTests.swift b/Tests/XCStringsToolTests/GenerateTests.swift index de59228..8240163 100644 --- a/Tests/XCStringsToolTests/GenerateTests.swift +++ b/Tests/XCStringsToolTests/GenerateTests.swift @@ -4,6 +4,12 @@ import SnapshotTesting import XCTest final class GenerateTests: FixtureTestCase { + override func invokeTest() { + withSnapshotTesting(record: .missing) { + super.invokeTest() + } + } + func testGenerate() throws { try eachFixture { inputURL in if !inputURL.lastPathComponent.hasPrefix("!") { @@ -78,7 +84,6 @@ private extension GenerateTests { func snapshot( for inputURLs: URL..., accessLevel: String? = nil, - record: Bool = false, file: StaticString = #file, testName: String = #function, line: UInt = #line @@ -87,7 +92,6 @@ private extension GenerateTests { of: try run(for: inputURLs, accessLevel: accessLevel), as: .sourceCode, named: inputURLs.first!.stem, - record: record, file: file, testName: testName, line: line diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.FormatSpecifiers.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.FormatSpecifiers.swift index 2fb1c3b..c4f4417 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.FormatSpecifiers.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.FormatSpecifiers.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct FormatSpecifiers: Sendable { + internal struct FormatSpecifiers: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -331,6 +331,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: FormatSpecifiers, rhs: FormatSpecifiers) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(formatSpecifiers: FormatSpecifiers, locale: Locale? = nil) { @@ -390,7 +404,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: formatSpecifiers.key) + key.overrideKeyForLookup(using: formatSpecifiers._key) self.init(key, tableName: formatSpecifiers.table, bundle: formatSpecifiers.bundle) } @@ -427,11 +441,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Localizable.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Localizable.swift index dbd0cf4..f253ff9 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Localizable.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Localizable.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Localizable: Sendable { + internal struct Localizable: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -192,6 +192,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Localizable, rhs: Localizable) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(localizable: Localizable, locale: Locale? = nil) { @@ -251,7 +265,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: localizable.key) + key.overrideKeyForLookup(using: localizable._key) self.init(key, tableName: localizable.table, bundle: localizable.bundle) } @@ -288,11 +302,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Multiline.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Multiline.swift index 54f366b..395f4fc 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Multiline.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Multiline.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Multiline: Sendable { + internal struct Multiline: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -138,6 +138,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Multiline, rhs: Multiline) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(multiline: Multiline, locale: Locale? = nil) { @@ -197,7 +211,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: multiline.key) + key.overrideKeyForLookup(using: multiline._key) self.init(key, tableName: multiline.table, bundle: multiline.bundle) } @@ -234,11 +248,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Positional.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Positional.swift index 56d5897..9648d3c 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Positional.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Positional.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Positional: Sendable { + internal struct Positional: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -170,6 +170,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Positional, rhs: Positional) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(positional: Positional, locale: Locale? = nil) { @@ -229,7 +243,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: positional.key) + key.overrideKeyForLookup(using: positional._key) self.init(key, tableName: positional.table, bundle: positional.bundle) } @@ -266,11 +280,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Simple.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Simple.swift index c1da4e0..0985832 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Simple.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Simple.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Simple: Sendable { + internal struct Simple: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -133,6 +133,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Simple, rhs: Simple) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(simple: Simple, locale: Locale? = nil) { @@ -192,7 +206,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: simple.key) + key.overrideKeyForLookup(using: simple._key) self.init(key, tableName: simple.table, bundle: simple.bundle) } @@ -229,11 +243,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Substitution.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Substitution.swift index 5a01c3c..4c18482 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Substitution.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Substitution.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Substitution: Sendable { + internal struct Substitution: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -137,6 +137,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Substitution, rhs: Substitution) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(substitution: Substitution, locale: Locale? = nil) { @@ -196,7 +210,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: substitution.key) + key.overrideKeyForLookup(using: substitution._key) self.init(key, tableName: substitution.table, bundle: substitution.bundle) } @@ -233,11 +247,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Variations.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Variations.swift index 33843dd..b17331c 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Variations.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerate.Variations.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Variations: Sendable { + internal struct Variations: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -148,6 +148,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Variations, rhs: Variations) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(variations: Variations, locale: Locale? = nil) { @@ -207,7 +221,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: variations.key) + key.overrideKeyForLookup(using: variations._key) self.init(key, tableName: variations.table, bundle: variations.bundle) } @@ -244,11 +258,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyFilesCombined.Legacy.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyFilesCombined.Legacy.swift index ea2da89..0f9cf21 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyFilesCombined.Legacy.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyFilesCombined.Legacy.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Legacy: Sendable { + internal struct Legacy: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -209,6 +209,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Legacy, rhs: Legacy) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(legacy: Legacy, locale: Locale? = nil) { @@ -268,7 +282,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: legacy.key) + key.overrideKeyForLookup(using: legacy._key) self.init(key, tableName: legacy.table, bundle: legacy.bundle) } @@ -305,11 +319,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStrings.Legacy.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStrings.Legacy.swift index 783eab4..1e6c08a 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStrings.Legacy.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStrings.Legacy.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Legacy: Sendable { + internal struct Legacy: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -193,6 +193,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Legacy, rhs: Legacy) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(legacy: Legacy, locale: Locale? = nil) { @@ -252,7 +266,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: legacy.key) + key.overrideKeyForLookup(using: legacy._key) self.init(key, tableName: legacy.table, bundle: legacy.bundle) } @@ -289,11 +303,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStringsdict.Legacy.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStringsdict.Legacy.swift index 93e003c..cc0dd75 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStringsdict.Legacy.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithLegacyStringsdict.Legacy.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - internal struct Legacy: Sendable { + internal struct Legacy: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -134,6 +134,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + internal func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + internal static func ==(lhs: Legacy, rhs: Legacy) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } internal init(legacy: Legacy, locale: Locale? = nil) { @@ -193,7 +207,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: legacy.key) + key.overrideKeyForLookup(using: legacy._key) self.init(key, tableName: legacy.table, bundle: legacy.bundle) } @@ -230,11 +244,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPackageAccessLevel.Localizable.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPackageAccessLevel.Localizable.swift index b90a625..3d81aa4 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPackageAccessLevel.Localizable.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPackageAccessLevel.Localizable.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - package struct Localizable: Sendable { + package struct Localizable: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -192,6 +192,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + package func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + package static func ==(lhs: Localizable, rhs: Localizable) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } package init(localizable: Localizable, locale: Locale? = nil) { @@ -251,7 +265,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: localizable.key) + key.overrideKeyForLookup(using: localizable._key) self.init(key, tableName: localizable.table, bundle: localizable.bundle) } @@ -288,11 +302,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } } diff --git a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPublicAccessLevel.Localizable.swift b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPublicAccessLevel.Localizable.swift index 66b81f4..2dbe602 100644 --- a/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPublicAccessLevel.Localizable.swift +++ b/Tests/XCStringsToolTests/__Snapshots__/GenerateTests/testGenerateWithPublicAccessLevel.Localizable.swift @@ -47,13 +47,13 @@ extension String { /// ``` /// /// - SeeAlso: [XCStrings Tool Documentation - Using the generated source code](https://swiftpackageindex.com/liamnichols/xcstrings-tool/0.5.2/documentation/documentation/using-the-generated-source-code) - public struct Localizable: Sendable { + public struct Localizable: Hashable, Sendable { #if !SWIFT_PACKAGE private class BundleLocator { } #endif - enum Argument: Sendable { + enum Argument: Hashable, Sendable { case int(Int) case uint(UInt) case float(Float) @@ -192,6 +192,20 @@ extension String { let makeDefaultValue = String.LocalizationValue.init(stringInterpolation:) return makeDefaultValue(stringInterpolation) } + + fileprivate var _key: String { + String(describing: key) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(_key) + hasher.combine(arguments) + hasher.combine(table) + } + + public static func ==(lhs: Localizable, rhs: Localizable) -> Bool { + lhs._key == rhs._key && lhs.arguments == rhs.arguments && lhs.table == rhs.table + } } public init(localizable: Localizable, locale: Locale? = nil) { @@ -251,7 +265,7 @@ extension Text { let makeKey = LocalizedStringKey.init(stringInterpolation:) var key = makeKey(stringInterpolation) - key.overrideKeyForLookup(using: localizable.key) + key.overrideKeyForLookup(using: localizable._key) self.init(key, tableName: localizable.table, bundle: localizable.bundle) } @@ -288,11 +302,11 @@ extension LocalizedStringKey { /// /// This method allows you to change the key after initialization in order /// to match the value that might be defined in the strings table. - fileprivate mutating func overrideKeyForLookup(using key: StaticString) { + fileprivate mutating func overrideKeyForLookup(using key: String) { withUnsafeMutablePointer(to: &self) { pointer in let raw = UnsafeMutableRawPointer(pointer) let bound = raw.assumingMemoryBound(to: String.self) - bound.pointee = String(describing: key) + bound.pointee = key } } }