@@ -4,82 +4,90 @@ import Foundation
44/// This command can be used to parse a string catalog and generate
55/// Swift code that allows other targets to access its internal keys.
66struct StringCatalogParserCommand : ParsableCommand {
7- static let configuration = CommandConfiguration (
8- commandName: " l10n-gen " ,
9- abstract: " Generate Swift code for a string catalog in any package. " ,
10- usage: """
7+ static let configuration = CommandConfiguration (
8+ commandName: " l10n-gen " ,
9+ abstract: " Generate Swift code for a string catalog in any package. " ,
10+ usage: """
1111 swift run l10n-gen --from /path/to/catalog.json --to /path/to/output.swift [--root <ROOT_NAMESPACE>]
1212 swift run l10n-gen --package /path/to/package/ --catalog package/relative/catalog/path --target package/relative/file/path [--root <ROOT_NAMESPACE>]
1313 """
14- )
14+ )
1515
16- @OptionGroup var packageOptions : PackageOptions
17- @OptionGroup var pathOptions : PathOptions
16+ @OptionGroup var packageOptions : PackageOptions
17+ @OptionGroup var pathOptions : PathOptions
1818
19- @Option var root : String = " l10n "
19+ @Option var root : String = " l10n "
2020
21- func run( ) throws {
22- print ( " \n Generating code... \n " )
23- if try packageOptions. tryExecute ( withRootNamespace: root) { return }
24- if try pathOptions. tryExecute ( withRootNamespace: root) { return }
25- fatalError ( " No matching operation. Aborting. " )
26- }
21+ func run( ) throws {
22+ print ( " \n Generating code... \n " )
23+ if try packageOptions. tryExecute ( withRootNamespace: root) { return }
24+ if try pathOptions. tryExecute ( withRootNamespace: root) { return }
25+ fatalError ( " No matching operation. Aborting. " )
26+ }
2727}
2828
2929/// These options are used when using the `--package` argument.
3030struct PackageOptions : ParsableArguments {
31- @Option ( name: . long, help: " A command-relative path to a Swift Package. " )
32- var package : String ?
31+ @Option ( name: . long, help: " A command-relative path to a Swift Package. " )
32+ var package : String ?
3333
34- @Option ( name: . long, help: " A package-relative path to the string catalog. " )
35- var catalog : String ?
34+ @Option ( name: . long, help: " A package-relative path to the string catalog. " )
35+ var catalog : String ?
3636
37- @Option ( name: . long, help: " A package-relative path to the target output file. " )
38- var target : String ?
37+ @Option ( name: . long, help: " A package-relative path to the target output file. " )
38+ var target : String ?
3939
40- func tryExecute(
41- withRootNamespace root: String
42- ) throws -> Bool {
43- guard let package , let catalog, let target else { return false }
44- let catalogPath = ( package + catalog) . cleanPath ( )
45- let filePath = ( package + target) . cleanPath ( )
46- try generateCode ( from: catalogPath, to: filePath, withRootNamespace: root)
47- return true
48- }
40+ func tryExecute(
41+ withRootNamespace root: String
42+ ) throws -> Bool {
43+ guard let package , let catalog, let target else { return false }
44+ let catalogPath = ( package + catalog) . cleanPath ( )
45+ let filePath = ( package + target) . cleanPath ( )
46+ try generateCode ( from: catalogPath, to: filePath, withRootNamespace: root)
47+ return true
48+ }
4949}
5050
5151/// These options are used when using the `--from` and `--to` arguments.
5252struct PathOptions : ParsableArguments {
53- @Option ( name: . long, help: " A command-relative path to a source string catalog. " )
54- var from : String ?
53+ @Option ( name: . long, help: " A command-relative path to a source string catalog. " )
54+ var from : String ?
5555
56- @Option ( name: . long, help: " A command-relative path to a target output file. " )
57- var to : String ?
56+ @Option ( name: . long, help: " A command-relative path to a target output file. " )
57+ var to : String ?
5858
59- func tryExecute(
60- withRootNamespace root: String
61- ) throws -> Bool {
62- guard let from, let to else { return false }
63- try generateCode ( from: from, to: to, withRootNamespace: root)
64- return true
65- }
59+ func tryExecute(
60+ withRootNamespace root: String
61+ ) throws -> Bool {
62+ guard let from, let to else { return false }
63+ try generateCode ( from: from, to: to, withRootNamespace: root)
64+ return true
65+ }
6666}
6767
6868extension ParsableArguments {
69- func generateCode(
70- from catalogPath: String ,
71- to filePath: String ,
72- withRootNamespace root: String
73- ) throws {
74- print ( " Generating code from \" \( catalogPath) \" to \" \( filePath) \" ... \n " )
75- let stringCatalog = try StringCatalog ( path: catalogPath)
76- let code = stringCatalog. generatePublicKeyWrappers ( withRootNamespace: root)
77- try code. write ( toFile: filePath, atomically: true , encoding: . utf8)
78- }
69+ func generateCode(
70+ from catalogPath: String ,
71+ to filePath: String ,
72+ withRootNamespace root: String
73+ ) throws {
74+ // Generate wrappers
75+ print ( " Generating code from \" \( catalogPath) \" to \" \( filePath) \" ... \n " )
76+ let stringCatalog = try StringCatalog ( path: catalogPath)
77+ let code = stringCatalog. generatePublicKeyWrappers ( withRootNamespace: root)
78+
79+ // Create parent directory if it doesn't exist
80+ let url = URL ( fileURLWithPath: filePath)
81+ let directory = url. deletingLastPathComponent ( )
82+ try FileManager . default. createDirectory ( at: directory, withIntermediateDirectories: true )
83+
84+ // Write code to file.
85+ try code. write ( toFile: filePath, atomically: true , encoding: . utf8)
86+ }
7987}
8088
8189private extension String {
82- func cleanPath( ) -> String {
83- replacingOccurrences ( of: " // " , with: " / " )
84- }
90+ func cleanPath( ) -> String {
91+ replacingOccurrences ( of: " // " , with: " / " )
92+ }
8593}
0 commit comments