Skip to content

Commit

Permalink
Merge pull request #16 from superarts/task/testability-case-name
Browse files Browse the repository at this point in the history
#2 #3 Addressed PR comments of #15
  • Loading branch information
superarts authored Jan 1, 2024
2 parents 0539c9c + 0c153c0 commit 1566c3f
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 155 deletions.
4 changes: 2 additions & 2 deletions StringCatalogEnum/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ NAME=xcstrings-enum-generate
FILENAME_USAGE=USAGE.md

# TODO: get from ENV
EXPORT_PROJECT_ROOT=~/prj/business/quible/quible-ios
EXPORT_PROJECT_ROOT=~/prj/quible/quible-ios
# TODO: remove other hard-coded paths

all: debug
Expand Down Expand Up @@ -34,4 +34,4 @@ doc:
./$(NAME) --help >> $(FILENAME_USAGE)
echo '```' >> $(FILENAME_USAGE)

publish: release doc
publish: release doc
4 changes: 2 additions & 2 deletions StringCatalogEnum/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git",
"state": {
"branch": null,
"revision": "a23ded2c91df9156628a6996ab4f347526f17b6b",
"version": "2.1.2"
"revision": "dc9af4781f2afdd1e68e90f80b8603be73ea7abc",
"version": "2.2.0"
}
},
{
Expand Down
54 changes: 31 additions & 23 deletions StringCatalogEnum/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,42 @@ import PackageDescription

// TODO: clean up this local CLI package dependency
/*
let packageCLIGit = Package.Dependency.package(
name: "StringCatalogEnum", // <- Not sure why this is needed, help?
url: "https://github.com/superarts/swift-cli-core",
.revision("70d7df4e862be86799e9d514e5e55ca92585e7f8")
)
let packageCLILocal = Package.Dependency.package(
name: "StringCatalogEnum", // <- Not sure why this is needed, help?
path: "../../"
)
let packageCLI = packageCLILocal
//let packageCLI = packageCLIGit
*/
let packageCLIGit = Package.Dependency.package(
name: "StringCatalogEnum", // <- Not sure why this is needed, help?
url: "https://github.com/superarts/swift-cli-core",
.revision("70d7df4e862be86799e9d514e5e55ca92585e7f8")
)
let packageCLILocal = Package.Dependency.package(
name: "StringCatalogEnum", // <- Not sure why this is needed, help?
path: "../../"
)
let packageCLI = packageCLILocal
//let packageCLI = packageCLIGit
*/

let package = Package(
name: "StringCatalogEnum",
platforms: [
.macOS(.v10_12),
],
products: [
.executable(name: "xcstrings-enum-generate", targets: ["StringCatalogEnum"])
.executable(name: "xcstrings-enum-generate", targets: ["StringCatalogEnum"]),
.library(name: "StringCatalogEnumLibrary", targets: ["StringCatalogEnumLibrary"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(
url: "https://github.com/apple/swift-argument-parser",
url: "https://github.com/apple/swift-argument-parser",
.upToNextMinor(from: "1.2.3")
),
// packageCLI,
.package(
url: "https://github.com/Quick/Quick.git",
url: "https://github.com/Quick/Quick.git",
.upToNextMinor(from: "7.3.0")
),
.package(
url: "https://github.com/Quick/Nimble.git",
url: "https://github.com/Quick/Nimble.git",
.upToNextMinor(from: "13.0.0")
),
],
Expand All @@ -49,21 +50,28 @@ let package = Package(
.target(
name: "StringCatalogEnum",
dependencies: [
"StringCatalogEnumLibrary",
.product(
name: "ArgumentParser",
package: "swift-argument-parser"
),
/*
.product(
name: "packageCLI",
package: "packageCLI"
),
*/
.product(
name: "packageCLI",
package: "packageCLI"
),
*/
]
),
.target(
name: "StringCatalogEnumLibrary",
dependencies: [
//
]
),
.testTarget(
name: "StringCatalogEnumTests",
dependencies: ["StringCatalogEnum", "Quick", "Nimble"]
dependencies: ["StringCatalogEnumLibrary", "StringCatalogEnum", "Quick", "Nimble"]
),
]
)
)
149 changes: 30 additions & 119 deletions StringCatalogEnum/Sources/StringCatalogEnum/main.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ArgumentParser
import Foundation
import StringCatalogEnumLibrary

struct StringCatalogEnum: ParsableCommand {
enum Error: Swift.Error {
Expand Down Expand Up @@ -29,6 +30,7 @@ struct StringCatalogEnum: ParsableCommand {
var enumTypealias: String = "XCS"

func run() throws {
let helper = StringEnumHelper()
print("LOADING: \(xcstringsPath)")
let url = URL(fileURLWithPath: xcstringsPath)
let data = try Data(contentsOf: url)
Expand All @@ -37,143 +39,52 @@ struct StringCatalogEnum: ParsableCommand {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
throw Error.unexpectedJSON(message: "cannot parse first level object")
}

guard let strings = json["strings"] as? [String: Any] else {
throw Error.unexpectedJSON(message: "cannot parse `strings`")
}

var output = """
// This file is generated by XcodeStringEnum. Please do *NOT* update it manually.
// As a common practice, swiftLint is disabled for generated files.
// swiftlint:disable all
import SwiftUI
/// Makes it a bit easier to type.
typealias \(enumTypealias) = \(enumName)
/// Generated by StringCatalogEnum, this enum contains all existing Strin Category keys.
enum \(enumName): String, CaseIterable {
"""

var cases = [String]()
var knownCases = [String]()
for (key, _) in strings {
guard let name = convertToVariableName(key: key) else {
print("SKIPPING: \(key)")
continue
}
guard key == name else {
continue
}
guard !knownCases.contains(name) else {
cases.append(" // TODO: fix duplicated entry - case \(name)\n")
continue
}
knownCases.append(name)

// print("\(name):\t\(key)")
// TODO: extract `localizations.en.stringUnit.value` and add in comments as inline documents
if Keyword.allCases.map({ $0.rawValue }).contains(name) {
cases.append(" case `\(name)`\n")
} else {
cases.append(" case \(name)\n")
}
}
cases.sort()
cases.forEach { string in
output += string
}
// This file is generated by XcodeStringEnum. Please do *NOT* update it manually.
// As a common practice, swiftLint is disabled for generated files.
// swiftlint:disable all
output += """
import SwiftUI
// MARK: - The following cases should be manually replaced in your codebase.
"""
cases.removeAll()
for (key, _) in strings {
guard let name = convertToVariableName(key: key) else {
print("SKIPPING: \(key)")
continue
}
guard key != name else {
continue
}
guard !knownCases.contains(name) else {
cases.append(" // TODO: fix duplicated entry - case \(name)\n")
continue
}
knownCases.append(name)

// print("\(name):\t\(key)")
// TODO: probably missing " handling?
if Keyword.allCases.map({ $0.rawValue }).contains(name) {
cases.append(" case `\(name)` = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n")
} else {
cases.append(" case \(name) = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n")
}
}
// cases = Array(Set<String>(cases))
cases.sort()
cases.forEach { string in
output += string
}
/// Makes it a bit easier to type.
typealias \(enumTypealias) = \(enumName)
/// Generated by StringCatalogEnum, this enum contains all existing Strin Category keys.
enum \(enumName): String, CaseIterable {
"""
let keywordRawValues = getKeywordRawValues()
let firstCases = helper.createEnumKeys(with: strings, keyNameMatches: true, keywordEnum: keywordRawValues)
let secondCases = helper.createEnumKeys(with: strings, keyNameMatches: false, keywordEnum: keywordRawValues)

output += """
\(firstCases)
// MARK: - The following cases should be manually replaced in your codebase.
\(secondCases)
/// Usage: `SwiftUI.Text(\(enumTypealias).yourStringCatalogKey.key)`
var key: LocalizedStringKey { LocalizedStringKey(rawValue) }
/// Usage: `SwiftUI.Text(\(enumTypealias).yourStringCatalogKey.key)`
var key: LocalizedStringKey { LocalizedStringKey(rawValue) }
var string: String { NSLocalizedString(self.rawValue, comment: "Generated localization from String Catalog key: \\(key)") }
var string: String { NSLocalizedString(self.rawValue, comment: "Generated localization from String Catalog key: \\(key)") }
// var text: String.LocalizationValue { String.LocalizationValue(rawValue) }
}
// swiftlint:enable all
"""

// var text: String.LocalizationValue { String.LocalizationValue(rawValue) }
}
// swiftlint:enable all
"""
print(output)
let outputURL = URL(fileURLWithPath: outputFilename)
try output.write(to: outputURL, atomically: true, encoding: .utf8)
print("Written to: \(outputFilename)")
}

/// Convert a Strint Catalog key to a Swift variable name.
private func convertToVariableName(key: String) -> String? {
// Leave only letters and numeric characters
var result = key.components(separatedBy: CharacterSet.letters.union(CharacterSet.alphanumerics).inverted).joined()

// Remove leading numeric characters
while !result.isEmpty {
let firstLetter = result.prefix(1)
let digitsCharacters = CharacterSet(charactersIn: "0123456789")
if CharacterSet(charactersIn: String(firstLetter)).isSubset(of: digitsCharacters) {
// print("dropping first of: \(result)")
result = String(result.dropFirst())
} else {
break
}
}

// Return nil if empty
guard !result.isEmpty else {
return nil
}

// Return lowercased string if there's only 1 character
guard result.count > 1 else {
return result.lowercased()
}

// Change the first character to lowercase
let firstLetter = result.prefix(1).lowercased()
let remainingLetters = result.dropFirst()
result = firstLetter + remainingLetters

// TODO: uppercase remaining words, e.g. "an example" to "anExample"; currently it's "anexample"
// TODO: lowercase capitalized words, e.g. "EXAMPLE" to "example"; currently it's "eXAMPLE"

return result
func getKeywordRawValues() -> [String] {
Keyword.allCases.map(\.rawValue)
}
}

Expand Down
12 changes: 6 additions & 6 deletions StringCatalogEnum/Sources/StringCatalogEnum/model.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// TODO: make XCStrings Decodable by listing all possible models
// Example: let obj = try JSONDecoder().decode(XCStrings.self, from: data)
/*
struct XCStrings: Decodable {
let sourceLanguage: String
let version: String
let strings: [String: String]
}
*/
struct XCStrings: Decodable {
let sourceLanguage: String
let version: String
let strings: [Strsing: String]
}
*/
Loading

0 comments on commit 1566c3f

Please sign in to comment.