Skip to content

Tweaks to String.safeForSwiftCode to inform caller if a change was made #645

Closed as not planned
@theoriginalbit

Description

@theoriginalbit

While merging main into my PR, #628, I noticed the changes introduced by #638 and #640 and it made me think more about the TranslatorContext and how it could provide more context (😄) to my translateVariableCase(_:) function. Specifically the if statement which needs to compare the two strings to see if the swiftSafeName function actually changed the output to determine if a raw value is required.

My thinking around this is that the String.safeForSwiftCode function already has the necessary information to determine if it made changes, without needing to perform string comparisons.

Knowing how many places use the closure on the context, I think a purely additive (opt-in) change should be utilised to allow any code that is interested in if there was a change to call an alternative closure. A very quick test I did for this looked like:

diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/SwiftSafeNames.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/SwiftSafeNames.swift
index 9a52bc0..76b2b25 100644
--- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/SwiftSafeNames.swift
+++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/SwiftSafeNames.swift
@@ -26,13 +26,14 @@ extension String {
     ///
     /// In addition to replacing illegal characters, it also
     /// ensures that the identifier starts with a letter and not a number.
-    var safeForSwiftCode: String {
-        guard !isEmpty else { return "_empty" }
+    var safeForSwiftCode: (String, requiredChanges: Bool) {
+        guard !isEmpty else { return ("_empty", requiredChanges: true) }
 
         let firstCharSet: CharacterSet = .letters.union(.init(charactersIn: "_"))
         let numbers: CharacterSet = .decimalDigits
         let otherCharSet: CharacterSet = .alphanumerics.union(.init(charactersIn: "_"))
 
+        var changed = false
         var sanitizedScalars: [Unicode.Scalar] = []
         for (index, scalar) in unicodeScalars.enumerated() {
             let allowedSet = index == 0 ? firstCharSet : otherCharSet
@@ -40,9 +41,11 @@ extension String {
             if allowedSet.contains(scalar) {
                 outScalar = scalar
             } else if index == 0 && numbers.contains(scalar) {
+                changed = true
                 sanitizedScalars.append("_")
                 outScalar = scalar
             } else {
+                changed = true
                 sanitizedScalars.append("_")
                 if let entityName = Self.specialCharsMap[scalar] {
                     for char in entityName.unicodeScalars { sanitizedScalars.append(char) }
@@ -61,10 +64,10 @@ extension String {
 
         //Special case for a single underscore.
         //We can't add it to the map as its a valid swift identifier in other cases.
-        if validString == "_" { return "_underscore_" }
+        if validString == "_" { return ("_underscore_", requiredChanges: true) }
 
-        guard Self.keywords.contains(validString) else { return validString }
-        return "_\(validString)"
+        guard Self.keywords.contains(validString) else { return (validString, requiredChanges: changed) }
+        return ("_\(validString)", requiredChanges: true)
     }
 
     /// A list of Swift keywords.
diff --git a/Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift b/Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
index 4f24652..3d69dad 100644
--- a/Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
+++ b/Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
@@ -58,4 +58,14 @@ struct TranslatorContext {
     /// - Parameter string: The string to convert to be safe for Swift.
     /// - Returns: A Swift-safe version of the input string.
     var asSwiftSafeName: (String) -> String
+
+    var swiftSafeName: (String) -> (String, requiredChanges: Bool)
+
+    init(asSwiftSafeName: @escaping (String) -> (String, requiredChanges: Bool)) {
+        swiftSafeName = asSwiftSafeName
+        self.asSwiftSafeName = {
+            let (result, _) = asSwiftSafeName($0)
+            return result
+        }
+    }
 }

It would then allow the changes in my PR to use the bool for the varied logic

         /// - Parameter name: The original name.
         /// - Returns: A declaration of an enum case.
         private func translateVariableCase(_ name: String) -> Declaration {
-            let caseName = context.asSwiftSafeName(name)
-            if caseName == name {
-                return .enumCase(name: caseName, kind: .nameOnly)
-            } else {
-                return .enumCase(name: caseName, kind: .nameWithRawValue(.string(name)))
-            }
+            let (caseName, changed) = context.swiftSafeName(name)
+            return .enumCase(name: caseName, kind: changed ? .nameWithRawValue(.string(name)) : .nameOnly)
         }

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/generatorAffects: plugin, CLI, config file.kind/enhancementImprovements to existing feature.size/SSmall task. (A couple of hours of work.)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions