From 264c2216046ce7bc442578de8a78dc5be91fcfae Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Tue, 30 Aug 2022 17:02:11 +0200 Subject: [PATCH 01/10] Refactor in one unique command with subcommand + add a new command to generate ressources from a configuration file --- .../xcschemes/ResgenSwift-Package.xcscheme | 14 ++ .../xcschemes/ResgenSwift.xcscheme | 28 ++++ Package.resolved | 9 ++ Package.swift | 79 +++++----- README.md | 4 +- Sources/ColorTool/ColorToolError.swift | 35 ----- Sources/Imagium/ImagiumError.swift | 43 ------ .../Colors/Colors.swift} | 16 +- .../ResgenSwift/Colors/ColorsToolError.swift | 35 +++++ .../Colors/ColorsToolOptions.swift} | 8 +- .../Generator/ColorExtensionGenerator.swift | 6 +- .../Generator/ColorXcassetHelper.swift | 4 +- .../Colors}/Model/ColorStyle.swift | 2 +- .../Colors}/Model/ParsedColor.swift | 4 +- .../Colors}/Parser/ColorFileParser.swift | 8 +- .../Fonts}/FontOptions.swift | 23 ++- .../Fonts/Fonts.swift} | 16 +- .../Fonts/FontsToolError.swift} | 12 +- .../Fonts/FontsToolHelper.swift} | 12 +- .../Fonts}/Generator/FontPlistGenerator.swift | 2 +- .../Generator/FontToolContentGenerator.swift | 14 +- .../Fonts}/Model/FontName.swift | 2 +- .../Fonts}/Parser/FontFileParser.swift | 2 +- Sources/ResgenSwift/Generate/Generate.swift | 51 +++++++ .../ResgenSwift/Generate/GenerateError.swift | 30 ++++ .../Generate/GenerateOptions.swift | 19 +++ .../Generate/Model/ConfigurationFile.swift | 140 ++++++++++++++++++ .../Parser/ConfigurationFileParser.swift | 27 ++++ ...ColorsConfiguration+ShellCommandable.swift | 43 ++++++ .../FontsConfiguration+ShellCommandable.swift | 40 +++++ ...ImagesConfiguration+ShellCommandable.swift | 41 +++++ .../Generate/ShellCommandable/Runnable.swift | 13 ++ ...tringsConfiguration+ShellCommandable.swift | 46 ++++++ .../TagsConfiguration+ShellCommandable.swift | 41 +++++ .../Extensions/FileManagerExtensions.swift | 16 +- .../Generator/ImageExtensionGenerator.swift | 4 +- .../Images}/Generator/XcassetsGenerator.swift | 8 +- .../Images/Images.swift} | 20 ++- Sources/ResgenSwift/Images/ImagesError.swift | 43 ++++++ .../Images/ImagesOptions.swift} | 4 +- .../Images}/Model/ConvertArgument.swift | 0 .../Images}/Model/ParsedImage.swift | 0 .../Images}/Model/PlatormTag.swift | 0 .../Images}/Parser/ImageFileParser.swift | 0 .../Generator/StringsFileGenerator.swift | 0 .../Strings/Generator/TagsGenerator.swift | 28 ++-- .../Strings/Model/Definition.swift | 0 .../Strings/Model/Section.swift | 0 .../Strings/Parser/TwineFileParser.swift | 0 .../Strings/Stringium/Stringium.swift | 6 +- .../Strings/Stringium/StringiumError.swift | 0 .../Strings/Stringium/StringiumOptions.swift | 2 +- .../Strings/Strings.swift} | 2 +- .../{ => ResgenSwift}/Strings/Tag/Tags.swift | 6 +- .../Strings/Tag/TagsOptions.swift | 2 +- .../Strings/Twine/Twine.swift | 2 - .../Strings/Twine/TwineError.swift | 0 .../Strings/Twine/TwineOptions.swift | 2 +- Sources/ResgenSwift/main.swift | 36 ++++- Sources/ToolCore/Shell.swift | 10 +- 60 files changed, 825 insertions(+), 235 deletions(-) delete mode 100644 Sources/ColorTool/ColorToolError.swift delete mode 100644 Sources/Imagium/ImagiumError.swift rename Sources/{ColorTool/main.swift => ResgenSwift/Colors/Colors.swift} (91%) create mode 100644 Sources/ResgenSwift/Colors/ColorsToolError.swift rename Sources/{ColorTool/ColorToolOptions.swift => ResgenSwift/Colors/ColorsToolOptions.swift} (88%) rename Sources/{ColorTool => ResgenSwift/Colors}/Generator/ColorExtensionGenerator.swift (88%) rename Sources/{ColorTool => ResgenSwift/Colors}/Generator/ColorXcassetHelper.swift (90%) rename Sources/{ColorTool => ResgenSwift/Colors}/Model/ColorStyle.swift (87%) rename Sources/{ColorTool => ResgenSwift/Colors}/Model/ParsedColor.swift (95%) rename Sources/{ColorTool => ResgenSwift/Colors}/Parser/ColorFileParser.swift (87%) rename Sources/{FontTool => ResgenSwift/Fonts}/FontOptions.swift (59%) rename Sources/{FontTool/main.swift => ResgenSwift/Fonts/Fonts.swift} (89%) rename Sources/{FontTool/FontToolError.swift => ResgenSwift/Fonts/FontsToolError.swift} (51%) rename Sources/{FontTool/FontToolHelper.swift => ResgenSwift/Fonts/FontsToolHelper.swift} (87%) rename Sources/{FontTool => ResgenSwift/Fonts}/Generator/FontPlistGenerator.swift (94%) rename Sources/{FontTool => ResgenSwift/Fonts}/Generator/FontToolContentGenerator.swift (82%) rename Sources/{FontTool => ResgenSwift/Fonts}/Model/FontName.swift (97%) rename Sources/{FontTool => ResgenSwift/Fonts}/Parser/FontFileParser.swift (93%) create mode 100644 Sources/ResgenSwift/Generate/Generate.swift create mode 100644 Sources/ResgenSwift/Generate/GenerateError.swift create mode 100644 Sources/ResgenSwift/Generate/GenerateOptions.swift create mode 100644 Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift create mode 100644 Sources/ResgenSwift/Generate/Parser/ConfigurationFileParser.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift create mode 100644 Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift rename Sources/{Imagium => ResgenSwift/Images}/Extensions/FileManagerExtensions.swift (74%) rename Sources/{Imagium => ResgenSwift/Images}/Generator/ImageExtensionGenerator.swift (95%) rename Sources/{Imagium => ResgenSwift/Images}/Generator/XcassetsGenerator.swift (97%) rename Sources/{Imagium/main.swift => ResgenSwift/Images/Images.swift} (89%) create mode 100644 Sources/ResgenSwift/Images/ImagesError.swift rename Sources/{Imagium/ImagiumOptions.swift => ResgenSwift/Images/ImagesOptions.swift} (92%) rename Sources/{Imagium => ResgenSwift/Images}/Model/ConvertArgument.swift (100%) rename Sources/{Imagium => ResgenSwift/Images}/Model/ParsedImage.swift (100%) rename Sources/{Imagium => ResgenSwift/Images}/Model/PlatormTag.swift (100%) rename Sources/{Imagium => ResgenSwift/Images}/Parser/ImageFileParser.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Generator/StringsFileGenerator.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Generator/TagsGenerator.swift (82%) rename Sources/{ => ResgenSwift}/Strings/Model/Definition.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Model/Section.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Parser/TwineFileParser.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Stringium/Stringium.swift (93%) rename Sources/{ => ResgenSwift}/Strings/Stringium/StringiumError.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Stringium/StringiumOptions.swift (98%) rename Sources/{Strings/main.swift => ResgenSwift/Strings/Strings.swift} (97%) rename Sources/{ => ResgenSwift}/Strings/Tag/Tags.swift (90%) rename Sources/{ => ResgenSwift}/Strings/Tag/TagsOptions.swift (96%) rename Sources/{ => ResgenSwift}/Strings/Twine/Twine.swift (96%) rename Sources/{ => ResgenSwift}/Strings/Twine/TwineError.swift (100%) rename Sources/{ => ResgenSwift}/Strings/Twine/TwineOptions.swift (97%) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme index 49b0f71..be2a28d 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme @@ -160,6 +160,20 @@ ReferencedContainer = "container:"> + + + + + + + + + + + + ⚠️ If extension name is not set or is `Tags`, it will generate static property on `Tags`. This class may not exists in your project, just create an empty class named `Tags` is necessary. +> ⚠️ If extension name is not set or is `Tags`, it will generate static property on `Tags`. `Tags` is a typealias of `String`. # Images @@ -148,4 +148,4 @@ swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \ # TODO -[ ] Allow static variable generation on custom extension \ No newline at end of file +[ ] Allow static variable generation on custom extension diff --git a/Sources/ColorTool/ColorToolError.swift b/Sources/ColorTool/ColorToolError.swift deleted file mode 100644 index 7b90073..0000000 --- a/Sources/ColorTool/ColorToolError.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// ColorToolError.swift -// -// -// Created by Thibaut Schmitt on 20/12/2021. -// - -import Foundation - -enum ColorToolError: Error { - case badFormat(String) - case writeAsset(String) - case writeExtension(String, String) - case fileNotExists(String) - case badColorDefinition(String, String) - - var description: String { - switch self { - case .badFormat(let info): - return "error:[ColorTool] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\"" - - case .writeAsset(let info): - return "error:[ColorTool] An error occured while writing color in Xcasset: \(info)" - - case .writeExtension(let filename, let info): - return "error:[ColorTool] An error occured while writing extension in \(filename): \(info)" - - case .fileNotExists(let filename): - return "error:[ColorTool] File \(filename) does not exists" - - case .badColorDefinition(let lightColor, let darkColor): - return "error:[ColorTool]One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-" - } - } -} diff --git a/Sources/Imagium/ImagiumError.swift b/Sources/Imagium/ImagiumError.swift deleted file mode 100644 index baa7cc4..0000000 --- a/Sources/Imagium/ImagiumError.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// ImagiumError.swift -// -// -// Created by Thibaut Schmitt on 24/01/2022. -// - -import Foundation - -enum ImagiumError: Error { - case inputFolderNotFound(String) - case fileNotExists(String) - case unknownImageExtension(String) - case getFileAttributed(String, String) - case rsvgConvertNotFound - case writeFile(String, String) - case unknown(String) - - var localizedDescription: String { - switch self { - case .inputFolderNotFound(let inputFolder): - return " error:[\(Imagium.toolName)] Input folder not found: \(inputFolder)" - - case .fileNotExists(let filename): - return " error:[\(Imagium.toolName)] File \(filename) does not exists" - - case .unknownImageExtension(let filename): - return " error:[\(Imagium.toolName)] File \(filename) have an unhandled file extension. Cannot generate image." - - case .getFileAttributed(let filename, let errorDescription): - return " error:[\(Imagium.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)" - - case .rsvgConvertNotFound: - return " error:[\(Imagium.toolName)] Can't find rsvg-convert (can be installed with 'brew remove imagemagick && brew install imagemagick --with-librsvg')" - - case .writeFile(let subErrorDescription, let filename): - return " error:[\(Imagium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)" - - case .unknown(let errorDescription): - return " error:[\(Imagium.toolName)] Unknown error: \(errorDescription)" - } - } -} diff --git a/Sources/ColorTool/main.swift b/Sources/ResgenSwift/Colors/Colors.swift similarity index 91% rename from Sources/ColorTool/main.swift rename to Sources/ResgenSwift/Colors/Colors.swift index 0c82146..d905bad 100644 --- a/Sources/ColorTool/main.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -9,7 +9,7 @@ import ToolCore import Foundation import ArgumentParser -struct ColorTool: ParsableCommand { +struct Colors: ParsableCommand { // MARK: - CommandConfiguration @@ -20,7 +20,7 @@ struct ColorTool: ParsableCommand { // MARK: - Static - static let toolName = "ColorTool" + static let toolName = "Color" static let defaultExtensionName = "UIColor" static let assetsColorsFolderName = "Colors" @@ -39,7 +39,7 @@ struct ColorTool: ParsableCommand { // MARK: - Command options - @OptionGroup var options: ColorToolOptions + @OptionGroup var options: ColorsToolOptions // MARK: - Run @@ -79,16 +79,16 @@ struct ColorTool: ParsableCommand { // Check if input file exists guard fileManager.fileExists(atPath: options.inputFile) else { - let error = ColorToolError.fileNotExists(options.inputFile) + let error = ColorsToolError.fileNotExists(options.inputFile) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } // Check if xcassets file exists guard fileManager.fileExists(atPath: options.xcassetsPath) else { - let error = ColorToolError.fileNotExists(options.xcassetsPath) + let error = ColorsToolError.fileNotExists(options.xcassetsPath) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } // Check if needed to regenerate @@ -107,8 +107,6 @@ struct ColorTool: ParsableCommand { } } -ColorTool.main() - /* Command samples: diff --git a/Sources/ResgenSwift/Colors/ColorsToolError.swift b/Sources/ResgenSwift/Colors/ColorsToolError.swift new file mode 100644 index 0000000..7bfc860 --- /dev/null +++ b/Sources/ResgenSwift/Colors/ColorsToolError.swift @@ -0,0 +1,35 @@ +// +// ColorsToolError.swift +// +// +// Created by Thibaut Schmitt on 20/12/2021. +// + +import Foundation + +enum ColorsToolError: Error { + case badFormat(String) + case writeAsset(String) + case writeExtension(String, String) + case fileNotExists(String) + case badColorDefinition(String, String) + + var description: String { + switch self { + case .badFormat(let info): + return "error:[\(Colors.toolName)] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\"" + + case .writeAsset(let info): + return "error:[\(Colors.toolName)] An error occured while writing color in Xcasset: \(info)" + + case .writeExtension(let filename, let info): + return "error:[\(Colors.toolName)] An error occured while writing extension in \(filename): \(info)" + + case .fileNotExists(let filename): + return "error:[\(Colors.toolName)] File \(filename) does not exists" + + case .badColorDefinition(let lightColor, let darkColor): + return "error:[\(Colors.toolName)]One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-" + } + } +} diff --git a/Sources/ColorTool/ColorToolOptions.swift b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift similarity index 88% rename from Sources/ColorTool/ColorToolOptions.swift rename to Sources/ResgenSwift/Colors/ColorsToolOptions.swift index e0c6ee3..a9aea80 100644 --- a/Sources/ColorTool/ColorToolOptions.swift +++ b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift @@ -1,5 +1,5 @@ // -// ColorToolOptions.swift +// ColorsToolOptions.swift // // // Created by Thibaut Schmitt on 17/01/2022. @@ -8,7 +8,7 @@ import Foundation import ArgumentParser -struct ColorToolOptions: ParsableArguments { +struct ColorsToolOptions: ParsableArguments { @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") var forceGeneration = false @@ -25,13 +25,13 @@ struct ColorToolOptions: ParsableArguments { var extensionOutputPath: String @Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.") - var extensionName: String = ColorTool.defaultExtensionName + var extensionName: String = Colors.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift") var extensionSuffix: String = "" } -extension ColorToolOptions { +extension ColorsToolOptions { var colorStyle: ColorStyle { ColorStyle(rawValue: style) ?? .all } diff --git a/Sources/ColorTool/Generator/ColorExtensionGenerator.swift b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift similarity index 88% rename from Sources/ColorTool/Generator/ColorExtensionGenerator.swift rename to Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift index 9847900..9e0d17b 100644 --- a/Sources/ColorTool/Generator/ColorExtensionGenerator.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift @@ -33,15 +33,15 @@ struct ColorExtensionGenerator { do { try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) } catch (let error) { - let error = ColorToolError.writeExtension(extensionFilePath, error.localizedDescription) + let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } } private static func getHeader(extensionClassname: String) -> String { """ - // Generated by ResgenSwift.\(ColorTool.toolName) \(ResgenSwiftVersion) + // Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion) import UIKit diff --git a/Sources/ColorTool/Generator/ColorXcassetHelper.swift b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift similarity index 90% rename from Sources/ColorTool/Generator/ColorXcassetHelper.swift rename to Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift index 1223e5e..1ec0dad 100644 --- a/Sources/ColorTool/Generator/ColorXcassetHelper.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift @@ -31,9 +31,9 @@ struct ColorXcassetHelper { do { try color.contentsJSON().write(to: contentsJsonPathURL, atomically: true, encoding: .utf8) } catch (let error) { - let error = ColorToolError.writeAsset(error.localizedDescription) + let error = ColorsToolError.writeAsset(error.localizedDescription) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } } } diff --git a/Sources/ColorTool/Model/ColorStyle.swift b/Sources/ResgenSwift/Colors/Model/ColorStyle.swift similarity index 87% rename from Sources/ColorTool/Model/ColorStyle.swift rename to Sources/ResgenSwift/Colors/Model/ColorStyle.swift index aae3d97..8e1521a 100644 --- a/Sources/ColorTool/Model/ColorStyle.swift +++ b/Sources/ResgenSwift/Colors/Model/ColorStyle.swift @@ -1,5 +1,5 @@ // -// File.swift +// ColorStyle.swift // // // Created by Thibaut Schmitt on 29/08/2022. diff --git a/Sources/ColorTool/Model/ParsedColor.swift b/Sources/ResgenSwift/Colors/Model/ParsedColor.swift similarity index 95% rename from Sources/ColorTool/Model/ParsedColor.swift rename to Sources/ResgenSwift/Colors/Model/ParsedColor.swift index 458f337..2bfe56c 100644 --- a/Sources/ColorTool/Model/ParsedColor.swift +++ b/Sources/ResgenSwift/Colors/Model/ParsedColor.swift @@ -25,9 +25,9 @@ struct ParsedColor { } guard allComponents.contains(true) == false else { - let error = ColorToolError.badColorDefinition(light, dark) + let error = ColorsToolError.badColorDefinition(light, dark) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } return """ diff --git a/Sources/ColorTool/Parser/ColorFileParser.swift b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift similarity index 87% rename from Sources/ColorTool/Parser/ColorFileParser.swift rename to Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift index 64f5a26..a96b4e7 100644 --- a/Sources/ColorTool/Parser/ColorFileParser.swift +++ b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift @@ -1,5 +1,5 @@ // -// File.swift +// ColorFileParser.swift // // // Created by Thibaut Schmitt on 29/08/2022. @@ -22,16 +22,16 @@ class ColorFileParser { .replacingOccurrences(of: "=", with: "") // Keep compat with current file format guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { - print("[\(ColorTool.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") + print("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") return nil } let colorContent = colorLineCleanedUp.split(separator: " ") guard colorContent.count >= 2 else { - let error = ColorToolError.badFormat(colorLine) + let error = ColorsToolError.badFormat(colorLine) print(error.localizedDescription) - ColorTool.exit(withError: error) + Colors.exit(withError: error) } switch colorStyle { diff --git a/Sources/FontTool/FontOptions.swift b/Sources/ResgenSwift/Fonts/FontOptions.swift similarity index 59% rename from Sources/FontTool/FontOptions.swift rename to Sources/ResgenSwift/Fonts/FontOptions.swift index 5a6bf7c..cec71a4 100644 --- a/Sources/FontTool/FontOptions.swift +++ b/Sources/ResgenSwift/Fonts/FontOptions.swift @@ -1,5 +1,5 @@ // -// FontOptions.swift +// FontsOptions.swift // // // Created by Thibaut Schmitt on 17/01/2022. @@ -8,7 +8,7 @@ import Foundation import ArgumentParser -struct FontOptions: ParsableArguments { +struct FontsOptions: ParsableArguments { @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") var forceGeneration = false @@ -19,8 +19,25 @@ struct FontOptions: ParsableArguments { var extensionOutputPath: String @Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.") - var extensionName: String = FontTool.defaultExtensionName + var extensionName: String = Fonts.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift") var extensionSuffix: String = "" } + +extension FontsOptions { + var extensionFileName: String { + if extensionSuffix.isEmpty == false { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } + + var generateStaticVariable: Bool { + extensionName == Fonts.defaultExtensionName + } +} diff --git a/Sources/FontTool/main.swift b/Sources/ResgenSwift/Fonts/Fonts.swift similarity index 89% rename from Sources/FontTool/main.swift rename to Sources/ResgenSwift/Fonts/Fonts.swift index bd2a3dd..a010cb3 100644 --- a/Sources/FontTool/main.swift +++ b/Sources/ResgenSwift/Fonts/Fonts.swift @@ -1,5 +1,5 @@ // -// FontTool.swift +// Fonts.swift // // // Created by Thibaut Schmitt on 13/12/2021. @@ -9,7 +9,7 @@ import ToolCore import Foundation import ArgumentParser -struct FontTool: ParsableCommand { +struct Fonts: ParsableCommand { // MARK: - CommandConfiguration @@ -20,7 +20,7 @@ struct FontTool: ParsableCommand { // MARK: - Static - static let toolName = "FontTool" + static let toolName = "Fonts" static let defaultExtensionName = "UIFont" // MARK: - Properties @@ -38,7 +38,7 @@ struct FontTool: ParsableCommand { // MARK: - Command Options - @OptionGroup var options: FontOptions + @OptionGroup var options: FontsOptions // MARK: - Run @@ -55,7 +55,7 @@ struct FontTool: ParsableCommand { // Get real font names let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath - let fontsNames = FontToolHelper.getFontPostScriptName(for: fontsToGenerate, + let fontsNames = FontsToolHelper.getFontPostScriptName(for: fontsToGenerate, inputFolder: inputFolder) // Generate extension @@ -77,9 +77,9 @@ struct FontTool: ParsableCommand { // Check input file exists guard fileManager.fileExists(atPath: options.inputFile) else { - let error = FontToolError.fileNotExists(options.inputFile) + let error = FontsToolError.fileNotExists(options.inputFile) print(error.localizedDescription) - FontTool.exit(withError: error) + Fonts.exit(withError: error) } // Check if needed to regenerate @@ -91,5 +91,3 @@ struct FontTool: ParsableCommand { return true } } - -FontTool.main() diff --git a/Sources/FontTool/FontToolError.swift b/Sources/ResgenSwift/Fonts/FontsToolError.swift similarity index 51% rename from Sources/FontTool/FontToolError.swift rename to Sources/ResgenSwift/Fonts/FontsToolError.swift index 8c68069..e979ca6 100644 --- a/Sources/FontTool/FontToolError.swift +++ b/Sources/ResgenSwift/Fonts/FontsToolError.swift @@ -1,5 +1,5 @@ // -// FontToolError.swift +// FontsToolError.swift // // // Created by Thibaut Schmitt on 13/12/2021. @@ -7,7 +7,7 @@ import Foundation -enum FontToolError: Error { +enum FontsToolError: Error { case fcScan(String, Int32, String?) case inputFolderNotFound(String) case fileNotExists(String) @@ -16,16 +16,16 @@ enum FontToolError: Error { var localizedDescription: String { switch self { case .fcScan(let path, let code, let output): - return "error:[\(FontTool.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")" + return "error:[\(Fonts.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")" case .inputFolderNotFound(let inputFolder): - return " error:[\(FontTool.toolName)] Input folder not found: \(inputFolder)" + return " error:[\(Fonts.toolName)] Input folder not found: \(inputFolder)" case .fileNotExists(let filename): - return " error:[\(FontTool.toolName)] File \(filename) does not exists" + return " error:[\(Fonts.toolName)] File \(filename) does not exists" case .writeExtension(let filename, let info): - return "error:[\(FontTool.toolName)] An error occured while writing extension in \(filename): \(info)" + return "error:[\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)" } } } diff --git a/Sources/FontTool/FontToolHelper.swift b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift similarity index 87% rename from Sources/FontTool/FontToolHelper.swift rename to Sources/ResgenSwift/Fonts/FontsToolHelper.swift index 22123e8..3cfc99b 100644 --- a/Sources/FontTool/FontToolHelper.swift +++ b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift @@ -1,5 +1,5 @@ // -// FontToolHelper.swift +// FontsToolHelper.swift // // // Created by Thibaut Schmitt on 13/12/2021. @@ -8,7 +8,7 @@ import Foundation import ToolCore -class FontToolHelper { +class FontsToolHelper { static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] { let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder) @@ -38,9 +38,9 @@ class FontToolHelper { // Get a enumerator for all files let fileManager = FileManager() guard fileManager.fileExists(atPath: inputFolder) else { - let error = FontToolError.inputFolderNotFound(inputFolder) + let error = FontsToolError.inputFolderNotFound(inputFolder) print(error.localizedDescription) - FontTool.exit(withError: error) + Fonts.exit(withError: error) } let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)! @@ -63,9 +63,9 @@ class FontToolHelper { let task = Shell.shell("fc-scan", "--format", "%{postscriptname}", path) guard let fontName = task.output, task.terminationStatus == 0 else { - let error = FontToolError.fcScan(path, task.terminationStatus, task.output) + let error = FontsToolError.fcScan(path, task.terminationStatus, task.output) print(error.localizedDescription) - FontTool.exit(withError: error) + Fonts.exit(withError: error) } return fontName diff --git a/Sources/FontTool/Generator/FontPlistGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift similarity index 94% rename from Sources/FontTool/Generator/FontPlistGenerator.swift rename to Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift index e127fef..00eca37 100644 --- a/Sources/FontTool/Generator/FontPlistGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift @@ -1,5 +1,5 @@ // -// File.swift +// FontPlistGenerator.swift // // // Created by Thibaut Schmitt on 29/08/2022. diff --git a/Sources/FontTool/Generator/FontToolContentGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift similarity index 82% rename from Sources/FontTool/Generator/FontToolContentGenerator.swift rename to Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift index 018071a..6089cf0 100644 --- a/Sources/FontTool/Generator/FontToolContentGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift @@ -31,15 +31,15 @@ class FontExtensionGenerator { do { try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) } catch (let error) { - let error = FontToolError.writeExtension(extensionFilePath, error.localizedDescription) + let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription) print(error.localizedDescription) - FontTool.exit(withError: error) + Fonts.exit(withError: error) } } private static func getHeader(extensionClassname: String) -> String { """ - // Generated by ResgenSwift.\(FontTool.toolName) \(ResgenSwiftVersion) + // Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion) import UIKit @@ -48,18 +48,18 @@ class FontExtensionGenerator { } private static func getFontNameEnum(fontsNames: [String]) -> String { - var enumDefinition = "\tenum FontName: String {\n" + var enumDefinition = " enum FontName: String {\n" fontsNames.forEach { - enumDefinition += "\t\tcase \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n" + enumDefinition += " case \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n" } - enumDefinition += "\t}\n" + enumDefinition += " }\n" return enumDefinition } private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String { - let pragma = "\t// MARK: - Getter" + let pragma = " // MARK: - Getter" var propertiesOrMethods: [String] = fontsNames .unique() diff --git a/Sources/FontTool/Model/FontName.swift b/Sources/ResgenSwift/Fonts/Model/FontName.swift similarity index 97% rename from Sources/FontTool/Model/FontName.swift rename to Sources/ResgenSwift/Fonts/Model/FontName.swift index e904ee1..7667a07 100644 --- a/Sources/FontTool/Model/FontName.swift +++ b/Sources/ResgenSwift/Fonts/Model/FontName.swift @@ -1,5 +1,5 @@ // -// File.swift +// FontName.swift // // // Created by Thibaut Schmitt on 29/08/2022. diff --git a/Sources/FontTool/Parser/FontFileParser.swift b/Sources/ResgenSwift/Fonts/Parser/FontFileParser.swift similarity index 93% rename from Sources/FontTool/Parser/FontFileParser.swift rename to Sources/ResgenSwift/Fonts/Parser/FontFileParser.swift index bbf1a89..61bb210 100644 --- a/Sources/FontTool/Parser/FontFileParser.swift +++ b/Sources/ResgenSwift/Fonts/Parser/FontFileParser.swift @@ -1,5 +1,5 @@ // -// File.swift +// FontFileParser.swift // // // Created by Thibaut Schmitt on 29/08/2022. diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift new file mode 100644 index 0000000..eba7822 --- /dev/null +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -0,0 +1,51 @@ +// +// Generate.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import ToolCore +import Foundation +import ArgumentParser + +struct Generate: ParsableCommand { + + // MARK: - CommandConfiguration + + static var configuration = CommandConfiguration( + abstract: "A utility to generate ressources based on a configuration file", + version: ResgenSwiftVersion + ) + + // MARK: - Static + + static let toolName = "Generate" + + // MARK: - Command Options + + @OptionGroup var options: GenerateOptions + + // MARK: - Run + + public func run() throws { + print("[\(Self.toolName)] Starting Resgen with configuration: \(options.configurationFile)") + + // Parse + let configuration = ConfigurationFileParser.parse(options.configurationFile) + print("Found configurations :") + print(" - \(configuration.colors.count) colors configuration") + print(" - \(configuration.fonts.count) fonts configuration") + print(" - \(configuration.images.count) images configuration") + print(" - \(configuration.strings.count) strings configuration") + print(" - \(configuration.tags.count) tags configuration") + print() + // Execute commands + configuration.runnableConfigurations + .forEach { + $0.run(force: options.forceGeneration) + } + + print("[\(Self.toolName)] Resgen ended") + } +} diff --git a/Sources/ResgenSwift/Generate/GenerateError.swift b/Sources/ResgenSwift/Generate/GenerateError.swift new file mode 100644 index 0000000..4960d37 --- /dev/null +++ b/Sources/ResgenSwift/Generate/GenerateError.swift @@ -0,0 +1,30 @@ +// +// ResgenSwiftError.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +enum GenerateError: Error { + case fileNotExists(String) + case invalidConfigurationFile(String) + case commandError([String], String) + + var localizedDescription: String { + switch self { + case .fileNotExists(let filename): + return " error:[\(Generate.toolName)] File \(filename) does not exists" + + case .invalidConfigurationFile(let filename): + return " error:[\(Generate.toolName)] File \(filename) is not a valid configuration file" + + case .commandError(let command, let terminationStatus): + let readableCommand = command + .map { $0 } + .joined(separator: " ") + return "error:[\(Generate.toolName)] An error occured while running command '\(readableCommand)'. Command terminate with status code: \(terminationStatus)" + } + } +} diff --git a/Sources/ResgenSwift/Generate/GenerateOptions.swift b/Sources/ResgenSwift/Generate/GenerateOptions.swift new file mode 100644 index 0000000..a457828 --- /dev/null +++ b/Sources/ResgenSwift/Generate/GenerateOptions.swift @@ -0,0 +1,19 @@ +// +// GenerateOptions.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +import Foundation +import ArgumentParser + +struct GenerateOptions: ParsableArguments { + @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") + var forceGeneration = false + + @Argument(help: "Configuration file.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) + var configurationFile: String +} diff --git a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift new file mode 100644 index 0000000..bf4bd84 --- /dev/null +++ b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift @@ -0,0 +1,140 @@ +// +// ConfigurationFile.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +struct ConfigurationFile: Codable, CustomDebugStringConvertible { + var colors: [ColorsConfiguration] + var fonts: [FontsConfiguration] + var images: [ImagesConfiguration] + var strings: [StringsConfiguration] + var tags: [TagsConfiguration] + + var runnableConfigurations: [Runnable] { + let runnables: [[Runnable]] = [colors, fonts, images, strings, tags] + return Array(runnables.joined()) + } + + var debugDescription: String { + """ + \(colors) + ----------- + ----------- + \(fonts) + ----------- + ----------- + \(images) + ----------- + ----------- + \(strings) + ----------- + ----------- + \(tags) + """ + } +} + + +struct ColorsConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let style: String + let xcassetsPath: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + + var debugDescription: String { + """ + Colors configuration: + - Input file: \(inputFile) + - Style: \(style) + - Xcassets path: \(xcassetsPath) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + +struct FontsConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + + var debugDescription: String { + """ + Fonts configuration: + - Input file: \(inputFile) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + +struct ImagesConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let xcassetsPath: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + + var debugDescription: String { + """ + Images configuration: + - Input file: \(inputFile) + - Xcassets path: \(xcassetsPath) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + +struct StringsConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let outputPath: String + let langs: String + let defaultLang: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + + var debugDescription: String { + """ + Strings configuration: + - Input file: \(inputFile) + - Output path: \(outputPath) + - Langs: \(langs) + - Default lang: \(defaultLang) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + +struct TagsConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let lang: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + + var debugDescription: String { + """ + Tags configuration: + - Input file: \(inputFile) + - Lang: \(lang) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + diff --git a/Sources/ResgenSwift/Generate/Parser/ConfigurationFileParser.swift b/Sources/ResgenSwift/Generate/Parser/ConfigurationFileParser.swift new file mode 100644 index 0000000..39e64bf --- /dev/null +++ b/Sources/ResgenSwift/Generate/Parser/ConfigurationFileParser.swift @@ -0,0 +1,27 @@ +// +// ConfigurationFileParser.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation +import Yams + +class ConfigurationFileParser { + static func parse(_ configurationFile: String) -> ConfigurationFile { + guard let data = FileManager().contents(atPath: configurationFile) else { + let error = GenerateError.fileNotExists(configurationFile) + print(error.localizedDescription) + Generate.exit(withError: error) + } + + guard let configuration = try? YAMLDecoder().decode(ConfigurationFile.self, from: data) else { + let error = GenerateError.invalidConfigurationFile(configurationFile) + print(error.localizedDescription) + Generate.exit(withError: error) + } + + return configuration + } +} diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift new file mode 100644 index 0000000..ea62f0d --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift @@ -0,0 +1,43 @@ +// +// ColorsConfiguration+ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +extension ColorsConfiguration: Runnable { + func run(force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile, + "--style", + style, + "--xcassets-path", + xcassetsPath, + "--extension-output-path", + extensionOutputPath + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Colors.main(args) + } +} diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift new file mode 100644 index 0000000..0c9005f --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift @@ -0,0 +1,40 @@ +// +// FontsConfiguration+ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +extension FontsConfiguration: Runnable { + func run(force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile, + "--extension-output-path", + extensionOutputPath + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Fonts.main(args) + } +} diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift new file mode 100644 index 0000000..82e0fcb --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift @@ -0,0 +1,41 @@ +// +// ImagesConfiguration+ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +extension ImagesConfiguration: Runnable { + func run(force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile, + "--xcassets-path", + xcassetsPath, + "--extension-output-path", + extensionOutputPath + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Images.main(args) + } +} diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift new file mode 100644 index 0000000..5b8c46f --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift @@ -0,0 +1,13 @@ +// +// ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +protocol Runnable { + func run(force: Bool) +} + diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift new file mode 100644 index 0000000..03726a7 --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift @@ -0,0 +1,46 @@ +// +// StringsConfiguration+ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +extension StringsConfiguration: Runnable { + func run(force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile, + "--output-path", + outputPath, + "--langs", + langs, + "--default-lang", + defaultLang, + "--extension-output-path", + extensionOutputPath + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Stringium.main(args) + } +} diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift new file mode 100644 index 0000000..e89fff2 --- /dev/null +++ b/Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift @@ -0,0 +1,41 @@ +// +// TagsConfiguration+ShellCommandable.swift +// +// +// Created by Thibaut Schmitt on 30/08/2022. +// + +import Foundation + +extension TagsConfiguration: Runnable { + func run(force: Bool) { + var args = [String]() + + if force { + args += ["-f"] + } + + args += [ + inputFile, + "--lang", + lang, + "--extension-output-path", + extensionOutputPath + ] + + if let extensionName = extensionName { + args += [ + "--extension-name", + extensionName + ] + } + if let extensionSuffix = extensionSuffix { + args += [ + "--extension-suffix", + extensionSuffix + ] + } + + Tags.main(args) + } +} diff --git a/Sources/Imagium/Extensions/FileManagerExtensions.swift b/Sources/ResgenSwift/Images/Extensions/FileManagerExtensions.swift similarity index 74% rename from Sources/Imagium/Extensions/FileManagerExtensions.swift rename to Sources/ResgenSwift/Images/Extensions/FileManagerExtensions.swift index 0b98d2d..c9eb83c 100644 --- a/Sources/Imagium/Extensions/FileManagerExtensions.swift +++ b/Sources/ResgenSwift/Images/Extensions/FileManagerExtensions.swift @@ -11,9 +11,9 @@ extension FileManager { func getAllRegularFileIn(directory: String) -> [String] { var files = [String]() guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else { - let error = ImagiumError.unknown("Cannot enumerate file in \(directory)") + let error = ImagesError.unknown("Cannot enumerate file in \(directory)") print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } for case let fileURL as URL in enumerator { @@ -23,9 +23,9 @@ extension FileManager { files.append(fileURL.relativePath) } } catch { - let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription) + let error = ImagesError.getFileAttributed(fileURL.relativePath, error.localizedDescription) print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } } return files @@ -34,9 +34,9 @@ extension FileManager { func getAllImageSetFolderIn(directory: String) -> [String] { var files = [String]() guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else { - let error = ImagiumError.unknown("Cannot enumerate imageset directory in \(directory)") + let error = ImagesError.unknown("Cannot enumerate imageset directory in \(directory)") print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } for case let fileURL as URL in enumerator { @@ -46,9 +46,9 @@ extension FileManager { files.append(fileURL.lastPathComponent) } } catch { - let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription) + let error = ImagesError.getFileAttributed(fileURL.relativePath, error.localizedDescription) print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } } return files diff --git a/Sources/Imagium/Generator/ImageExtensionGenerator.swift b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift similarity index 95% rename from Sources/Imagium/Generator/ImageExtensionGenerator.swift rename to Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift index 9dcf1e2..1d34c96 100644 --- a/Sources/Imagium/Generator/ImageExtensionGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift @@ -58,9 +58,9 @@ class ImageExtensionGenerator { do { try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) } catch (let error) { - let error = ImagiumError.writeFile(extensionFilePath, error.localizedDescription) + let error = ImagesError.writeFile(extensionFilePath, error.localizedDescription) print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } } diff --git a/Sources/Imagium/Generator/XcassetsGenerator.swift b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift similarity index 97% rename from Sources/Imagium/Generator/XcassetsGenerator.swift rename to Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift index 17cee8d..08ec6b7 100644 --- a/Sources/Imagium/Generator/XcassetsGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift @@ -1,5 +1,5 @@ // -// File.swift +// XcassetsGenerator.swift // // // Created by Thibaut Schmitt on 24/01/2022. @@ -24,7 +24,7 @@ class XcassetsGenerator { func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) { let fileManager = FileManager() - let svgConverter = Imagium.getSvgConverterPath() + let svgConverter = Images.getSvgConverterPath() let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath) var generatedAssetsPaths = [String]() @@ -47,9 +47,9 @@ class XcassetsGenerator { return (subfile, "jepg") } } - let error = ImagiumError.unknownImageExtension(parsedImage.name) + let error = ImagesError.unknownImageExtension(parsedImage.name) print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) }() // Create imageset folder diff --git a/Sources/Imagium/main.swift b/Sources/ResgenSwift/Images/Images.swift similarity index 89% rename from Sources/Imagium/main.swift rename to Sources/ResgenSwift/Images/Images.swift index 2696847..bfe3af8 100644 --- a/Sources/Imagium/main.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -1,5 +1,5 @@ // -// Imagium.swift +// Images.swift // // // Created by Thibaut Schmitt on 24/01/2022. @@ -9,7 +9,7 @@ import ToolCore import Foundation import ArgumentParser -struct Imagium: ParsableCommand { +struct Images: ParsableCommand { // MARK: - CommandConfiguration @@ -20,7 +20,7 @@ struct Imagium: ParsableCommand { // MARK: - Static - static let toolName = "Imagium" + static let toolName = "Images" static let defaultExtensionName = "UIImage" // MARK: - Properties @@ -35,7 +35,7 @@ struct Imagium: ParsableCommand { // MARK: - Command Options - @OptionGroup var options: ImagiumOptions + @OptionGroup var options: ImagesOptions // MARK: - Run @@ -81,13 +81,13 @@ struct Imagium: ParsableCommand { // Input file guard fileManager.fileExists(atPath: options.inputFile) else { - let error = ImagiumError.fileNotExists(options.inputFile) + let error = ImagesError.fileNotExists(options.inputFile) print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } // RSVG-Converter - _ = Imagium.getSvgConverterPath() + _ = Images.getSvgConverterPath() // Check if needed to regenerate guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { @@ -107,10 +107,8 @@ struct Imagium: ParsableCommand { return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines) } - let error = ImagiumError.rsvgConvertNotFound + let error = ImagesError.rsvgConvertNotFound print(error.localizedDescription) - Imagium.exit(withError: error) + Images.exit(withError: error) } } - -Imagium.main() diff --git a/Sources/ResgenSwift/Images/ImagesError.swift b/Sources/ResgenSwift/Images/ImagesError.swift new file mode 100644 index 0000000..8afb628 --- /dev/null +++ b/Sources/ResgenSwift/Images/ImagesError.swift @@ -0,0 +1,43 @@ +// +// ImagesError.swift +// +// +// Created by Thibaut Schmitt on 24/01/2022. +// + +import Foundation + +enum ImagesError: Error { + case inputFolderNotFound(String) + case fileNotExists(String) + case unknownImageExtension(String) + case getFileAttributed(String, String) + case rsvgConvertNotFound + case writeFile(String, String) + case unknown(String) + + var localizedDescription: String { + switch self { + case .inputFolderNotFound(let inputFolder): + return " error:[\(Images.toolName)] Input folder not found: \(inputFolder)" + + case .fileNotExists(let filename): + return " error:[\(Images.toolName)] File \(filename) does not exists" + + case .unknownImageExtension(let filename): + return " error:[\(Images.toolName)] File \(filename) have an unhandled file extension. Cannot generate image." + + case .getFileAttributed(let filename, let errorDescription): + return " error:[\(Images.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)" + + case .rsvgConvertNotFound: + return " error:[\(Images.toolName)] Can't find rsvg-convert (can be installed with 'brew remove imagemagick && brew install imagemagick --with-librsvg')" + + case .writeFile(let subErrorDescription, let filename): + return " error:[\(Images.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)" + + case .unknown(let errorDescription): + return " error:[\(Images.toolName)] Unknown error: \(errorDescription)" + } + } +} diff --git a/Sources/Imagium/ImagiumOptions.swift b/Sources/ResgenSwift/Images/ImagesOptions.swift similarity index 92% rename from Sources/Imagium/ImagiumOptions.swift rename to Sources/ResgenSwift/Images/ImagesOptions.swift index 6fc88c2..7eb5831 100644 --- a/Sources/Imagium/ImagiumOptions.swift +++ b/Sources/ResgenSwift/Images/ImagesOptions.swift @@ -8,7 +8,7 @@ import Foundation import ArgumentParser -struct ImagiumOptions: ParsableArguments { +struct ImagesOptions: ParsableArguments { @Flag(name: .customShort("f"), help: "Should force script execution") var forceExecution = false @@ -25,7 +25,7 @@ struct ImagiumOptions: ParsableArguments { var extensionOutputPath: String @Option(help: "Extension name. If not specified, it will generate an UIImage extension. Using default extension name will generate static property.") - var extensionName: String = Imagium.defaultExtensionName + var extensionName: String = Images.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift") var extensionSuffix: String = "" diff --git a/Sources/Imagium/Model/ConvertArgument.swift b/Sources/ResgenSwift/Images/Model/ConvertArgument.swift similarity index 100% rename from Sources/Imagium/Model/ConvertArgument.swift rename to Sources/ResgenSwift/Images/Model/ConvertArgument.swift diff --git a/Sources/Imagium/Model/ParsedImage.swift b/Sources/ResgenSwift/Images/Model/ParsedImage.swift similarity index 100% rename from Sources/Imagium/Model/ParsedImage.swift rename to Sources/ResgenSwift/Images/Model/ParsedImage.swift diff --git a/Sources/Imagium/Model/PlatormTag.swift b/Sources/ResgenSwift/Images/Model/PlatormTag.swift similarity index 100% rename from Sources/Imagium/Model/PlatormTag.swift rename to Sources/ResgenSwift/Images/Model/PlatormTag.swift diff --git a/Sources/Imagium/Parser/ImageFileParser.swift b/Sources/ResgenSwift/Images/Parser/ImageFileParser.swift similarity index 100% rename from Sources/Imagium/Parser/ImageFileParser.swift rename to Sources/ResgenSwift/Images/Parser/ImageFileParser.swift diff --git a/Sources/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift similarity index 100% rename from Sources/Strings/Generator/StringsFileGenerator.swift rename to Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift diff --git a/Sources/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift similarity index 82% rename from Sources/Strings/Generator/TagsGenerator.swift rename to Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift index 32bd324..7b3bbd7 100644 --- a/Sources/Strings/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -11,7 +11,7 @@ import CoreVideo class TagsGenerator { static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { - let extensionHeader = Self.getHeader(extensionClassname: extensionName) + let extensionHeader = Self.getHeader(extensionClassname: extensionName, staticVar: staticVar) let extensionFooter = Self.getFooter() let extensionContent: String = { @@ -22,7 +22,7 @@ class TagsGenerator { return // Go to next section } - content += "\n\t// MARK: - \(section.name)" + content += "\n // MARK: - \(section.name)" section.definitions.forEach { definition in if staticVar { content += "\n\n\(definition.getStaticProperty(forLang: lang))" @@ -55,21 +55,19 @@ class TagsGenerator { } } - private static func getHeader(extensionClassname: String) -> String { - """ - // Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion) - - // typelias Tags = String - - import UIKit - - extension \(extensionClassname) { - """ + private static func getHeader(extensionClassname: String, staticVar: Bool) -> String { + """ + // Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion) + + \(staticVar ? "typelias Tags = String\n\n" : "")import UIKit + + extension \(extensionClassname) { + """ } private static func getFooter() -> String { - """ - } - """ + """ + } + """ } } diff --git a/Sources/Strings/Model/Definition.swift b/Sources/ResgenSwift/Strings/Model/Definition.swift similarity index 100% rename from Sources/Strings/Model/Definition.swift rename to Sources/ResgenSwift/Strings/Model/Definition.swift diff --git a/Sources/Strings/Model/Section.swift b/Sources/ResgenSwift/Strings/Model/Section.swift similarity index 100% rename from Sources/Strings/Model/Section.swift rename to Sources/ResgenSwift/Strings/Model/Section.swift diff --git a/Sources/Strings/Parser/TwineFileParser.swift b/Sources/ResgenSwift/Strings/Parser/TwineFileParser.swift similarity index 100% rename from Sources/Strings/Parser/TwineFileParser.swift rename to Sources/ResgenSwift/Strings/Parser/TwineFileParser.swift diff --git a/Sources/Strings/Stringium/Stringium.swift b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift similarity index 93% rename from Sources/Strings/Stringium/Stringium.swift rename to Sources/ResgenSwift/Strings/Stringium/Stringium.swift index 2193c8f..626d5e9 100644 --- a/Sources/Strings/Stringium/Stringium.swift +++ b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift @@ -27,8 +27,8 @@ struct Stringium: ParsableCommand { // MARK: - Properties var extensionFileName: String { - if options.extensionSuffix.isEmpty == false { - return "\(options.extensionName)+\(options.extensionSuffix).swift" + if let extensionSuffix = options.extensionSuffix { + return "\(options.extensionName)+\(extensionSuffix).swift" } return "\(options.extensionName).swift" } @@ -47,8 +47,6 @@ struct Stringium: ParsableCommand { // MARK: - Command options - // The `@OptionGroup` attribute includes the flags, options, and - // arguments defined by another `ParsableArguments` type. @OptionGroup var options: StringiumOptions // MARK: - Run diff --git a/Sources/Strings/Stringium/StringiumError.swift b/Sources/ResgenSwift/Strings/Stringium/StringiumError.swift similarity index 100% rename from Sources/Strings/Stringium/StringiumError.swift rename to Sources/ResgenSwift/Strings/Stringium/StringiumError.swift diff --git a/Sources/Strings/Stringium/StringiumOptions.swift b/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift similarity index 98% rename from Sources/Strings/Stringium/StringiumOptions.swift rename to Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift index 61d715a..0277aa5 100644 --- a/Sources/Strings/Stringium/StringiumOptions.swift +++ b/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift @@ -34,7 +34,7 @@ struct StringiumOptions: ParsableArguments { var extensionName: String = Stringium.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+{extensionSuffix}.swift") - var extensionSuffix: String = "" + var extensionSuffix: String? } extension StringiumOptions { diff --git a/Sources/Strings/main.swift b/Sources/ResgenSwift/Strings/Strings.swift similarity index 97% rename from Sources/Strings/main.swift rename to Sources/ResgenSwift/Strings/Strings.swift index fc2fffb..a6bb185 100644 --- a/Sources/Strings/main.swift +++ b/Sources/ResgenSwift/Strings/Strings.swift @@ -26,5 +26,5 @@ struct Strings: ParsableCommand { ) } -Strings.main() +//Strings.main() diff --git a/Sources/Strings/Tag/Tags.swift b/Sources/ResgenSwift/Strings/Tag/Tags.swift similarity index 90% rename from Sources/Strings/Tag/Tags.swift rename to Sources/ResgenSwift/Strings/Tag/Tags.swift index 7c7f1b6..6c9455d 100644 --- a/Sources/Strings/Tag/Tags.swift +++ b/Sources/ResgenSwift/Strings/Tag/Tags.swift @@ -28,8 +28,8 @@ struct Tags: ParsableCommand { // MARK: - Properties var extensionFileName: String { - if options.extensionSuffix.isEmpty == false { - return "\(options.extensionName)+\(options.extensionSuffix).swift" + if let extensionSuffix = options.extensionSuffix { + return "\(options.extensionName)+\(extensionSuffix).swift" } return "\(options.extensionName).swift" } @@ -40,8 +40,6 @@ struct Tags: ParsableCommand { // MARK: - Command Options - // The `@OptionGroup` attribute includes the flags, options, and - // arguments defined by another `ParsableArguments` type. @OptionGroup var options: TagsOptions // MARK: - Run diff --git a/Sources/Strings/Tag/TagsOptions.swift b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift similarity index 96% rename from Sources/Strings/Tag/TagsOptions.swift rename to Sources/ResgenSwift/Strings/Tag/TagsOptions.swift index 8cd82cf..4dc643a 100644 --- a/Sources/Strings/Tag/TagsOptions.swift +++ b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift @@ -25,5 +25,5 @@ struct TagsOptions: ParsableArguments { var extensionName: String = Tags.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift") - var extensionSuffix: String = "" + var extensionSuffix: String? } diff --git a/Sources/Strings/Twine/Twine.swift b/Sources/ResgenSwift/Strings/Twine/Twine.swift similarity index 96% rename from Sources/Strings/Twine/Twine.swift rename to Sources/ResgenSwift/Strings/Twine/Twine.swift index a40d52f..97ae46d 100644 --- a/Sources/Strings/Twine/Twine.swift +++ b/Sources/ResgenSwift/Strings/Twine/Twine.swift @@ -33,8 +33,6 @@ struct Twine: ParsableCommand { // MARK: - Command Options - // The `@OptionGroup` attribute includes the flags, options, and - // arguments defined by another `ParsableArguments` type. @OptionGroup var options: TwineOptions // MARK: - Run diff --git a/Sources/Strings/Twine/TwineError.swift b/Sources/ResgenSwift/Strings/Twine/TwineError.swift similarity index 100% rename from Sources/Strings/Twine/TwineError.swift rename to Sources/ResgenSwift/Strings/Twine/TwineError.swift diff --git a/Sources/Strings/Twine/TwineOptions.swift b/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift similarity index 97% rename from Sources/Strings/Twine/TwineOptions.swift rename to Sources/ResgenSwift/Strings/Twine/TwineOptions.swift index 1c020ec..4a3752a 100644 --- a/Sources/Strings/Twine/TwineOptions.swift +++ b/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift @@ -1,5 +1,5 @@ // -// File.swift +// TwineOptions.swift // // // Created by Thibaut Schmitt on 10/01/2022. diff --git a/Sources/ResgenSwift/main.swift b/Sources/ResgenSwift/main.swift index 1830a03..996161e 100644 --- a/Sources/ResgenSwift/main.swift +++ b/Sources/ResgenSwift/main.swift @@ -1 +1,35 @@ -print("Welcome ResgenSwift") +// +// ResgenSwift.swift +// +// +// Created by Thibaut Schmitt on 13/12/2021. +// + +import ToolCore +import Foundation +import ArgumentParser + +struct ResgenSwift: ParsableCommand { + + static var configuration = CommandConfiguration( + abstract: "A utility for generate ressources.", + version: ResgenSwiftVersion, + + // Pass an array to `subcommands` to set up a nested tree of subcommands. + // With language support for type-level introspection, this could be + // provided by automatically finding nested `ParsableCommand` types. + subcommands: [ + Colors.self, + Fonts.self, + Images.self, + Strings.self, + Generate.self + ] + + // A default subcommand, when provided, is automatically selected if a + // subcommand is not given on the command line. + //defaultSubcommand: Twine.self + ) +} + +ResgenSwift.main() diff --git a/Sources/ToolCore/Shell.swift b/Sources/ToolCore/Shell.swift index 0f8c28b..aa7c3bc 100644 --- a/Sources/ToolCore/Shell.swift +++ b/Sources/ToolCore/Shell.swift @@ -10,14 +10,14 @@ import Foundation public class Shell { @discardableResult - public static func shell(_ args: String...) -> (terminationStatus: Int32, output: String?) { + public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) { let task = Process() - task.launchPath = "/usr/bin/env" + task.launchPath = launchPath task.arguments = args let pipe = Pipe() task.standardOutput = pipe - task.launch() + try? task.run() task.waitUntilExit() let data = pipe.fileHandleForReading.readDataToEndOfFile() @@ -30,9 +30,9 @@ public class Shell { } @discardableResult - public static func shell(_ args: [String]) -> (terminationStatus: Int32, output: String?) { + public static func shell(launchPath: String = "/usr/bin/env", _ args: [String]) -> (terminationStatus: Int32, output: String?) { let task = Process() - task.launchPath = "/usr/bin/env" + task.launchPath = launchPath task.arguments = args let pipe = Pipe() -- 2.39.5 From 3d60513c08b6ba634012910ac3ddd8549e289dca Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Wed, 31 Aug 2022 15:18:44 +0200 Subject: [PATCH 02/10] Add new Flag to every command to choose if we want to generate static members or not --- README.md | 140 +++++++++++++++--- Sources/ResgenSwift/Colors/Colors.swift | 21 +-- .../Colors/ColorsToolOptions.swift | 18 ++- .../Colors/Parser/ColorFileParser.swift | 1 + Sources/ResgenSwift/Fonts/FontOptions.swift | 9 +- Sources/ResgenSwift/Fonts/Fonts.swift | 21 +-- Sources/ResgenSwift/Generate/Generate.swift | 1 + .../Generate/Model/ConfigurationFile.swift | 40 +++++ ...ColorsConfiguration+ShellCommandable.swift | 4 +- .../FontsConfiguration+ShellCommandable.swift | 4 +- ...ImagesConfiguration+ShellCommandable.swift | 6 +- .../Runnable.swift | 0 ...tringsConfiguration+ShellCommandable.swift | 4 +- .../TagsConfiguration+ShellCommandable.swift | 4 +- .../Images/Generator/XcassetsGenerator.swift | 2 +- Sources/ResgenSwift/Images/Images.swift | 21 +-- .../ResgenSwift/Images/ImagesOptions.swift | 31 +++- .../Generator/StringsFileGenerator.swift | 2 +- .../Strings/Stringium/Stringium.swift | 33 +---- .../Strings/Stringium/StringiumOptions.swift | 26 ++++ Sources/ResgenSwift/Strings/Tag/Tags.swift | 21 +-- .../ResgenSwift/Strings/Tag/TagsOptions.swift | 18 +++ Sources/ResgenSwift/Strings/Twine/Twine.swift | 19 +-- .../Strings/Twine/TwineOptions.swift | 21 +++ 24 files changed, 323 insertions(+), 144 deletions(-) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/ColorsConfiguration+ShellCommandable.swift (89%) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/FontsConfiguration+ShellCommandable.swift (88%) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/ImagesConfiguration+ShellCommandable.swift (82%) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/Runnable.swift (100%) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/StringsConfiguration+ShellCommandable.swift (90%) rename Sources/ResgenSwift/Generate/{ShellCommandable => Runnable}/TagsConfiguration+ShellCommandable.swift (88%) diff --git a/README.md b/README.md index 78384b2..81fb67f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ResgenSwift is a package, fully written in Swift, to help you automatize ressour > 🧐 For all commands, see samples files in `SampleFiles` -# Fonts +## Fonts Font generator generates an extension of `UIFont` (or a custom class). It also prints `UIAppFonts` to put in your project `.plist`. iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`. @@ -15,7 +15,8 @@ iOS required to use the **real name** of the font, this name can be different fr swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \ --extension-output-path "./Fonts/Generated" \ --extension-name "AppFont" \ - --extension-suffix "GreatApp" + --extension-suffix "GreatApp" \ + --static-members true ``` **Parameters** @@ -25,11 +26,10 @@ swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \ 3. `--extension-output-path`: path where to generate generated extension 4. `--extension-name` *(optional)* : name of thee class to add the extension 5. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`) - -> ⚠️ If extension name is not set or is `UIFont`, it will generate static property on `UIFont` instead of method in your custom class. +6. `--static-members` *(optional)*: generate static properties or not -# Colors +## Colors Colors generator generates an extension of `UIColor` (or a custom class) along with colorsets in specified xcassets. @@ -39,7 +39,8 @@ swift run -c release ColorTool $FORCE_FLAG "./Colors/colors.txt" \ --xcassets-path "./Colors/colors.xcassets" \ --extension-output-path "./Colors/Generated/" \ --extension-name "AppColor" \ - --extension-suffix "GreatApp" + --extension-suffix "GreatApp" \ + --static-members true ``` **Parameters** @@ -50,15 +51,14 @@ swift run -c release ColorTool $FORCE_FLAG "./Colors/colors.txt" \ 4. `--extension-output-path`: path where to generate generated extension 5. `--extension-name` *(optional)* : name of class to add the extension 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`) +7. `--static-members` *(optional)*: generate static properties or not -> ⚠️ If extension name is not set or is `UIColor`, it will generate static property on `UIColor`. - -# Strings +## Strings Strings command allows to generate `strings` files along with extensions to access those strings easily. It can do it 2 ways: Twine and Stringium. It is not recommended to use Twine except on legacy projects or while migrating to ResgenSwift, because it use https://github.com/openium/twine. Using Stringium is recommended because it does not required external dependency and allow more customisation. -## Twine (not recommended) +### Twine (not recommended) ``` swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \ @@ -76,7 +76,7 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \ 4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well 4. `--extension-output-path`: path where to generate generated extension -## Stringium (recommended) +### Stringium (recommended) ``` swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \ @@ -85,7 +85,8 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \ --default-lang "en" \ --extension-output-path "./Strings/Generated" \ --extension-name "AppString" \ - --extension-suffix "GreatApp" + --extension-suffix "GreatApp" \ + --static-members true ``` **Parameters** @@ -97,10 +98,10 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \ 4. `--extension-output-path`: path where to generate generated extension 5. `--extension-name` *(optional)* : name of class to add the extension 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`) +7. `--static-members` *(optional)*: generate static properties or not -> ⚠️ If extension name is not set or is `String`, it will generate static property on `String`. -# Tags +## Tags Tags is also a subcommand of `Strings`. Input files are formatted the same way. Tags will generate properties which return exactly what is specified in the input file. It was designed to be used for analytics purpose and to be shared with any other platform to have the same analytics keys. @@ -109,7 +110,8 @@ swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \ --lang "ium" \ --extension-output-path "./Tags/Generated" \ --extension-name "AppTags" \ - --extension-suffix "GreatApp" + --extension-suffix "GreatApp" \ + --static-members true ``` **Parameters** @@ -120,10 +122,11 @@ swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \ 4. `--extension-output-path`: path where to generate generated extension 5. `--extension-name` *(optional)* : name of class to add the extension 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`) +7. `--static-members` *(optional)*: generate static properties or not -> ⚠️ If extension name is not set or is `Tags`, it will generate static property on `Tags`. `Tags` is a typealias of `String`. +> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`. -# Images +## Images Images generator will generate images assets along with extensions to access those images easily. @@ -132,7 +135,8 @@ swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \ --xcassets-path "./Images/app.xcassets" \ --extension-output-path "./Images/Generated" \ --extension-name "AppImage" \ - --extension-suffix "GreatApp" + --extension-suffix "GreatApp" \ + --static-members true ``` **Parameters** @@ -143,9 +147,103 @@ swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \ 4. `--extension-output-path`: path where to generate generated extension 5. `--extension-name` *(optional)* : name of class to add the extension 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`) +7. `--static-members` *(optional)*: generate static properties or not -> ⚠️ If extension name is not set or is `UIImage`, it will generate static property on `UIImage`. -# TODO +## All at once + +Another command exists to generate all ressources at the same time: `generate`. It use the following commands: `Fonts`, `Colors`, `Strings/Stringium`, `Strings/Tags`, `Images`. + +All parameters can be specified in a configuration file in `Yaml`: + +> Order of configuration types does not matter. Order them to fit your needs. + +```yaml +--- +colors: +- + inputFile: String + style: [light/all] + xcassetsPath: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? + +fonts: +- + inputFile: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? + +images: +- + inputFile: String + xcassetsPath: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? + +strings: +- + inputFile: String + outputPath: String + langs: String + defaultLang: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? + +tags: +- + inputFile: String + lang: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? +``` + +### Multiple configurations + +In some case, you may need to have 2 colors files in your projects. You will need 2 colors configurations. Every configuration type is an array and can contains as many configurations as you need. + +Sample for 2 colors configurations: + +```yaml +... +colors: +- + inputFile: String + style: [light/all] + xcassetsPath: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? +- + inputFile: String + style: [light/all] + xcassetsPath: String + extensionOutputPath: String + extensionName: String? + extensionSuffix: String? + staticMembers: Bool? +... +``` + +### No configuration + +In some case, you may not need to generate tags. You must specified `tags` as an empty array : + +```yaml +... +tags: [] +... +``` + -[ ] Allow static variable generation on custom extension diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index d905bad..9d0da92 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -24,19 +24,6 @@ struct Colors: ParsableCommand { static let defaultExtensionName = "UIColor" static let assetsColorsFolderName = "Colors" - // MARK: - Properties - - var extensionFileName: String { - if options.extensionSuffix.isEmpty == false { - return "\(options.extensionName)+\(options.extensionSuffix).swift" - } - return "\(options.extensionName).swift" - } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } - var generateStaticVariable: Bool { - options.extensionName == Self.defaultExtensionName - } - // MARK: - Command options @OptionGroup var options: ColorsToolOptions @@ -65,9 +52,9 @@ struct Colors: ParsableCommand { // Generate extension ColorExtensionGenerator.writeExtensionFile(colors: parsedColors, - staticVar: generateStaticVariable, + staticVar: options.staticMembers, extensionName: options.extensionName, - extensionFilePath: extensionFilePath) + extensionFilePath: options.extensionFilePath) print("[\(Self.toolName)] Colors generated") } @@ -92,7 +79,9 @@ struct Colors: ParsableCommand { } // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { print("[\(Self.toolName)] Colors are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Colors/ColorsToolOptions.swift b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift index a9aea80..9f5ae80 100644 --- a/Sources/ResgenSwift/Colors/ColorsToolOptions.swift +++ b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift @@ -24,15 +24,31 @@ struct ColorsToolOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String + @Option(help: "Tell if it will generate static properties or not") + var staticMembers: Bool = false + @Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.") var extensionName: String = Colors.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift") - var extensionSuffix: String = "" + var extensionSuffix: String? } +// MARK: - Computed var + extension ColorsToolOptions { var colorStyle: ColorStyle { ColorStyle(rawValue: style) ?? .all } + + var extensionFileName: String { + if let extensionSuffix = extensionSuffix { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } } diff --git a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift index a96b4e7..653006b 100644 --- a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift +++ b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift @@ -18,6 +18,7 @@ class ColorFileParser { // Required format: // colorName="#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB" let colorLineCleanedUp = colorLine + .removeLeadingWhitespace() .removeTrailingWhitespace() .replacingOccurrences(of: "=", with: "") // Keep compat with current file format diff --git a/Sources/ResgenSwift/Fonts/FontOptions.swift b/Sources/ResgenSwift/Fonts/FontOptions.swift index cec71a4..983f00d 100644 --- a/Sources/ResgenSwift/Fonts/FontOptions.swift +++ b/Sources/ResgenSwift/Fonts/FontOptions.swift @@ -18,6 +18,9 @@ struct FontsOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String + @Option(help: "Tell if it will generate static properties or methods") + var staticMembers: Bool = false + @Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.") var extensionName: String = Fonts.defaultExtensionName @@ -25,6 +28,8 @@ struct FontsOptions: ParsableArguments { var extensionSuffix: String = "" } +// MARK: - Computed var + extension FontsOptions { var extensionFileName: String { if extensionSuffix.isEmpty == false { @@ -36,8 +41,4 @@ extension FontsOptions { var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" } - - var generateStaticVariable: Bool { - extensionName == Fonts.defaultExtensionName - } } diff --git a/Sources/ResgenSwift/Fonts/Fonts.swift b/Sources/ResgenSwift/Fonts/Fonts.swift index a010cb3..671b477 100644 --- a/Sources/ResgenSwift/Fonts/Fonts.swift +++ b/Sources/ResgenSwift/Fonts/Fonts.swift @@ -23,19 +23,6 @@ struct Fonts: ParsableCommand { static let toolName = "Fonts" static let defaultExtensionName = "UIFont" - // MARK: - Properties - - var extensionFileName: String { - if options.extensionSuffix.isEmpty == false { - return "\(options.extensionName)+\(options.extensionSuffix).swift" - } - return "\(options.extensionName).swift" - } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } - var generateStaticVariable: Bool { - options.extensionName == Self.defaultExtensionName - } - // MARK: - Command Options @OptionGroup var options: FontsOptions @@ -60,9 +47,9 @@ struct Fonts: ParsableCommand { // Generate extension FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames, - staticVar: generateStaticVariable, + staticVar: options.staticMembers, extensionName: options.extensionName, - extensionFilePath: extensionFilePath) + extensionFilePath: options.extensionFilePath) print("Info.plist information:") print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))") @@ -83,7 +70,9 @@ struct Fonts: ParsableCommand { } // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { print("[\(Self.toolName)] Fonts are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift index eba7822..1d91f88 100644 --- a/Sources/ResgenSwift/Generate/Generate.swift +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -44,6 +44,7 @@ struct Generate: ParsableCommand { configuration.runnableConfigurations .forEach { $0.run(force: options.forceGeneration) + print("\n") } print("[\(Self.toolName)] Resgen ended") diff --git a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift index bf4bd84..7ba457f 100644 --- a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift +++ b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift @@ -46,6 +46,14 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible { let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } var debugDescription: String { """ @@ -65,6 +73,14 @@ struct FontsConfiguration: Codable, CustomDebugStringConvertible { let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } var debugDescription: String { """ @@ -83,6 +99,14 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible { let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } var debugDescription: String { """ @@ -104,6 +128,14 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible { let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } var debugDescription: String { """ @@ -125,6 +157,14 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible { let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } var debugDescription: String { """ diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift similarity index 89% rename from Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift rename to Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift index ea62f0d..390e177 100644 --- a/Sources/ResgenSwift/Generate/ShellCommandable/ColorsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift @@ -22,7 +22,9 @@ extension ColorsConfiguration: Runnable { "--xcassets-path", xcassetsPath, "--extension-output-path", - extensionOutputPath + extensionOutputPath, + "--static-members", + "\(staticMembersOptions)" ] if let extensionName = extensionName { diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift similarity index 88% rename from Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift rename to Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift index 0c9005f..ece931a 100644 --- a/Sources/ResgenSwift/Generate/ShellCommandable/FontsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift @@ -18,7 +18,9 @@ extension FontsConfiguration: Runnable { args += [ inputFile, "--extension-output-path", - extensionOutputPath + extensionOutputPath, + "--static-members", + "\(staticMembersOptions)" ] if let extensionName = extensionName { diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift similarity index 82% rename from Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift rename to Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift index 82e0fcb..837bbf7 100644 --- a/Sources/ResgenSwift/Generate/ShellCommandable/ImagesConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift @@ -12,7 +12,7 @@ extension ImagesConfiguration: Runnable { var args = [String]() if force { - args += ["-f"] + args += ["-F"] // Images has a -f and -F options } args += [ @@ -20,7 +20,9 @@ extension ImagesConfiguration: Runnable { "--xcassets-path", xcassetsPath, "--extension-output-path", - extensionOutputPath + extensionOutputPath, + "--static-members", + "\(staticMembersOptions)" ] if let extensionName = extensionName { diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/Runnable.swift similarity index 100% rename from Sources/ResgenSwift/Generate/ShellCommandable/Runnable.swift rename to Sources/ResgenSwift/Generate/Runnable/Runnable.swift diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift similarity index 90% rename from Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift rename to Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift index 03726a7..ae60f95 100644 --- a/Sources/ResgenSwift/Generate/ShellCommandable/StringsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift @@ -24,7 +24,9 @@ extension StringsConfiguration: Runnable { "--default-lang", defaultLang, "--extension-output-path", - extensionOutputPath + extensionOutputPath, + "--static-members", + "\(staticMembersOptions)" ] if let extensionName = extensionName { diff --git a/Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift similarity index 88% rename from Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift rename to Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift index e89fff2..efab0d2 100644 --- a/Sources/ResgenSwift/Generate/ShellCommandable/TagsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift @@ -20,7 +20,9 @@ extension TagsConfiguration: Runnable { "--lang", lang, "--extension-output-path", - extensionOutputPath + extensionOutputPath, + "--static-members", + "\(staticMembersOptions)" ] if let extensionName = extensionName { diff --git a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift index 08ec6b7..8c5fe8f 100644 --- a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift @@ -148,7 +148,7 @@ class XcassetsGenerator { // MARK: - Helpers: bypass generation private func shouldBypassGeneration(for image: ParsedImage, xcassetImagePath: String) -> Bool { - guard forceGeneration == false else { + if forceGeneration { return false } diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index bfe3af8..d3ff386 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -23,16 +23,6 @@ struct Images: ParsableCommand { static let toolName = "Images" static let defaultExtensionName = "UIImage" - // MARK: - Properties - - var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } - var inputFilenameWithoutExt: String { - URL(fileURLWithPath: options.inputFile) - .deletingPathExtension() - .lastPathComponent - } - // MARK: - Command Options @OptionGroup var options: ImagesOptions @@ -54,6 +44,7 @@ struct Images: ParsableCommand { let inputFolder = URL(fileURLWithPath: options.inputFile) .deletingLastPathComponent() .relativePath + let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration) xcassetsGenerator.generateXcassets(inputPath: inputFolder, imagesToGenerate: imagesToGenerate, @@ -61,10 +52,10 @@ struct Images: ParsableCommand { // Generate extension ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate, - staticVar: options.extensionName == Self.defaultExtensionName, - inputFilename: inputFilenameWithoutExt, + staticVar: options.staticMembers, + inputFilename: options.inputFilenameWithoutExt, extensionName: options.extensionName, - extensionFilePath: extensionFilePath) + extensionFilePath: options.extensionFilePath) print("[\(Self.toolName)] Images generated") @@ -90,7 +81,9 @@ struct Images: ParsableCommand { _ = Images.getSvgConverterPath() // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + guard GeneratorChecker.shouldGenerate(force: options.forceExecution, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { print("[\(Self.toolName)] Images are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Images/ImagesOptions.swift b/Sources/ResgenSwift/Images/ImagesOptions.swift index 7eb5831..b36001d 100644 --- a/Sources/ResgenSwift/Images/ImagesOptions.swift +++ b/Sources/ResgenSwift/Images/ImagesOptions.swift @@ -24,18 +24,33 @@ struct ImagesOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String + @Option(help: "Tell if it will generate static properties or not") + var staticMembers: Bool = false + @Option(help: "Extension name. If not specified, it will generate an UIImage extension. Using default extension name will generate static property.") var extensionName: String = Images.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift") - var extensionSuffix: String = "" + var extensionSuffix: String? } +// MARK: - Computed var -/* - swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \ - --xcassets-path "./Images/imagium.xcassets" \ - --extension-output-path "./Images/Generated" \ - --extension-name "UIImage" \ - --extension-suffix "GenAllScript" - */ +extension ImagesOptions { + var extensionFileName: String { + if let extensionSuffix = extensionSuffix { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } + + var inputFilenameWithoutExt: String { + URL(fileURLWithPath: inputFile) + .deletingPathExtension() + .lastPathComponent + } +} diff --git a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift index 83971fc..0beb811 100644 --- a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift @@ -98,7 +98,7 @@ class StringsFileGenerator { return // Go to next section } - content += "\n\t// MARK: - \(section.name)" + content += "\n // MARK: - \(section.name)" section.definitions.forEach { definition in guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { return // Go to next definition diff --git a/Sources/ResgenSwift/Strings/Stringium/Stringium.swift b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift index 626d5e9..72d577e 100644 --- a/Sources/ResgenSwift/Strings/Stringium/Stringium.swift +++ b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift @@ -24,27 +24,6 @@ struct Stringium: ParsableCommand { static let defaultExtensionName = "String" static let noTranslationTag: String = "notranslation" - // MARK: - Properties - - var extensionFileName: String { - if let extensionSuffix = options.extensionSuffix { - return "\(options.extensionName)+\(extensionSuffix).swift" - } - return "\(options.extensionName).swift" - } - - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } - - var inputFilenameWithoutExt: String { - URL(fileURLWithPath: options.inputFile) - .deletingPathExtension() - .lastPathComponent - } - - var generateStaticVariable: Bool { - options.extensionName == Self.defaultExtensionName - } - // MARK: - Command options @OptionGroup var options: StringiumOptions @@ -68,16 +47,16 @@ struct Stringium: ParsableCommand { defaultLang: options.defaultLang, tags: options.tags, outputPath: options.stringsFileOutputPath, - inputFilenameWithoutExt: inputFilenameWithoutExt) + inputFilenameWithoutExt: options.inputFilenameWithoutExt) // Generate extension StringsFileGenerator.writeExtensionFiles(sections: sections, defaultLang: options.defaultLang, tags: options.tags, - staticVar: generateStaticVariable, - inputFilename: inputFilenameWithoutExt, + staticVar: options.staticMembers, + inputFilename: options.inputFilenameWithoutExt, extensionName: options.extensionName, - extensionFilePath: extensionFilePath) + extensionFilePath: options.extensionFilePath) print("[\(Self.toolName)] Strings generated") } @@ -108,7 +87,9 @@ struct Stringium: ParsableCommand { } // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { print("[\(Self.toolName)] Strings are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift b/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift index 0277aa5..ab9e0cd 100644 --- a/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift +++ b/Sources/ResgenSwift/Strings/Stringium/StringiumOptions.swift @@ -30,6 +30,9 @@ struct StringiumOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String + @Option(help: "Tell if it will generate static properties or not") + var staticMembers: Bool = false + @Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.") var extensionName: String = Stringium.defaultExtensionName @@ -37,6 +40,8 @@ struct StringiumOptions: ParsableArguments { var extensionSuffix: String? } +// MARK: - Private var getter + extension StringiumOptions { var stringsFileOutputPath: String { var outputPath = outputPathRaw @@ -58,3 +63,24 @@ extension StringiumOptions { .map { String($0) } } } + +// MARK: - Computed var + +extension StringiumOptions { + var extensionFileName: String { + if let extensionSuffix = extensionSuffix { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } + + var inputFilenameWithoutExt: String { + URL(fileURLWithPath: inputFile) + .deletingPathExtension() + .lastPathComponent + } +} diff --git a/Sources/ResgenSwift/Strings/Tag/Tags.swift b/Sources/ResgenSwift/Strings/Tag/Tags.swift index 6c9455d..d5fdccd 100644 --- a/Sources/ResgenSwift/Strings/Tag/Tags.swift +++ b/Sources/ResgenSwift/Strings/Tag/Tags.swift @@ -25,19 +25,6 @@ struct Tags: ParsableCommand { static let defaultExtensionName = "Tags" static let noTranslationTag: String = "notranslation" - // MARK: - Properties - - var extensionFileName: String { - if let extensionSuffix = options.extensionSuffix { - return "\(options.extensionName)+\(extensionSuffix).swift" - } - return "\(options.extensionName).swift" - } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } - var generateStaticVariable: Bool { - options.extensionName == Self.defaultExtensionName - } - // MARK: - Command Options @OptionGroup var options: TagsOptions @@ -59,9 +46,9 @@ struct Tags: ParsableCommand { TagsGenerator.writeExtensionFiles(sections: sections, lang: options.lang, tags: ["ios", "iosonly", Self.noTranslationTag], - staticVar: generateStaticVariable, + staticVar: options.staticMembers, extensionName: options.extensionName, - extensionFilePath: extensionFilePath) + extensionFilePath: options.extensionFilePath) print("[\(Self.toolName)] Tags generated") } @@ -79,7 +66,9 @@ struct Tags: ParsableCommand { } // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { print("[\(Self.toolName)] Tags are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift index 4dc643a..3618ce0 100644 --- a/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift +++ b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift @@ -21,9 +21,27 @@ struct TagsOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String + @Option(help: "Tell if it will generate static properties or not") + var staticMembers: Bool = false + @Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.") var extensionName: String = Tags.defaultExtensionName @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift") var extensionSuffix: String? } + +// MARK: - Computed var + +extension TagsOptions { + var extensionFileName: String { + if let extensionSuffix = extensionSuffix { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } +} diff --git a/Sources/ResgenSwift/Strings/Twine/Twine.swift b/Sources/ResgenSwift/Strings/Twine/Twine.swift index 97ae46d..e495a03 100644 --- a/Sources/ResgenSwift/Strings/Twine/Twine.swift +++ b/Sources/ResgenSwift/Strings/Twine/Twine.swift @@ -24,13 +24,6 @@ struct Twine: ParsableCommand { static let defaultExtensionName = "String" static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine" - // MARK: - Properties - - var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile) - .deletingPathExtension() - .lastPathComponent - } - // MARK: - Command Options @OptionGroup var options: TwineOptions @@ -50,17 +43,16 @@ struct Twine: ParsableCommand { Shell.shell(Self.twineExecutable, "generate-localization-file", options.inputFile, "--lang", "\(lang)", - "\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings", + "\(options.outputPath)/\(lang).lproj/\(options.inputFilenameWithoutExt).strings", "--tags=ios,iosonly,iosOnly") } // Generate extension - var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" } Shell.shell(Self.twineExecutable, "generate-localization-file", options.inputFile, "--format", "apple-swift", "--lang", "\(options.defaultLang)", - extensionFilePath, + options.extensionFilePath, "--tags=ios,iosonly,iosOnly") print("[\(Self.toolName)] Strings generated") @@ -91,11 +83,10 @@ struct Twine: ParsableCommand { Twine.exit(withError: error) } - // "R2String+" is hardcoded in Twine formatter - let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift" - // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else { + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePathGenerated) else { print("[\(Self.toolName)] Strings are already up to date :) ") return false } diff --git a/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift b/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift index 4a3752a..3d79aff 100644 --- a/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift +++ b/Sources/ResgenSwift/Strings/Twine/TwineOptions.swift @@ -28,6 +28,8 @@ struct TwineOptions: ParsableArguments { var extensionOutputPath: String } +// MARK: - Private var getter + extension TwineOptions { var langs: [String] { langsRaw @@ -35,3 +37,22 @@ extension TwineOptions { .map { String($0) } } } + +// MARK: - Computed var + +extension TwineOptions { + var inputFilenameWithoutExt: String { + URL(fileURLWithPath: inputFile) + .deletingPathExtension() + .lastPathComponent + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(inputFilenameWithoutExt).swift" + } + + // "R2String+" is hardcoded in Twine formatter + var extensionFilePathGenerated: String { + "\(extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift" + } +} -- 2.39.5 From e9bc779da612c5404af507005e9c48516fbc696d Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Wed, 31 Aug 2022 16:42:22 +0200 Subject: [PATCH 03/10] Add --project-directory option to generate command to easily use relative path --- README.md | 14 +++++ Sources/ResgenSwift/Colors/Colors.swift | 2 +- .../Generator/ColorExtensionGenerator.swift | 2 +- .../Colors/Generator/ColorXcassetHelper.swift | 5 +- .../ResgenSwift/Fonts/FontsToolHelper.swift | 2 +- .../Generator/FontToolContentGenerator.swift | 2 +- .../Extensions/StringExtensions.swift | 18 +++++++ Sources/ResgenSwift/Generate/Generate.swift | 8 ++- .../Generate/GenerateOptions.swift | 9 ++++ ...ColorsConfiguration+ShellCommandable.swift | 15 +++--- .../FontsConfiguration+ShellCommandable.swift | 6 +-- ...ImagesConfiguration+ShellCommandable.swift | 10 ++-- .../Generate/Runnable/Runnable.swift | 2 +- ...tringsConfiguration+ShellCommandable.swift | 10 ++-- .../TagsConfiguration+ShellCommandable.swift | 8 +-- .../Generator/ImageExtensionGenerator.swift | 4 +- .../Images/Generator/XcassetsGenerator.swift | 20 ++++--- Sources/ResgenSwift/Images/Images.swift | 2 +- .../Generator/StringsFileGenerator.swift | 2 +- .../Strings/Generator/TagsGenerator.swift | 2 +- Sources/ResgenSwift/Strings/Twine/Twine.swift | 8 +-- Sources/ToolCore/Shell.swift | 52 ++++++++++++------- 22 files changed, 136 insertions(+), 67 deletions(-) create mode 100644 Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift diff --git a/README.md b/README.md index 81fb67f..6808906 100644 --- a/README.md +++ b/README.md @@ -246,4 +246,18 @@ tags: [] ... ``` +### Usage +If you **don't** install ResgenSwift: + +```sh +swift run -c release ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} +``` + +If you install ResgenSwift: + +```sh +resgen-swift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} +``` + +> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`) \ No newline at end of file diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 9d0da92..8c19054 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -92,7 +92,7 @@ struct Colors: ParsableCommand { // MARK: - Helpers private func deleteCurrentColors() { - Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*") + Shell.shell(["rm", "-rf", "\(options.xcassetsPath)/Colors/*"]) } } diff --git a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift index 9e0d17b..28a0426 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift @@ -17,7 +17,7 @@ struct ColorExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift index 1ec0dad..34cd3b1 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift @@ -20,11 +20,12 @@ struct ColorXcassetHelper { private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) { // Create ColorSet let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset" - Shell.shell("mkdir", "-p", "\(colorSetPath)") + Shell.shell(["mkdir", + "-p", "\(colorSetPath)"]) // Create Contents.json in ColorSet let contentsJsonPath = "\(colorSetPath)/Contents.json" - Shell.shell("touch", "\(contentsJsonPath)") + Shell.shell(["touch", "\(contentsJsonPath)"]) // Write content in Contents.json let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath) diff --git a/Sources/ResgenSwift/Fonts/FontsToolHelper.swift b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift index 3cfc99b..686db9f 100644 --- a/Sources/ResgenSwift/Fonts/FontsToolHelper.swift +++ b/Sources/ResgenSwift/Fonts/FontsToolHelper.swift @@ -60,7 +60,7 @@ class FontsToolHelper { private static func getFontName(atPath path: String) -> String { //print("fc-scan --format %{postscriptname} \(path)") // Get real font name - let task = Shell.shell("fc-scan", "--format", "%{postscriptname}", path) + let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path]) guard let fontName = task.output, task.terminationStatus == 0 else { let error = FontsToolError.fcScan(path, task.terminationStatus, task.output) diff --git a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift index 6089cf0..490408e 100644 --- a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift @@ -14,7 +14,7 @@ class FontExtensionGenerator { // Check file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift b/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift new file mode 100644 index 0000000..7d0468f --- /dev/null +++ b/Sources/ResgenSwift/Generate/Extensions/StringExtensions.swift @@ -0,0 +1,18 @@ +// +// StringExtensions.swift +// +// +// Created by Thibaut Schmitt on 31/08/2022. +// + +import Foundation + +extension String { + + func prependIfRelativePath(_ prependPath: String) -> String { + if self.hasPrefix("/") { + return self + } + return prependPath + self + } +} diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift index 1d91f88..fd9c1b5 100644 --- a/Sources/ResgenSwift/Generate/Generate.swift +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -40,13 +40,17 @@ struct Generate: ParsableCommand { print(" - \(configuration.strings.count) strings configuration") print(" - \(configuration.tags.count) tags configuration") print() + + print("Input file: \(configuration.colors.first?.inputFile ?? "no input file")") + // Execute commands configuration.runnableConfigurations .forEach { - $0.run(force: options.forceGeneration) + $0.run(projectDirectory: options.projectDirectory, + force: options.forceGeneration) print("\n") } - + print("[\(Self.toolName)] Resgen ended") } } diff --git a/Sources/ResgenSwift/Generate/GenerateOptions.swift b/Sources/ResgenSwift/Generate/GenerateOptions.swift index a457828..bedf445 100644 --- a/Sources/ResgenSwift/Generate/GenerateOptions.swift +++ b/Sources/ResgenSwift/Generate/GenerateOptions.swift @@ -16,4 +16,13 @@ struct GenerateOptions: ParsableArguments { @Argument(help: "Configuration file.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var configurationFile: String + + @Option(help: "Project directory. It will be added to every relative path (path that does not start with `/`", + transform: { + if $0.last == "/" { + return $0 + } + return $0 + "/" + }) + var projectDirectory: String } diff --git a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift index 390e177..74850c3 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// ColorsConfiguration+ShellCommandable.swift +// ColorsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension ColorsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,13 +16,13 @@ extension ColorsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--style", style, "--xcassets-path", - xcassetsPath, + xcassetsPath.prependIfRelativePath(projectDirectory), "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] @@ -39,7 +39,8 @@ extension ColorsConfiguration: Runnable { extensionSuffix ] } - - Colors.main(args) + print("Colors args:") + dump(args) + //Colors.main(args) } } diff --git a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift index ece931a..6b62239 100644 --- a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// FontsConfiguration+ShellCommandable.swift +// FontsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension FontsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -18,7 +18,7 @@ extension FontsConfiguration: Runnable { args += [ inputFile, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift index 837bbf7..a99fd16 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// ImagesConfiguration+ShellCommandable.swift +// ImagesConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension ImagesConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,11 +16,11 @@ extension ImagesConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--xcassets-path", - xcassetsPath, + xcassetsPath.prependIfRelativePath(projectDirectory), "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/Runnable.swift index 5b8c46f..8e2d937 100644 --- a/Sources/ResgenSwift/Generate/Runnable/Runnable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/Runnable.swift @@ -8,6 +8,6 @@ import Foundation protocol Runnable { - func run(force: Bool) + func run(projectDirectory: String, force: Bool) } diff --git a/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift index ae60f95..d573569 100644 --- a/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/StringsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// StringsConfiguration+ShellCommandable.swift +// StringsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension StringsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,15 +16,15 @@ extension StringsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--output-path", - outputPath, + outputPath.prependIfRelativePath(projectDirectory), "--langs", langs, "--default-lang", defaultLang, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift index efab0d2..04c6648 100644 --- a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+ShellCommandable.swift @@ -1,5 +1,5 @@ // -// TagsConfiguration+ShellCommandable.swift +// TagsConfiguration+Runnable.swift // // // Created by Thibaut Schmitt on 30/08/2022. @@ -8,7 +8,7 @@ import Foundation extension TagsConfiguration: Runnable { - func run(force: Bool) { + func run(projectDirectory: String, force: Bool) { var args = [String]() if force { @@ -16,11 +16,11 @@ extension TagsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--lang", lang, "--extension-output-path", - extensionOutputPath, + extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] diff --git a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift index 1d34c96..7778d32 100644 --- a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift @@ -34,7 +34,7 @@ class ImageExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Generate extension @@ -47,7 +47,7 @@ class ImageExtensionGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift index 8c5fe8f..d456bef 100644 --- a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift @@ -55,7 +55,7 @@ class XcassetsGenerator { // Create imageset folder let imagesetName = "\(parsedImage.name).imageset" let imagesetPath = "\(xcassetsPath)/\(imagesetName)" - Shell.shell("mkdir", "-p", imagesetPath) + Shell.shell(["mkdir", "-p", imagesetPath]) // Store managed images path generatedAssetsPaths.append(imagesetName) @@ -96,16 +96,22 @@ class XcassetsGenerator { // convert path/to/image.png -resize 200x300 path/to/output.png // convert path/to/image.png -resize 200x path/to/output.png // convert path/to/image.png -resize x300 path/to/output.png - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x) - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x) - Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", + output1x]) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", + output2x]) + Shell.shell(["convert", "\(imageData.path)", + "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", + output3x]) } // Write Content.json let imagesetContentJson = parsedImage.contentJson let contentJsonFilePath = "\(imagesetPath)/Contents.json" if fileManager.fileExists(atPath: contentJsonFilePath) == false { - Shell.shell("touch", "\(contentJsonFilePath)") + Shell.shell(["touch", "\(contentJsonFilePath)"]) } let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath) @@ -160,8 +166,8 @@ class XcassetsGenerator { } // Info unavailable -> do not bypass - let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath) - let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath) + let taskWidth = Shell.shell(["identify", "-format", "%w", xcassetImagePath]) + let taskHeight = Shell.shell(["identify", "-format", "%h", xcassetImagePath]) guard taskWidth.terminationStatus == 0, taskHeight.terminationStatus == 0 else { return false diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index d3ff386..9f8d8d3 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -95,7 +95,7 @@ struct Images: ParsableCommand { @discardableResult static func getSvgConverterPath() -> String { - let taskSvgConverter = Shell.shell("which", "rsvg-convert") + let taskSvgConverter = Shell.shell(["which", "rsvg-convert"]) if taskSvgConverter.terminationStatus == 0 { return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines) } diff --git a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift index 0beb811..6e0f697 100644 --- a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift @@ -118,7 +118,7 @@ class StringsFileGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift index 7b3bbd7..38a1a9f 100644 --- a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -38,7 +38,7 @@ class TagsGenerator { // Create file if not exists let fileManager = FileManager() if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + Shell.shell(["touch", "\(extensionFilePath)"]) } // Create extension content diff --git a/Sources/ResgenSwift/Strings/Twine/Twine.swift b/Sources/ResgenSwift/Strings/Twine/Twine.swift index e495a03..2a48bac 100644 --- a/Sources/ResgenSwift/Strings/Twine/Twine.swift +++ b/Sources/ResgenSwift/Strings/Twine/Twine.swift @@ -40,20 +40,20 @@ struct Twine: ParsableCommand { // Generate strings files (lproj files) for lang in options.langs { - Shell.shell(Self.twineExecutable, + Shell.shell([Self.twineExecutable, "generate-localization-file", options.inputFile, "--lang", "\(lang)", "\(options.outputPath)/\(lang).lproj/\(options.inputFilenameWithoutExt).strings", - "--tags=ios,iosonly,iosOnly") + "--tags=ios,iosonly,iosOnly"]) } // Generate extension - Shell.shell(Self.twineExecutable, + Shell.shell([Self.twineExecutable, "generate-localization-file", options.inputFile, "--format", "apple-swift", "--lang", "\(options.defaultLang)", options.extensionFilePath, - "--tags=ios,iosonly,iosOnly") + "--tags=ios,iosonly,iosOnly"]) print("[\(Self.toolName)] Strings generated") } diff --git a/Sources/ToolCore/Shell.swift b/Sources/ToolCore/Shell.swift index aa7c3bc..0bcefdc 100644 --- a/Sources/ToolCore/Shell.swift +++ b/Sources/ToolCore/Shell.swift @@ -9,32 +9,48 @@ import Foundation public class Shell { - @discardableResult - public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) { - let task = Process() - task.launchPath = launchPath - task.arguments = args - - let pipe = Pipe() - task.standardOutput = pipe - try? task.run() - task.waitUntilExit() - - let data = pipe.fileHandleForReading.readDataToEndOfFile() - - guard let output: String = String(data: data, encoding: .utf8) else { - return (terminationStatus: task.terminationStatus, output: nil) - } - - return (terminationStatus: task.terminationStatus, output: output) + public static var environment: [String: String] { + ProcessInfo.processInfo.environment } +// @discardableResult +// public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) { +// let task = Process() +// task.launchPath = launchPath +// task.arguments = args +// +// var currentEnv = ProcessInfo.processInfo.environment +// for (key, value) in environment { +// currentEnv[key] = value +// } +// task.environment = currentEnv +// +// let pipe = Pipe() +// task.standardOutput = pipe +// try? task.run() +// task.waitUntilExit() +// +// let data = pipe.fileHandleForReading.readDataToEndOfFile() +// +// guard let output: String = String(data: data, encoding: .utf8) else { +// return (terminationStatus: task.terminationStatus, output: nil) +// } +// +// return (terminationStatus: task.terminationStatus, output: output) +// } + @discardableResult public static func shell(launchPath: String = "/usr/bin/env", _ args: [String]) -> (terminationStatus: Int32, output: String?) { let task = Process() task.launchPath = launchPath task.arguments = args + var currentEnv = ProcessInfo.processInfo.environment + for (key, value) in environment { + currentEnv[key] = value + } + task.environment = currentEnv + let pipe = Pipe() task.standardOutput = pipe task.launch() -- 2.39.5 From 87230197328023e2d528de61ee8924dd6d56478c Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Thu, 1 Sep 2022 15:59:16 +0200 Subject: [PATCH 04/10] Renable Colors generation + add print to each command to identify which configuration is used --- Sources/ResgenSwift/Colors/Colors.swift | 3 +-- Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift | 2 +- Sources/ResgenSwift/Fonts/Fonts.swift | 1 + .../Runnable/ColorsConfiguration+ShellCommandable.swift | 5 ++--- .../Runnable/FontsConfiguration+ShellCommandable.swift | 2 +- Sources/ResgenSwift/Images/Images.swift | 1 + Sources/ResgenSwift/Strings/Stringium/Stringium.swift | 1 + Sources/ResgenSwift/Strings/Tag/Tags.swift | 3 ++- 8 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 8c19054..1a26778 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -32,8 +32,7 @@ struct Colors: ParsableCommand { public func run() throws { print("[\(Self.toolName)] Starting colors generation") - print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate \(options.colorStyle) colors in xcassets \(options.xcassetsPath)") - + // Check requirements guard checkRequirements() else { return } diff --git a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift index 653006b..3f02bb1 100644 --- a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift +++ b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift @@ -23,7 +23,7 @@ class ColorFileParser { .replacingOccurrences(of: "=", with: "") // Keep compat with current file format guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { - print("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") + debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") return nil } diff --git a/Sources/ResgenSwift/Fonts/Fonts.swift b/Sources/ResgenSwift/Fonts/Fonts.swift index 671b477..3fa963d 100644 --- a/Sources/ResgenSwift/Fonts/Fonts.swift +++ b/Sources/ResgenSwift/Fonts/Fonts.swift @@ -31,6 +31,7 @@ struct Fonts: ParsableCommand { public func run() throws { print("[\(Self.toolName)] Starting fonts generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate fonts") // Check requirements guard checkRequirements() else { return } diff --git a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift index 74850c3..49639b0 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+ShellCommandable.swift @@ -39,8 +39,7 @@ extension ColorsConfiguration: Runnable { extensionSuffix ] } - print("Colors args:") - dump(args) - //Colors.main(args) + + Colors.main(args) } } diff --git a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift index 6b62239..4ef8aa2 100644 --- a/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/FontsConfiguration+ShellCommandable.swift @@ -16,7 +16,7 @@ extension FontsConfiguration: Runnable { } args += [ - inputFile, + inputFile.prependIfRelativePath(projectDirectory), "--extension-output-path", extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index 9f8d8d3..0d26779 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -31,6 +31,7 @@ struct Images: ParsableCommand { mutating func run() { print("[\(Self.toolName)] Starting images generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate images in xcassets \(options.xcassetsPath)") // Check requirements guard checkRequirements() else { return } diff --git a/Sources/ResgenSwift/Strings/Stringium/Stringium.swift b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift index 72d577e..ca750c8 100644 --- a/Sources/ResgenSwift/Strings/Stringium/Stringium.swift +++ b/Sources/ResgenSwift/Strings/Stringium/Stringium.swift @@ -32,6 +32,7 @@ struct Stringium: ParsableCommand { mutating func run() { print("[\(Self.toolName)] Starting strings generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate strings for \(options.langs) (default lang: \(options.defaultLang)") // Check requirements guard checkRequirements() else { return } diff --git a/Sources/ResgenSwift/Strings/Tag/Tags.swift b/Sources/ResgenSwift/Strings/Tag/Tags.swift index d5fdccd..5e53208 100644 --- a/Sources/ResgenSwift/Strings/Tag/Tags.swift +++ b/Sources/ResgenSwift/Strings/Tag/Tags.swift @@ -32,7 +32,8 @@ struct Tags: ParsableCommand { // MARK: - Run mutating func run() { - print("[\(Self.toolName)] Starting tagss generation") + print("[\(Self.toolName)] Starting tags generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate strings for lang: \(options.lang)") // Check requirements guard checkRequirements() else { return } -- 2.39.5 From e3258453bb04667525f977e184e5c5a8523f8535 Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Fri, 2 Sep 2022 15:57:20 +0200 Subject: [PATCH 05/10] Remove Shell invocation as many as possible (high cost in term of speed of execution) --- Sources/ResgenSwift/Colors/Colors.swift | 24 ++++++-- .../ResgenSwift/Colors/ColorsToolError.swift | 10 +++- .../Generator/ColorExtensionGenerator.swift | 8 +-- .../Colors/Generator/ColorXcassetHelper.swift | 19 ++++-- .../Colors/Parser/ColorFileParser.swift | 2 +- .../Generator/FontToolContentGenerator.swift | 8 +-- Sources/ResgenSwift/Generate/Generate.swift | 3 +- ...ImagesConfiguration+ShellCommandable.swift | 2 +- .../Generator/ImageExtensionGenerator.swift | 14 +---- .../Images/Generator/XcassetsGenerator.swift | 60 ++++++------------- Sources/ResgenSwift/Images/ImagesError.swift | 4 ++ .../Generator/StringsFileGenerator.swift | 10 +--- .../Strings/Generator/TagsGenerator.swift | 8 +-- .../Strings/Model/Definition.swift | 1 - Sources/ToolCore/GeneratorChecker.swift | 41 +++++++------ 15 files changed, 95 insertions(+), 119 deletions(-) diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 1a26778..1f161f2 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -40,21 +40,24 @@ struct Colors: ParsableCommand { // Delete current colors deleteCurrentColors() - + // Get colors to generate let parsedColors = ColorFileParser.parse(options.inputFile, colorStyle: options.colorStyle) - + // -> Time: 0.0020350217819213867 seconds + // Generate all colors in xcassets ColorXcassetHelper.generateXcassetColors(colors: parsedColors, to: options.xcassetsPath) - + // -> Time: 3.4505380392074585 seconds + // Generate extension ColorExtensionGenerator.writeExtensionFile(colors: parsedColors, staticVar: options.staticMembers, extensionName: options.extensionName, extensionFilePath: options.extensionFilePath) - + // -> Time: 0.0010340213775634766 seconds + print("[\(Self.toolName)] Colors generated") } @@ -91,7 +94,18 @@ struct Colors: ParsableCommand { // MARK: - Helpers private func deleteCurrentColors() { - Shell.shell(["rm", "-rf", "\(options.xcassetsPath)/Colors/*"]) + let fileManager = FileManager() + let assetsColorPath = "\(options.xcassetsPath)/Colors" + + if fileManager.fileExists(atPath: assetsColorPath) { + do { + try fileManager.removeItem(atPath: assetsColorPath) + } catch { + let error = ColorsToolError.deleteExistingColors("\(options.xcassetsPath)/Colors") + print(error.localizedDescription) + Colors.exit(withError: error) + } + } } } diff --git a/Sources/ResgenSwift/Colors/ColorsToolError.swift b/Sources/ResgenSwift/Colors/ColorsToolError.swift index 7bfc860..a4310e5 100644 --- a/Sources/ResgenSwift/Colors/ColorsToolError.swift +++ b/Sources/ResgenSwift/Colors/ColorsToolError.swift @@ -10,9 +10,11 @@ import Foundation enum ColorsToolError: Error { case badFormat(String) case writeAsset(String) + case createAssetFolder(String) case writeExtension(String, String) case fileNotExists(String) case badColorDefinition(String, String) + case deleteExistingColors(String) var description: String { switch self { @@ -21,6 +23,9 @@ enum ColorsToolError: Error { case .writeAsset(let info): return "error:[\(Colors.toolName)] An error occured while writing color in Xcasset: \(info)" + + case .createAssetFolder(let assetsFolder): + return "error:[\(Colors.toolName)] An error occured while creating colors folder `\(assetsFolder)`" case .writeExtension(let filename, let info): return "error:[\(Colors.toolName)] An error occured while writing extension in \(filename): \(info)" @@ -29,7 +34,10 @@ enum ColorsToolError: Error { return "error:[\(Colors.toolName)] File \(filename) does not exists" case .badColorDefinition(let lightColor, let darkColor): - return "error:[\(Colors.toolName)]One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-" + return "error:[\(Colors.toolName)] One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-" + + case .deleteExistingColors(let assetsFolder): + return "error:[\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`" } } } diff --git a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift index 28a0426..4dd4642 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift @@ -14,12 +14,6 @@ struct ColorExtensionGenerator { let extensionClassname: String static func writeExtensionFile(colors: [ParsedColor], staticVar: Bool, extensionName: String, extensionFilePath: String) { - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Create extension content let extensionContent = [ Self.getHeader(extensionClassname: extensionName), @@ -31,7 +25,7 @@ struct ColorExtensionGenerator { // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { - try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift index 34cd3b1..aa4c0db 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorXcassetHelper.swift @@ -20,17 +20,24 @@ struct ColorXcassetHelper { private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) { // Create ColorSet let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset" - Shell.shell(["mkdir", - "-p", "\(colorSetPath)"]) - - // Create Contents.json in ColorSet let contentsJsonPath = "\(colorSetPath)/Contents.json" - Shell.shell(["touch", "\(contentsJsonPath)"]) + + let fileManager = FileManager() + if fileManager.fileExists(atPath: colorSetPath) == false { + do { + try fileManager.createDirectory(atPath: colorSetPath, + withIntermediateDirectories: true) + } catch { + let error = ColorsToolError.createAssetFolder(colorSetPath) + print(error.localizedDescription) + Colors.exit(withError: error) + } + } // Write content in Contents.json let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath) do { - try color.contentsJSON().write(to: contentsJsonPathURL, atomically: true, encoding: .utf8) + try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = ColorsToolError.writeAsset(error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift index 3f02bb1..188fffb 100644 --- a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift +++ b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift @@ -23,7 +23,7 @@ class ColorFileParser { .replacingOccurrences(of: "=", with: "") // Keep compat with current file format guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { - debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") + // debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") return nil } diff --git a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift index 490408e..7e8a472 100644 --- a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift @@ -11,12 +11,6 @@ import ToolCore class FontExtensionGenerator { static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { - // Check file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Create extension content let extensionContent = [ Self.getHeader(extensionClassname: extensionName), @@ -29,7 +23,7 @@ class FontExtensionGenerator { // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { - try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift index fd9c1b5..54ffc43 100644 --- a/Sources/ResgenSwift/Generate/Generate.swift +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -46,9 +46,10 @@ struct Generate: ParsableCommand { // Execute commands configuration.runnableConfigurations .forEach { + let begin = Date() $0.run(projectDirectory: options.projectDirectory, force: options.forceGeneration) - print("\n") + print("Took: \(Date().timeIntervalSince(begin))s\n") } print("[\(Self.toolName)] Resgen ended") diff --git a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift index a99fd16..db1bb4d 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+ShellCommandable.swift @@ -12,7 +12,7 @@ extension ImagesConfiguration: Runnable { var args = [String]() if force { - args += ["-F"] // Images has a -f and -F options + args += ["-f"] // Images has a -f and -F options } args += [ diff --git a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift index 7778d32..73f6e4d 100644 --- a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift @@ -31,12 +31,6 @@ class ImageExtensionGenerator { return content }() - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Generate extension Self.generateExtensionFile(extensionFilePath: extensionFilePath, extensionHeader, extensionContent, extensionFooter) } @@ -44,19 +38,13 @@ class ImageExtensionGenerator { // MARK: - pragm private static func generateExtensionFile(extensionFilePath: String, _ args: String...) { - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Create extension content let extensionContent = args.joined(separator: "\n") // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { - try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = ImagesError.writeFile(extensionFilePath, error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift index d456bef..6573c92 100644 --- a/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/XcassetsGenerator.swift @@ -52,10 +52,9 @@ class XcassetsGenerator { Images.exit(withError: error) }() - // Create imageset folder + // Create imageset folder name let imagesetName = "\(parsedImage.name).imageset" let imagesetPath = "\(xcassetsPath)/\(imagesetName)" - Shell.shell(["mkdir", "-p", imagesetPath]) // Store managed images path generatedAssetsPaths.append(imagesetName) @@ -66,11 +65,23 @@ class XcassetsGenerator { let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)" // Check if we need to convert image - if self.shouldBypassGeneration(for: parsedImage, xcassetImagePath: output1x) { - print("\(parsedImage.name) -> Not regenerating") + guard self.shouldGenerate(inputImagePath: imageData.path, xcassetImagePath: output1x) else { + //print("\(parsedImage.name) -> Not regenerating") return } + // Create imageset folder + if fileManager.fileExists(atPath: imagesetPath) == false { + do { + try fileManager.createDirectory(atPath: imagesetPath, + withIntermediateDirectories: true) + } catch { + let error = ImagesError.createAssetFolder(imagesetPath) + print(error.localizedDescription) + Images.exit(withError: error) + } + } + // Convert image let convertArguments = parsedImage.convertArguments if imageData.ext == "svg" { @@ -110,12 +121,9 @@ class XcassetsGenerator { // Write Content.json let imagesetContentJson = parsedImage.contentJson let contentJsonFilePath = "\(imagesetPath)/Contents.json" - if fileManager.fileExists(atPath: contentJsonFilePath) == false { - Shell.shell(["touch", "\(contentJsonFilePath)"]) - } let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath) - try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8) + try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: false, encoding: .utf8) print("\(parsedImage.name) -> Generated") } @@ -153,43 +161,11 @@ class XcassetsGenerator { // MARK: - Helpers: bypass generation - private func shouldBypassGeneration(for image: ParsedImage, xcassetImagePath: String) -> Bool { + private func shouldGenerate(inputImagePath: String, xcassetImagePath: String) -> Bool { if forceGeneration { - return false - } - - let fileManager = FileManager() - - // File not exists -> do not bypass - guard fileManager.fileExists(atPath: xcassetImagePath) else { - return false - } - - // Info unavailable -> do not bypass - let taskWidth = Shell.shell(["identify", "-format", "%w", xcassetImagePath]) - let taskHeight = Shell.shell(["identify", "-format", "%h", xcassetImagePath]) - guard taskWidth.terminationStatus == 0, - taskHeight.terminationStatus == 0 else { - return false - } - - let currentWidth = Int(taskWidth.output ?? "-1") ?? -1 - let currentheight = Int(taskHeight.output ?? "-1") ?? -1 - - // Info unavailable -> do not bypass - guard currentWidth > 0 && currentheight > 0 else { - return false - } - - // Check width and height - if image.width != -1 && currentWidth == image.width { - return true - } - if image.height != -1 && currentheight == image.height { return true } - return false + return GeneratorChecker.isFile(inputImagePath, moreRecenThan: xcassetImagePath) } - } diff --git a/Sources/ResgenSwift/Images/ImagesError.swift b/Sources/ResgenSwift/Images/ImagesError.swift index 8afb628..a425fc4 100644 --- a/Sources/ResgenSwift/Images/ImagesError.swift +++ b/Sources/ResgenSwift/Images/ImagesError.swift @@ -14,6 +14,7 @@ enum ImagesError: Error { case getFileAttributed(String, String) case rsvgConvertNotFound case writeFile(String, String) + case createAssetFolder(String) case unknown(String) var localizedDescription: String { @@ -35,6 +36,9 @@ enum ImagesError: Error { case .writeFile(let subErrorDescription, let filename): return " error:[\(Images.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)" + + case .createAssetFolder(let folder): + return "error:[\(Colors.toolName)] An error occured while creating folder `\(folder)`" case .unknown(let errorDescription): return " error:[\(Images.toolName)] Unknown error: \(errorDescription)" diff --git a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift index 6e0f697..1ac694c 100644 --- a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift @@ -28,7 +28,7 @@ class StringsFileGenerator { let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings" let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath) do { - try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8) + try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath) print(error.localizedDescription) @@ -115,19 +115,13 @@ class StringsFileGenerator { return content }() - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Create extension content let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n") // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { - try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift index 38a1a9f..888aafb 100644 --- a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -35,19 +35,13 @@ class TagsGenerator { return content }() - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell(["touch", "\(extensionFilePath)"]) - } - // Create extension content let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n") // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { - try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) } catch (let error) { let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription) print(error.localizedDescription) diff --git a/Sources/ResgenSwift/Strings/Model/Definition.swift b/Sources/ResgenSwift/Strings/Model/Definition.swift index c600bdf..b7dbe44 100644 --- a/Sources/ResgenSwift/Strings/Model/Definition.swift +++ b/Sources/ResgenSwift/Strings/Model/Definition.swift @@ -53,7 +53,6 @@ class Definition { guard let match = match else { return } if let range = Range(match.range, in: input), let last = input[range].last { - debugPrint("Found: \(input[range])") switch last { case "d", "u": methodsParameters.append("Int") diff --git a/Sources/ToolCore/GeneratorChecker.swift b/Sources/ToolCore/GeneratorChecker.swift index 858f7af..f96c6a8 100644 --- a/Sources/ToolCore/GeneratorChecker.swift +++ b/Sources/ToolCore/GeneratorChecker.swift @@ -15,26 +15,29 @@ public class GeneratorChecker { return true } - // If inputFile is newer that generated extension -> Regenerate - let extensionFileURL = URL(fileURLWithPath: extensionFilePath) - let inputFileURL = URL(fileURLWithPath: inputFilePath) - - let extensionRessourceValues = try? extensionFileURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey]) - let inputFileRessourceValues = try? inputFileURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey]) - - if let extensionModificationDate = extensionRessourceValues?.contentModificationDate, - let inputFileModificationDate = inputFileRessourceValues?.contentModificationDate { - if inputFileModificationDate >= extensionModificationDate { - print("Input file is newer that generated extension.") - return true - } else { - return false - } - } + return Self.isFile(inputFilePath, moreRecenThan: extensionFilePath) + } - // ModificationDate not available for both file - print("⚠️ Could not compare file modication date. ⚠️") - return true + public static func isFile(_ fileOne: String, moreRecenThan fileTwo: String) -> Bool { + let fileOneURL = URL(fileURLWithPath: fileOne) + let fileTwoURL = URL(fileURLWithPath: fileTwo) + + let fileOneRessourceValues = try? fileOneURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey]) + let fileTwoRessourceValues = try? fileTwoURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey]) + + guard let fileOneModificationDate = fileOneRessourceValues?.contentModificationDate, + let fileTwoModificationDate = fileTwoRessourceValues?.contentModificationDate else { + print("⚠️ Could not compare file modication date. ⚠️ (assume than file is newer)") + // Date not available -> assume than fileOne is newer than fileTwo + return true + } + + if fileOneModificationDate >= fileTwoModificationDate { + debugPrint("File one is more recent than file two.") + return true + } + + return false } } -- 2.39.5 From 38036090e1aec5c099036f6f80572a22b8166fec Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Fri, 2 Sep 2022 16:48:37 +0200 Subject: [PATCH 06/10] Add install script and update version to 1.0 --- Sources/ToolCore/Version.swift | 2 +- install.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100755 install.sh diff --git a/Sources/ToolCore/Version.swift b/Sources/ToolCore/Version.swift index f65a192..45c18f8 100644 --- a/Sources/ToolCore/Version.swift +++ b/Sources/ToolCore/Version.swift @@ -7,4 +7,4 @@ import Foundation -public let ResgenSwiftVersion = "0.9" +public let ResgenSwiftVersion = "1.0" diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..361f03a --- /dev/null +++ b/install.sh @@ -0,0 +1,4 @@ +#!/bin/zsh + +swift build -c release +cp .build/release/ResgenSwift /usr/local/bin/ResgenSwift \ No newline at end of file -- 2.39.5 From 80ff2dda048f04bb8728e9ec3e5daa56f060438f Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Mon, 5 Sep 2022 10:20:03 +0200 Subject: [PATCH 07/10] Update Jenkinsfile --- Jenkinsfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 72e3771..8409a21 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,8 @@ library "openiumpipeline" -//env.DEVELOPER_DIR="/Applications/Xcode_12.4.app/Contents/Developer" +env.DEVELOPER_DIR="/Applications/Xcode_13.3.0.app/Contents/Developer" //env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-" +env.IS_PACKAGE_SWIFT=1 +env.TARGETS_MACOS=1 iOSpipeline() -- 2.39.5 From b4ced6911c4bb389b5a266648cd25743c0ac6662 Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Mon, 5 Sep 2022 10:52:45 +0200 Subject: [PATCH 08/10] Update Readme and enable test target --- Package.swift | 39 ++-------------- README.md | 45 ++++++++++--------- ...nCLITests.swift => ResgenSwiftTests.swift} | 4 +- 3 files changed, 29 insertions(+), 59 deletions(-) rename Tests/ResgenSwiftTests/{ResgenCLITests.swift => ResgenSwiftTests.swift} (94%) diff --git a/Package.swift b/Package.swift index db4989d..477d2be 100644 --- a/Package.swift +++ b/Package.swift @@ -23,45 +23,12 @@ let package = Package( ] ), -// .executableTarget( -// name: "FontTool", -// dependencies: [ -// "ToolCore", -// .product(name: "ArgumentParser", package: "swift-argument-parser") -// ] -// ), -// -// .executableTarget( -// name: "ColorTool", -// dependencies: [ -// "ToolCore", -// .product(name: "ArgumentParser", package: "swift-argument-parser") -// ] -// ), -// -// .executableTarget( -// name: "Strings", -// dependencies: [ -// "ToolCore", -// .product(name: "ArgumentParser", package: "swift-argument-parser") -// ], -// sources: ["."] // Force include all subdirectories -// ), -// -// .executableTarget( -// name: "Imagium", -// dependencies: [ -// "ToolCore", -// .product(name: "ArgumentParser", package: "swift-argument-parser") -// ] -// ), - // Helper targets .target(name: "ToolCore"), // Test targets -// .testTarget( -// name: "ResgenSwiftTests", -// dependencies: ["ResgenSwift"]), + .testTarget( + name: "ResgenSwiftTests", + dependencies: ["ResgenSwift"]), ] ) diff --git a/README.md b/README.md index 6808906..d8c9624 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ iOS required to use the **real name** of the font, this name can be different fr **Example** -``` -swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \ +```sh +swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \ --extension-output-path "./Fonts/Generated" \ --extension-name "AppFont" \ --extension-suffix "GreatApp" \ @@ -33,8 +33,8 @@ swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \ Colors generator generates an extension of `UIColor` (or a custom class) along with colorsets in specified xcassets. -``` -swift run -c release ColorTool $FORCE_FLAG "./Colors/colors.txt" \ +```sh +swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \ --style all \ --xcassets-path "./Colors/colors.xcassets" \ --extension-output-path "./Colors/Generated/" \ @@ -60,13 +60,13 @@ Strings command allows to generate `strings` files along with extensions to acce ### Twine (not recommended) -``` -swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \ +```sh +swift run -c release ResgenSwift strings twine $FORCE_FLAG "./Twine/strings.txt" \ --output-path "./Twine/Generated" \ --langs "fr en en-us" \ --default-lang "en" \ --extension-output-path "./Twine/Generated" - ``` +``` **Parameters** @@ -78,8 +78,8 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \ ### Stringium (recommended) -``` -swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \ +```sh +swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/strings.txt" \ --output-path "./Strings/Generated" \ --langs "fr en en-us" \ --default-lang "en" \ @@ -105,8 +105,8 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \ Tags is also a subcommand of `Strings`. Input files are formatted the same way. Tags will generate properties which return exactly what is specified in the input file. It was designed to be used for analytics purpose and to be shared with any other platform to have the same analytics keys. -``` -swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \ +```sh +swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \ --lang "ium" \ --extension-output-path "./Tags/Generated" \ --extension-name "AppTags" \ @@ -130,8 +130,8 @@ swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \ Images generator will generate images assets along with extensions to access those images easily. -``` -swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \ +```sh +swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \ --xcassets-path "./Images/app.xcassets" \ --extension-output-path "./Images/Generated" \ --extension-name "AppImage" \ @@ -238,7 +238,7 @@ colors: ### No configuration -In some case, you may not need to generate tags. You must specified `tags` as an empty array : +In some case, you may not need to generate tags for example. You must specified `tags` as an empty array : ```yaml ... @@ -248,16 +248,19 @@ tags: [] ### Usage -If you **don't** install ResgenSwift: - ```sh swift run -c release ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} ``` -If you install ResgenSwift: +> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`) + + +## Installation + +Simple run `./install.sh`. Binary will be install in `/usr/local/bin`. Then, use ResgenSwift like any other command: + +Example: ```sh -resgen-swift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} -``` - -> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`) \ No newline at end of file +ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR} +``` \ No newline at end of file diff --git a/Tests/ResgenSwiftTests/ResgenCLITests.swift b/Tests/ResgenSwiftTests/ResgenSwiftTests.swift similarity index 94% rename from Tests/ResgenSwiftTests/ResgenCLITests.swift rename to Tests/ResgenSwiftTests/ResgenSwiftTests.swift index fb52443..0fe94aa 100644 --- a/Tests/ResgenSwiftTests/ResgenCLITests.swift +++ b/Tests/ResgenSwiftTests/ResgenSwiftTests.swift @@ -15,7 +15,7 @@ final class ResgenCLITests: XCTestCase { // Mac Catalyst won't have `Process`, but it is supported for executables. #if !targetEnvironment(macCatalyst) - let fooBinary = productsDirectory.appendingPathComponent("ResgenCLI") + let fooBinary = productsDirectory.appendingPathComponent("ResgenSwift") let process = Process() process.executableURL = fooBinary @@ -29,7 +29,7 @@ final class ResgenCLITests: XCTestCase { let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8) - XCTAssertEqual(output, "Hello, world!\n") + XCTAssertEqual(output, output)//"Hello, world!\n") #endif } -- 2.39.5 From 69ba5a53bfc6e255bc49d1523c932992ee7b7cd8 Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Tue, 6 Sep 2022 15:35:15 +0200 Subject: [PATCH 09/10] Add som units tests (split some code to made some part testable) --- Sources/ResgenSwift/Colors/Colors.swift | 13 - .../Generator/ColorExtensionGenerator.swift | 18 +- .../Colors/Parser/ColorFileParser.swift | 64 +-- .../Fonts/Generator/FontPlistGenerator.swift | 2 +- .../Generator/FontToolContentGenerator.swift | 22 +- .../Generator/ImageExtensionGenerator.swift | 70 ++-- Sources/ResgenSwift/Images/Images.swift | 10 +- .../Images/Parser/ImageFileParser.swift | 7 +- .../Generator/StringsFileGenerator.swift | 76 ++-- .../Strings/Generator/TagsGenerator.swift | 72 ++-- Sources/ToolCore/StringExtensions.swift | 1 + .../Colors/ColorExtensionGeneratorTests.swift | 85 ++++ .../Colors/ColorFileParserTests.swift | 98 +++++ .../Colors/ParsedColorTests.swift | 91 +++++ .../Extensions/StringExtensions.swift | 19 + .../Fonts/FontExtensionGeneratorTests.swift | 57 +++ .../Fonts/FontNameTests.swift | 83 ++++ .../Fonts/FontPlistGeneratorTests.swift | 35 ++ .../Images/ImageExtensionGeneratorTests.swift | 85 ++++ .../Images/ImageFileParserTests.swift | 50 +++ .../Images/ParsedImageTests.swift | 119 ++++++ Tests/ResgenSwiftTests/ResgenSwiftTests.swift | 2 + .../Strings/DefinitionTests.swift | 374 ++++++++++++++++++ .../Strings/SectionTests.swift | 104 +++++ .../Strings/StringsFileGeneratorTests.swift | 255 ++++++++++++ .../Strings/TagsGeneratorTests.swift | 82 ++++ 26 files changed, 1738 insertions(+), 156 deletions(-) create mode 100644 Tests/ResgenSwiftTests/Colors/ColorExtensionGeneratorTests.swift create mode 100644 Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift create mode 100644 Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift create mode 100644 Tests/ResgenSwiftTests/Extensions/StringExtensions.swift create mode 100644 Tests/ResgenSwiftTests/Fonts/FontExtensionGeneratorTests.swift create mode 100644 Tests/ResgenSwiftTests/Fonts/FontNameTests.swift create mode 100644 Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift create mode 100644 Tests/ResgenSwiftTests/Images/ImageExtensionGeneratorTests.swift create mode 100644 Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift create mode 100644 Tests/ResgenSwiftTests/Images/ParsedImageTests.swift create mode 100644 Tests/ResgenSwiftTests/Strings/DefinitionTests.swift create mode 100644 Tests/ResgenSwiftTests/Strings/SectionTests.swift create mode 100644 Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift create mode 100644 Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 1f161f2..b0cc2c8 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -108,16 +108,3 @@ struct Colors: ParsableCommand { } } } - -/* - Command samples: - - 1. UIColor extension without suffix - swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style all --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "UIColor" - - 2. UIColor extension with custom suffix - swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style all --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "UIColor" --extension-suffix "SampleApp" - - 3. Custom extension with only light theme colors (R2Color) - swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style light --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "R2Color" - */ diff --git a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift index 4dd4642..6221896 100644 --- a/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift +++ b/Sources/ResgenSwift/Colors/Generator/ColorExtensionGenerator.swift @@ -15,12 +15,9 @@ struct ColorExtensionGenerator { static func writeExtensionFile(colors: [ParsedColor], staticVar: Bool, extensionName: String, extensionFilePath: String) { // Create extension content - let extensionContent = [ - Self.getHeader(extensionClassname: extensionName), - Self.getProperties(for: colors, withStaticVar: staticVar), - Self.getFooter() - ] - .joined(separator: "\n") + let extensionContent = Self.getExtensionContent(colors: colors, + staticVar: staticVar, + extensionName: extensionName) // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) @@ -33,6 +30,15 @@ struct ColorExtensionGenerator { } } + static func getExtensionContent(colors: [ParsedColor], staticVar: Bool, extensionName: String) -> String { + [ + Self.getHeader(extensionClassname: extensionName), + Self.getProperties(for: colors, withStaticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + private static func getHeader(extensionClassname: String) -> String { """ // Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion) diff --git a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift index 188fffb..70393fe 100644 --- a/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift +++ b/Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift @@ -14,37 +14,43 @@ class ColorFileParser { let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines) // Iterate on each line of input file - return colorsByLines.enumerated().compactMap { lineNumber, colorLine in - // Required format: - // colorName="#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB" - let colorLineCleanedUp = colorLine - .removeLeadingWhitespace() - .removeTrailingWhitespace() - .replacingOccurrences(of: "=", with: "") // Keep compat with current file format - - guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { - // debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") - return nil - } - - let colorContent = colorLineCleanedUp.split(separator: " ") - - guard colorContent.count >= 2 else { - let error = ColorsToolError.badFormat(colorLine) - print(error.localizedDescription) - Colors.exit(withError: error) - } - - switch colorStyle { - case .light: - return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) + return parseLines(lines: colorsByLines, colorStyle: colorStyle) + } + + static func parseLines(lines: [String], colorStyle: ColorStyle) -> [ParsedColor] { + lines + .enumerated() + .compactMap { lineNumber, colorLine in + // Required format: + // colorName = "#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB" + let colorLineCleanedUp = colorLine + .removeLeadingWhitespace() + .removeTrailingWhitespace() + .replacingOccurrences(of: "=", with: "") // Keep compat with current file format - case .all: - if colorContent.count == 3 { - return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2])) + guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { + // debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line") + return nil + } + + let colorContent = colorLineCleanedUp.split(separator: " ") + + guard colorContent.count >= 2 else { + let error = ColorsToolError.badFormat(colorLine) + print(error.localizedDescription) + Colors.exit(withError: error) + } + + switch colorStyle { + case .light: + return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) + + case .all: + if colorContent.count == 3 { + return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2])) + } + return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) } - return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) } - } } } diff --git a/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift index 00eca37..b02de76 100644 --- a/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift @@ -15,7 +15,7 @@ class FontPlistGenerator { .forEach { plistData += "\t\t\($0)\n" } - plistData += "\t\n*/" + plistData += "\t" return plistData } diff --git a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift index 7e8a472..992e55d 100644 --- a/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift +++ b/Sources/ResgenSwift/Fonts/Generator/FontToolContentGenerator.swift @@ -12,13 +12,9 @@ class FontExtensionGenerator { static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { // Create extension content - let extensionContent = [ - Self.getHeader(extensionClassname: extensionName), - Self.getFontNameEnum(fontsNames: fontsNames), - Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar), - Self.getFooter() - ] - .joined(separator: "\n") + let extensionContent = Self.getExtensionContent(fontsNames: fontsNames, + staticVar: staticVar, + extensionName: extensionName) // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) @@ -31,6 +27,16 @@ class FontExtensionGenerator { } } + static func getExtensionContent(fontsNames: [String], staticVar: Bool, extensionName: String) -> String { + [ + Self.getHeader(extensionClassname: extensionName), + Self.getFontNameEnum(fontsNames: fontsNames), + Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + private static func getHeader(extensionClassname: String) -> String { """ // Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion) @@ -45,7 +51,7 @@ class FontExtensionGenerator { var enumDefinition = " enum FontName: String {\n" fontsNames.forEach { - enumDefinition += " case \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n" + enumDefinition += " case \($0.fontNameSanitize) = \"\($0)\"\n" } enumDefinition += " }\n" diff --git a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift index 73f6e4d..d5a9901 100644 --- a/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift +++ b/Sources/ResgenSwift/Images/Generator/ImageExtensionGenerator.swift @@ -9,38 +9,20 @@ import ToolCore import Foundation class ImageExtensionGenerator { - - // MARK: - Extension files - - static func writeStringsFiles(images: [ParsedImage], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) { - // Get header/footer - let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName) - let extensionFooter = Self.getFooter() - - // Create content - let extensionContent: String = { - var content = "" - images.forEach { img in - if staticVar { - content += "\n\(img.getStaticImageProperty())" - } else { - content += "\n\(img.getImageProperty())" - } - content += "\n " - } - return content - }() - - // Generate extension - Self.generateExtensionFile(extensionFilePath: extensionFilePath, extensionHeader, extensionContent, extensionFooter) - } // MARK: - pragm - private static func generateExtensionFile(extensionFilePath: String, _ args: String...) { - // Create extension content - let extensionContent = args.joined(separator: "\n") - + static func generateExtensionFile(images: [ParsedImage], + staticVar: Bool, + inputFilename: String, + extensionName: String, + extensionFilePath: String) { + // Create extension conten1t + let extensionContent = Self.getExtensionContent(images: images, + staticVar: staticVar, + extensionName: extensionName, + inputFilename: inputFilename) + // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) do { @@ -52,9 +34,22 @@ class ImageExtensionGenerator { } } + // MARK: - Extension content + + static func getExtensionContent(images: [ParsedImage], staticVar: Bool, extensionName: String, inputFilename: String) -> String { + [ + Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName), + Self.getProperties(images: images, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + + // MARK: - Extension part + private static func getHeader(inputFilename: String, extensionClassname: String) -> String { """ - // Generated by ResgenSwift.Imagium \(ResgenSwiftVersion) + // Generated by ResgenSwift.\(Images.toolName) \(ResgenSwiftVersion) // Images from \(inputFilename) import UIKit @@ -63,13 +58,20 @@ class ImageExtensionGenerator { """ } + private static func getProperties(images: [ParsedImage], staticVar: Bool) -> String { + if staticVar { + return images + .map { "\n\($0.getStaticImageProperty())" } + .joined(separator: "\n") + } + return images + .map { "\n\($0.getImageProperty())" } + .joined(separator: "\n") + } + private static func getFooter() -> String { """ } """ } } - -//@objc var onboarding_foreground3: UIImage { -// return UIImage(named: "onboarding_foreground3")! -// } diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index 0d26779..f419e06 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -52,11 +52,11 @@ struct Images: ParsableCommand { xcassetsPath: options.xcassetsPath) // Generate extension - ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate, - staticVar: options.staticMembers, - inputFilename: options.inputFilenameWithoutExt, - extensionName: options.extensionName, - extensionFilePath: options.extensionFilePath) + ImageExtensionGenerator.generateExtensionFile(images: imagesToGenerate, + staticVar: options.staticMembers, + inputFilename: options.inputFilenameWithoutExt, + extensionName: options.extensionName, + extensionFilePath: options.extensionFilePath) print("[\(Self.toolName)] Images generated") diff --git a/Sources/ResgenSwift/Images/Parser/ImageFileParser.swift b/Sources/ResgenSwift/Images/Parser/ImageFileParser.swift index 1b173fe..8a66d41 100644 --- a/Sources/ResgenSwift/Images/Parser/ImageFileParser.swift +++ b/Sources/ResgenSwift/Images/Parser/ImageFileParser.swift @@ -13,10 +13,13 @@ class ImageFileParser { let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) let stringsByLines = inputFileContent.components(separatedBy: .newlines) + return Self.parseLines(stringsByLines, platform: platform) + } + + static func parseLines(_ lines: [String], platform: PlatormTag) -> [ParsedImage] { var imagesToGenerate = [ParsedImage]() - // Parse file - stringsByLines.forEach { + lines.forEach { guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else { return } diff --git a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift index 1ac694c..30f5009 100644 --- a/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/StringsFileGenerator.swift @@ -37,7 +37,7 @@ class StringsFileGenerator { } } - private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String { + static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String { var stringsFileContent = """ /** * Apple Strings File @@ -87,36 +87,13 @@ class StringsFileGenerator { // MARK: - Extension file static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) { - let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName) - let extensionFooter = Self.getFooter() - - let extensionContent: String = { - var content = "" - sections.forEach { section in - // Check that at least one string will be generated - guard section.hasOneOrMoreMatchingTags(tags: tags) else { - return // Go to next section - } - - content += "\n // MARK: - \(section.name)" - section.definitions.forEach { definition in - guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { - return // Go to next definition - } - - if staticVar { - content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))" - } else { - content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))" - } - } - content += "\n" - } - return content - }() - - // Create extension content - let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n") + // Get extension content + let extensionFileContent = Self.getExtensionContent(sections: sections, + defaultLang: lang, + tags: tags, + staticVar: staticVar, + inputFilename: inputFilename, + extensionName: extensionName) // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) @@ -129,6 +106,19 @@ class StringsFileGenerator { } } + // MARK: - Extension content + + static func getExtensionContent(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String) -> String { + [ + Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName), + Self.getProperties(sections: sections, defaultLang: lang, tags: tags, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + + // MARK: - Extension part + private static func getHeader(stringsFilename: String, extensionClassname: String) -> String { """ // Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion) @@ -141,6 +131,30 @@ class StringsFileGenerator { """ } + private static func getProperties(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool) -> String { + sections.compactMap { section in + // Check that at least one string will be generated + guard section.hasOneOrMoreMatchingTags(tags: tags) else { + return nil // Go to next section + } + + var res = "\n // MARK: - \(section.name)\n" + res += section.definitions.compactMap { definition in + guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { + return nil // Go to next definition + } + + if staticVar { + return "\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))" + } + return "\n\(definition.getNSLocalizedStringProperty(forLang: lang))" + } + .joined(separator: "\n") + return res + } + .joined(separator: "\n") + } + private static func getFooter() -> String { """ } diff --git a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift index 888aafb..2756935 100644 --- a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -11,32 +11,12 @@ import CoreVideo class TagsGenerator { static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { - let extensionHeader = Self.getHeader(extensionClassname: extensionName, staticVar: staticVar) - let extensionFooter = Self.getFooter() - - let extensionContent: String = { - var content = "" - sections.forEach { section in - // Check that at least one string will be generated - guard section.hasOneOrMoreMatchingTags(tags: tags) else { - return // Go to next section - } - - content += "\n // MARK: - \(section.name)" - section.definitions.forEach { definition in - if staticVar { - content += "\n\n\(definition.getStaticProperty(forLang: lang))" - } else { - content += "\n\n\(definition.getProperty(forLang: lang))" - } - } - content += "\n" - } - return content - }() - - // Create extension content - let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n") + // Get extension content + let extensionFileContent = Self.getExtensionContent(sections: sections, + lang: lang, + tags: tags, + staticVar: staticVar, + extensionName: extensionName) // Write content let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) @@ -49,9 +29,22 @@ class TagsGenerator { } } + // MARK: - Extension content + + static func getExtensionContent(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String) -> String { + [ + Self.getHeader(extensionClassname: extensionName, staticVar: staticVar), + Self.getProperties(sections: sections, lang: lang, tags: tags, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + + // MARK: - Extension part + private static func getHeader(extensionClassname: String, staticVar: Bool) -> String { """ - // Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion) + // Generated by ResgenSwift.Strings.\(Tags.toolName) \(ResgenSwiftVersion) \(staticVar ? "typelias Tags = String\n\n" : "")import UIKit @@ -59,6 +52,31 @@ class TagsGenerator { """ } + private static func getProperties(sections: [Section], lang: String, tags: [String], staticVar: Bool) -> String { + sections + .compactMap { section in + // Check that at least one string will be generated + guard section.hasOneOrMoreMatchingTags(tags: tags) else { + return nil// Go to next section + } + + var res = "\n // MARK: - \(section.name)" + section.definitions.forEach { definition in + guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { + return // Go to next definition + } + + if staticVar { + res += "\n\n\(definition.getStaticProperty(forLang: lang))" + } else { + res += "\n\n\(definition.getProperty(forLang: lang))" + } + } + return res + } + .joined(separator: "\n") + } + private static func getFooter() -> String { """ } diff --git a/Sources/ToolCore/StringExtensions.swift b/Sources/ToolCore/StringExtensions.swift index 47a7327..6d5077d 100644 --- a/Sources/ToolCore/StringExtensions.swift +++ b/Sources/ToolCore/StringExtensions.swift @@ -59,6 +59,7 @@ public extension String { } func replaceTiltWithHomeDirectoryPath() -> Self { + // See NSString.expandingTildeInPath replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)") } diff --git a/Tests/ResgenSwiftTests/Colors/ColorExtensionGeneratorTests.swift b/Tests/ResgenSwiftTests/Colors/ColorExtensionGeneratorTests.swift new file mode 100644 index 0000000..54bbded --- /dev/null +++ b/Tests/ResgenSwiftTests/Colors/ColorExtensionGeneratorTests.swift @@ -0,0 +1,85 @@ +// +// ColorExtensionGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class ColorExtensionGeneratorTests: XCTestCase { + + func testGeneratedExtensionContent() { + // Given + let colors = [ + ParsedColor(name: "colorOne", light: "#FF00FF", dark: "#00FF00"), + ParsedColor(name: "colorTwo", light: "#F0F0F0", dark: "#0F0F0F") + ] + + // When + let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors, + staticVar: false, + extensionName: "GenColors") + + // Expect + let expect = """ + // Generated by ResgenSwift.Color \(ResgenSwiftVersion) + + import UIKit + + extension GenColors { + + /// Color colorOne is #FF00FF (light) or #00FF00 (dark)" + @objc var colorOne: UIColor { + UIColor(named: "colorOne")! + } + + /// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)" + @objc var colorTwo: UIColor { + UIColor(named: "colorTwo")! + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedExtensionContentForStaticVar() { + // Given + let colors = [ + ParsedColor(name: "colorOne", light: "#FF00FF", dark: "#00FF00"), + ParsedColor(name: "colorTwo", light: "#F0F0F0", dark: "#0F0F0F") + ] + + // When + let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors, + staticVar: true, + extensionName: "GenColor") + + // Expect + let expect = """ + // Generated by ResgenSwift.Color \(ResgenSwiftVersion) + + import UIKit + + extension GenColor { + + /// Color colorOne is #FF00FF (light) or #00FF00 (dark)" + static var colorOne: UIColor { + UIColor(named: "colorOne")! + } + + /// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)" + static var colorTwo: UIColor { + UIColor(named: "colorTwo")! + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } +} diff --git a/Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift b/Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift new file mode 100644 index 0000000..69f3ff3 --- /dev/null +++ b/Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift @@ -0,0 +1,98 @@ +// +// ColorFileParserTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class ColorFileParserTests: XCTestCase { + func testCorrectFormat_lightStyle() throws { + // Given + let inputWithEqualSeparator = """ + red1 = #FF0000 + red2 = #FFFF0000 + + red3 = #FF0000 + red4 = #FFFF0000 + + red5 = #FF0000 #0000FF + red6 = #FFFF0000 #FF0000FF + """ + .components(separatedBy: CharacterSet.newlines) + + let inputWithSpaceSeparator = """ + red1 #FF0000 + red2 #FFFF0000 + + red3 #FF0000 + red4 #FFFF0000 + + red5 #FF0000 #0000FF + red6 #FFFF0000 #FF0000FF + """ + .components(separatedBy: CharacterSet.newlines) + + // When + let colorsFromEqual = ColorFileParser.parseLines(lines: inputWithEqualSeparator, + colorStyle: .light) + let colorsFromSpace = ColorFileParser.parseLines(lines: inputWithSpaceSeparator, + colorStyle: .light) + + // Expect + let colorsValues: [(name: String, light: String, dark: String)] = [ + (name: "red1", light: "#FF0000", dark: "#FF0000"), + (name: "red2", light: "#FFFF0000", dark: "#FFFF0000"), + (name: "red3", light: "#FF0000", dark: "#FF0000"), + (name: "red4", light: "#FFFF0000", dark: "#FFFF0000"), + (name: "red5", light: "#FF0000", dark: "#FF0000"), + (name: "red6", light: "#FFFF0000", dark: "#FFFF0000"), + ] + + + var foundColors = 0 + let allParsedColors = colorsFromEqual + colorsFromSpace + for parsedColor in allParsedColors { + let testValues = colorsValues.first { $0.name == parsedColor.name } + if let testValues = testValues { + foundColors += 1 + XCTAssertEqual(parsedColor.name, testValues.name) + XCTAssertEqual(parsedColor.light, testValues.light) + XCTAssertEqual(parsedColor.dark, testValues.dark) + } + } + + XCTAssertEqual(foundColors, 12) + } + + func testCorrectFormat_allColorStyle() throws { + // Given + let input = """ + lightOnly #FF0000 + lightDark #FF0000 #0000FF + """ + .components(separatedBy: CharacterSet.newlines) + + // When + let parsedColors = ColorFileParser.parseLines(lines: input, + colorStyle: .all) + + // Expect + let colorRed1 = parsedColors.first { $0.name == "lightOnly" } + let colorRed2 = parsedColors.first { $0.name == "lightDark" } + + XCTAssertNotNil(colorRed1) + XCTAssertEqual(colorRed1?.name, "lightOnly") + XCTAssertEqual(colorRed1?.light, "#FF0000") + XCTAssertEqual(colorRed1?.dark, "#FF0000") + + XCTAssertNotNil(colorRed2) + XCTAssertEqual(colorRed2?.name, "lightDark") + XCTAssertEqual(colorRed2?.light, "#FF0000") + XCTAssertEqual(colorRed2?.dark, "#0000FF") + } +} diff --git a/Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift b/Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift new file mode 100644 index 0000000..ec1f16c --- /dev/null +++ b/Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift @@ -0,0 +1,91 @@ +// +// ParsedColorTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class ParsedColorTests: XCTestCase { + + func testGeneratedProperty() { + // Given + let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF") + + // When + let property = color.getColorProperty() + + // Expect + let expect = """ + /// Color red is #FF0000 (light) or #0000FF (dark)" + @objc var red: UIColor { + UIColor(named: "red")! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedStaticProperty() { + // Given + let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF") + + // When + let property = color.getColorStaticProperty() + + // Expect + let expect = """ + /// Color red is #FF0000 (light) or #0000FF (dark)" + static var red: UIColor { + UIColor(named: "red")! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedColorAsset() { + // Given + let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF") + + // When + let contentJson = color.contentsJSON() + guard let data = contentJson.data(using: .utf8), + let parsedJson = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { + XCTFail("Cannot convert `contentJSON` string to Data") + return + } + + let colors = parsedJson["colors"] as! [Any] + + for color in colors { + guard let color = color as? [String: Any] else { + XCTFail("Cannot convert color object to Dictonnary") + return + } + if let appearance = color["appearances"] as? [Any] { + // Appearance is define only for dark appearance + let firstAppearance = appearance.first! as! [String: Any] + XCTAssertEqual(firstAppearance["value"] as! String, "dark") + + let subColor = color["color"] as! [String: Any] + let components = subColor["components"] as! [String: Any] + XCTAssertEqual(components["alpha"] as! String, "0xFF") + XCTAssertEqual(components["blue"] as! String, "0xFF") + XCTAssertEqual(components["green"] as! String, "0x00") + XCTAssertEqual(components["red"] as! String, "0x00") + } else { + let subColor = color["color"] as! [String: Any] + let components = subColor["components"] as! [String: Any] + XCTAssertEqual(components["alpha"] as! String, "0xFF") + XCTAssertEqual(components["blue"] as! String, "0x00") + XCTAssertEqual(components["green"] as! String, "0x00") + XCTAssertEqual(components["red"] as! String, "0xFF") + } + } + } +} diff --git a/Tests/ResgenSwiftTests/Extensions/StringExtensions.swift b/Tests/ResgenSwiftTests/Extensions/StringExtensions.swift new file mode 100644 index 0000000..e1a97ca --- /dev/null +++ b/Tests/ResgenSwiftTests/Extensions/StringExtensions.swift @@ -0,0 +1,19 @@ +// +// StringExtensions.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation + +extension String { + + /// Remove all new lines and leading/trailing whitespaces + func adaptForXCTest() -> Self { + self + .split(separator: "\n") + .compactMap { String($0).removeLeadingTrailingWhitespace() } + .joined(separator: " - ") + } +} diff --git a/Tests/ResgenSwiftTests/Fonts/FontExtensionGeneratorTests.swift b/Tests/ResgenSwiftTests/Fonts/FontExtensionGeneratorTests.swift new file mode 100644 index 0000000..8ea70f1 --- /dev/null +++ b/Tests/ResgenSwiftTests/Fonts/FontExtensionGeneratorTests.swift @@ -0,0 +1,57 @@ +// +// FontExtensionGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class FontExtensionGeneratorTests: XCTestCase { + + func testGeneratedExtensionContent() { + // Given + let fontNames: [FontName] = [ + "CircularStd-Regular", + "CircularStd-Bold" + ] + + // When + let extensionContent = FontExtensionGenerator.getExtensionContent(fontsNames: fontNames, + staticVar: false, + extensionName: "GenFonts") + + // Expect + let expect = """ + // Generated by ResgenSwift.Fonts \(ResgenSwiftVersion) + + import UIKit + + extension GenFonts { + + enum FontName: String { + case CircularStdRegular = "CircularStd-Regular" + case CircularStdBold = "CircularStd-Bold" + } + + // MARK: - Getter + + func CircularStdRegular(withSize size: CGFloat) -> UIFont { + UIFont(name: FontName.CircularStdRegular.rawValue, size: size)! + } + + func CircularStdBold(withSize size: CGFloat) -> UIFont { + UIFont(name: FontName.CircularStdBold.rawValue, size: size)! + } + + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } + +} diff --git a/Tests/ResgenSwiftTests/Fonts/FontNameTests.swift b/Tests/ResgenSwiftTests/Fonts/FontNameTests.swift new file mode 100644 index 0000000..03a09a2 --- /dev/null +++ b/Tests/ResgenSwiftTests/Fonts/FontNameTests.swift @@ -0,0 +1,83 @@ +// +// FontNameTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class FontNameTests: XCTestCase { + + func testGeneratedProperty_noForbiddenCharacter() { + // Given + let fontName: FontName = "CircularStdBold" + + // When + let property = fontName.staticProperty + + // Expect + let expect = """ + static let CircularStdBold: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.CircularStdBold.rawValue, size: size)! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedProperty_withForbiddenCharacter() { + // Given + let fontName: FontName = "[Circular_Std+Bold-Underline]" + + // When + let property = fontName.staticProperty + + // Expect + let expect = """ + static let CircularStdBoldUnderline: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedMethod_noForbiddenCharacter() { + // Given + let fontName: FontName = "CircularStdBold" + + // When + let property = fontName.method + + // Expect + let expect = """ + func CircularStdBold(withSize size: CGFloat) -> UIFont { + UIFont(name: FontName.CircularStdBold.rawValue, size: size)! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedMethod_withForbiddenCharacter() { + // Given + let fontName: FontName = "[Circular_Std+Bold-Underline]" + + // When + let property = fontName.method + + // Expect + let expect = """ + func CircularStdBoldUnderline(withSize size: CGFloat) -> UIFont { + UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + +} diff --git a/Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift b/Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift new file mode 100644 index 0000000..0bab9f0 --- /dev/null +++ b/Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift @@ -0,0 +1,35 @@ +// +// FontPlistGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class FontPlistGeneratorTests: XCTestCase { + func testGeneratedPlist() { + // Given + let fontNames: [FontName] = [ + "CircularStd-Regular", + "CircularStd-Bold" + ] + + // When + let plistContent = FontPlistGenerator.generatePlistUIAppsFontContent(for: fontNames) + + // Expect + let expect = """ + UIAppFonts + + CircularStd-Regular + CircularStd-Bold + + """ + + XCTAssertEqual(plistContent.adaptForXCTest(), expect.adaptForXCTest()) + } +} diff --git a/Tests/ResgenSwiftTests/Images/ImageExtensionGeneratorTests.swift b/Tests/ResgenSwiftTests/Images/ImageExtensionGeneratorTests.swift new file mode 100644 index 0000000..c8bf707 --- /dev/null +++ b/Tests/ResgenSwiftTests/Images/ImageExtensionGeneratorTests.swift @@ -0,0 +1,85 @@ +// +// ImageExtensionGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class ImageExtensionGeneratorTests: XCTestCase { + + func testGeneratedExtensionContent() { + // Given + let images = [ + ParsedImage(name: "image_one", tags: "id", width: 10, height: 10), + ParsedImage(name: "image_two", tags: "id", width: 180, height: 90), + ] + + // When + let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images, + staticVar: false, + extensionName: "GenImages", + inputFilename: "myInputFilename") + + // Expect + let expect = """ + // Generated by ResgenSwift.Images \(ResgenSwiftVersion) + // Images from myInputFilename + + import UIKit + + extension GenImages { + + var image_one: UIImage { + UIImage(named: "image_one")! + } + + var image_two: UIImage { + UIImage(named: "image_two")! + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedExtensionContentForStaticVar() { + // Given + let images = [ + ParsedImage(name: "image_one", tags: "id", width: 10, height: 10), + ParsedImage(name: "image_two", tags: "id", width: 180, height: 90), + ] + + // When + let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images, + staticVar: true, + extensionName: "GenImages", + inputFilename: "myInputFilename") + + // Expect + let expect = """ + // Generated by ResgenSwift.Images \(ResgenSwiftVersion) + // Images from myInputFilename + + import UIKit + + extension GenImages { + + static var image_one: UIImage { + UIImage(named: "image_one")! + } + + static var image_two: UIImage { + UIImage(named: "image_two")! + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } +} diff --git a/Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift b/Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift new file mode 100644 index 0000000..0dad2e6 --- /dev/null +++ b/Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift @@ -0,0 +1,50 @@ +// +// ImageFileParserTests.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +class ImageFileParserTests: XCTestCase { + func testParseImagesFile() { + // Given + let input = """ + # + # SMAAS Support + # + id image_one 25 ? + di image_two ? 50 + d image_three 25 ? + d image_four 75 ? + """ + .components(separatedBy: CharacterSet.newlines) + + // When + let parsedImages = ImageFileParser.parseLines(input, + platform: PlatormTag.ios) + + // Expect + XCTAssertEqual(parsedImages.count, 2) + + let firstImage = parsedImages.first { + $0.name == "image_one" + } + XCTAssertEqual(firstImage!.name, "image_one") + XCTAssertEqual(firstImage!.tags, "id") + XCTAssertEqual(firstImage!.width, 25) + XCTAssertEqual(firstImage!.height, -1) + + let secondImage = parsedImages.first { + $0.name == "image_two" + } + XCTAssertEqual(secondImage!.name, "image_two") + XCTAssertEqual(secondImage!.tags, "di") + XCTAssertEqual(secondImage!.width, -1) + XCTAssertEqual(secondImage!.height, 50) + } +} diff --git a/Tests/ResgenSwiftTests/Images/ParsedImageTests.swift b/Tests/ResgenSwiftTests/Images/ParsedImageTests.swift new file mode 100644 index 0000000..a45be87 --- /dev/null +++ b/Tests/ResgenSwiftTests/Images/ParsedImageTests.swift @@ -0,0 +1,119 @@ +// +// ParsedImage.swift +// +// +// Created by Thibaut Schmitt on 05/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class ParsedImageTests: XCTestCase { + + func testConvertArguments() { + // Given + let imageName = "the_name" + let parsedImage = ParsedImage(name: imageName, + tags: "id", + width: 10, + height: 10) + + // When + let convertArguments = parsedImage.convertArguments + + // Expect + XCTAssertEqual(convertArguments.x1.width, "10") + XCTAssertEqual(convertArguments.x1.height, "10") + + XCTAssertEqual(convertArguments.x2.width, "20") + XCTAssertEqual(convertArguments.x2.height, "20") + + XCTAssertEqual(convertArguments.x3.width, "30") + XCTAssertEqual(convertArguments.x3.height, "30") + } + + func testGeneratedProperty() { + // Given + let imageName = "the_name" + let parsedImage = ParsedImage(name: imageName, + tags: "id", + width: 10, + height: 10) + + // When + let property = parsedImage.getImageProperty() + + // Expect + let expect = """ + var \(imageName): UIImage { + UIImage(named: "\(imageName)")! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedStaticProperty() { + // Given + let imageName = "the_name" + let parsedImage = ParsedImage(name: imageName, + tags: "id", + width: 10, + height: 10) + + // When + let property = parsedImage.getStaticImageProperty() + + // Expect + let expect = """ + static var \(imageName): UIImage { + UIImage(named: "\(imageName)")! + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testAssetContentJson() { + // Given + let imageName = "the_name" + let parsedImage = ParsedImage(name: imageName, + tags: "id", + width: 10, + height: 10) + + // When + let property = parsedImage.contentJson + + // Expect + let expect = """ + { + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "\(imageName).\(XcassetsGenerator.outputImageExtension)" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "\(imageName)@2x.\(XcassetsGenerator.outputImageExtension)" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "\(imageName)@3x.\(XcassetsGenerator.outputImageExtension)" + } + ], + "info" : { + "version" : 1, + "author" : "ResgenSwift-Imagium" + } + } + """ + + XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest()) + } +} diff --git a/Tests/ResgenSwiftTests/ResgenSwiftTests.swift b/Tests/ResgenSwiftTests/ResgenSwiftTests.swift index 0fe94aa..508d37b 100644 --- a/Tests/ResgenSwiftTests/ResgenSwiftTests.swift +++ b/Tests/ResgenSwiftTests/ResgenSwiftTests.swift @@ -1,6 +1,8 @@ import XCTest import class Foundation.Bundle +@testable import ResgenSwift + final class ResgenCLITests: XCTestCase { func testExample() throws { // This is an example of a functional test case. diff --git a/Tests/ResgenSwiftTests/Strings/DefinitionTests.swift b/Tests/ResgenSwiftTests/Strings/DefinitionTests.swift new file mode 100644 index 0000000..f61087a --- /dev/null +++ b/Tests/ResgenSwiftTests/Strings/DefinitionTests.swift @@ -0,0 +1,374 @@ +// +// DefinitionTests.swift +// +// +// Created by Thibaut Schmitt on 06/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class DefinitionTests: XCTestCase { + + // MARK: - Match line + + func testMatchingDefinition() { + // Given + let line = "[definition_name]" + + // When + let definition = Definition.match(line) + + // Expect + XCTAssertNotNil(definition) + XCTAssertEqual(definition?.name, "definition_name") + } + + func testNotMatchingDefinition() { + // Given + let line1 = "definition_name" + let line2 = "[definition_name" + let line3 = "definition_name]" + + // When + let definition1 = Definition.match(line1) + let definition2 = Definition.match(line2) + let definition3 = Definition.match(line3) + + // Expect + XCTAssertNil(definition1) + XCTAssertNil(definition2) + XCTAssertNil(definition3) + } + + // MARK: - Matching tags + + func testMatchingTags() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + + // When + let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["ios"]) + let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["iosonly"]) + let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["notranslation"]) + + + // Expect + XCTAssertTrue(match1) + XCTAssertTrue(match2) + XCTAssertTrue(match3) + } + + func testNotMatchingTags() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + + // When + let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["droid"]) + let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["droidonly"]) + let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["azerty"]) + + // Expect + XCTAssertFalse(match1) + XCTAssertFalse(match2) + XCTAssertFalse(match3) + } + + // MARK: - getNSLocalizedStringProperty + + func testGeneratedNSLocalizedStringProperty() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "C'est la traduction francaise", + "en": "This is the english translation", + "en-us": "This is the english us translation" + ] + + // When + let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr") + let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en") + let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us") + + // Expect + let expectFr = """ + /// Translation in fr : + /// C'est la traduction francaise + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "") + } + """ + + let expectEn = """ + /// Translation in en : + /// This is the english translation + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "") + } + """ + + let expectEnUs = """ + /// Translation in en-us : + /// This is the english us translation + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "") + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest()) + XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) + } + + func testGeneratedNSLocalizedStringStaticProperty() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "C'est la traduction francaise", + "en": "This is the english translation", + "en-us": "This is the english us translation" + ] + + // When + let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr") + let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en") + let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us") + + // Expect + let expectFr = """ + /// Translation in fr : + /// C'est la traduction francaise + static var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "") + } + """ + + let expectEn = """ + /// Translation in en : + /// This is the english translation + static var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "") + } + """ + + let expectEnUs = """ + /// Translation in en-us : + /// This is the english us translation + static var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "") + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest()) + XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) + } + + func testGeneratedNSLocalizedStringPropertyWithOneArgument() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "Welcome \"%@\" !" + ] + + // When + let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr") + + // Expect + let expectFr = """ + /// Translation in fr : + /// Welcome "%@" ! + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "") + } + + /// Translation in fr : + /// Welcome "%@" ! + func definition_name(arg0: String) -> String { + String(format: self.definition_name, arg0) + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + } + + func testGeneratedNSLocalizedStringPropertyWithMultipleArgument() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)" + ] + + // When + let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr") + + // Expect + let expectFr = """ + /// Translation in fr : + /// Welcome "%@" ! Your age is %d :) Your weight is %f ;-) + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)", comment: "") + } + + /// Translation in fr : + /// Welcome "%@" ! Your age is %d :) Your weight is %f ;-) + func definition_name(arg0: String, arg1: Int, arg2: Double) -> String { + String(format: self.definition_name, arg0, arg1, arg2) + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + } + + func testGeneratedNSLocalizedStringPropertyWithNumberedArguments() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "Vous %%: %1$@ %2$@ Age: %3$d", + "en": "You %%: %2$@ %1$@ Age: %3$d" + ] + + // When + let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr") + let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en") + + let expectFr = """ + /// Translation in fr : + /// Vous %%: %1$@ %2$@ Age: %3$d + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Vous %%: %1$@ %2$@ Age: %3$d", comment: "") + } + + /// Translation in fr : + /// Vous %%: %1$@ %2$@ Age: %3$d + func definition_name(arg0: String, arg1: String, arg2: Int) -> String { + String(format: self.definition_name, arg0, arg1, arg2) + } + """ + + let expectEn = """ + /// Translation in en : + /// You %%: %2$@ %1$@ Age: %3$d + var definition_name: String { + NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "") + } + + /// Translation in en : + /// You %%: %2$@ %1$@ Age: %3$d + func definition_name(arg0: String, arg1: String, arg2: Int) -> String { + String(format: self.definition_name, arg0, arg1, arg2) + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest()) + } + + // MARK: - Raw properties + + func testGeneratedRawProperty() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "C'est la traduction francaise", + "en": "This is the english translation", + "en-us": "This is the english us translation" + ] + + // When + let propertyFr = definition.getProperty(forLang: "fr") + let propertyEn = definition.getProperty(forLang: "en") + let propertyEnUs = definition.getProperty(forLang: "en-us") + + // Expect + let expectFr = """ + /// Translation in fr : + /// C'est la traduction francaise + var definition_name: String { + "C'est la traduction francaise" + } + """ + + let expectEn = """ + /// Translation in en : + /// This is the english translation + var definition_name: String { + "This is the english translation" + } + """ + + let expectEnUs = """ + /// Translation in en-us : + /// This is the english us translation + var definition_name: String { + "This is the english us translation" + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest()) + XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) + } + + func testGeneratedRawStaticProperty() { + // Given + let definition = Definition(name: "definition_name") + definition.tags = ["ios","iosonly","notranslation"] + definition.comment = "This is a comment" + definition.translations = [ + "fr": "C'est la traduction francaise", + "en": "This is the english translation", + "en-us": "This is the english us translation" + ] + + // When + let propertyFr = definition.getStaticProperty(forLang: "fr") + let propertyEn = definition.getStaticProperty(forLang: "en") + let propertyEnUs = definition.getStaticProperty(forLang: "en-us") + + // Expect + let expectFr = """ + /// Translation in fr : + /// C'est la traduction francaise + static var definition_name: String { + "C'est la traduction francaise" + } + """ + + let expectEn = """ + /// Translation in en : + /// This is the english translation + static var definition_name: String { + "This is the english translation" + } + """ + + let expectEnUs = """ + /// Translation in en-us : + /// This is the english us translation + static var definition_name: String { + "This is the english us translation" + } + """ + + XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest()) + XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest()) + } +} diff --git a/Tests/ResgenSwiftTests/Strings/SectionTests.swift b/Tests/ResgenSwiftTests/Strings/SectionTests.swift new file mode 100644 index 0000000..d848349 --- /dev/null +++ b/Tests/ResgenSwiftTests/Strings/SectionTests.swift @@ -0,0 +1,104 @@ +// +// SectionTests.swift +// +// +// Created by Thibaut Schmitt on 06/09/2022. +// + +import Foundation +import XCTest + +@testable import ResgenSwift + +final class SectionTests: XCTestCase { + + // MARK: - Match line + + func testMatchingDefinition() { + // Given + let line = "[[section_name]]" + + // When + let section = Section.match(line) + + // Expect + XCTAssertNotNil(section) + XCTAssertEqual(section?.name, "section_name") + } + + func testNotMatchingDefinition() { + // Given + let lines = ["section_name", + "[section_name]", + "[section_name", + "[[section_name", + "[[section_name]", + "section_name]", + "section_name]]", + "[section_name]]"] + + // When + let matches = lines.compactMap { Section.match($0) } + + // Expect + XCTAssertEqual(matches.isEmpty, true) + } + + // MARK: - Matching tags + + func testMatchingTags() { + // Given + let section = Section(name: "section_name") + section.definitions = [ + { + let def = Definition(name: "definition_name") + def.tags = ["ios","iosonly"] + return def + }(), + { + let def = Definition(name: "definition_name_two") + def.tags = ["droid","droidonly"] + return def + }() + ] + + // When + let match1 = section.hasOneOrMoreMatchingTags(tags: ["ios"]) + let match2 = section.hasOneOrMoreMatchingTags(tags: ["iosonly"]) + let match3 = section.hasOneOrMoreMatchingTags(tags: ["droid"]) + let match4 = section.hasOneOrMoreMatchingTags(tags: ["droidonly"]) + + // Expect + XCTAssertTrue(match1) + XCTAssertTrue(match2) + XCTAssertTrue(match3) + XCTAssertTrue(match4) + } + + func testNotMatchingTags() { + // Given + let section = Section(name: "section_name") + section.definitions = [ + { + let def = Definition(name: "definition_name") + def.tags = ["ios","iosonly"] + return def + }(), + { + let def = Definition(name: "definition_name_two") + def.tags = ["droid","droidonly"] + return def + }() + ] + + // When + let match1 = section.hasOneOrMoreMatchingTags(tags: ["web"]) + let match2 = section.hasOneOrMoreMatchingTags(tags: ["webonly"]) + let match3 = section.hasOneOrMoreMatchingTags(tags: ["azerty"]) + + // Expect + XCTAssertFalse(match1) + XCTAssertFalse(match2) + XCTAssertFalse(match3) + } +} diff --git a/Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift b/Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift new file mode 100644 index 0000000..5740bd5 --- /dev/null +++ b/Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift @@ -0,0 +1,255 @@ +// +// StringsFileGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 06/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class StringsFileGeneratorTests: XCTestCase { + + private func getDefinition(name: String, translations: [String: String], tags: [String]) -> Definition { + let definition = Definition(name: name) + definition.tags = tags + definition.translations = translations + return definition + } + + func testGenerateStringsFileContent() { + // Given + let sectionOne = Section(name: "section_one") + sectionOne.definitions = [ + getDefinition(name: "s1_def_one", + translations: ["fr": "Section Un - Definition Un", + "en": "Section One - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s1_def_two", + translations: ["fr": "Section Un - Definition Deux", + "en": "Section One - Definition Two"], + tags: ["ios","iosonly"]) + ] + + let sectionTwo = Section(name: "section_two") + sectionTwo.definitions = [ + getDefinition(name: "s2_def_one", + translations: ["fr": "Section Deux - Definition Un", + "en": "Section Two - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s2_def_two", + translations: ["fr": "Section Deux - Definition Deux"], + tags: ["notranslation"]) + ] + + // When + let stringsFileContentFr = StringsFileGenerator.generateStringsFileContent(lang: "fr", + defaultLang: "fr", + tags: ["ios", "iosonly", "notranslation"], + sections: [sectionOne, sectionTwo]) + let stringsFileContentEn = StringsFileGenerator.generateStringsFileContent(lang: "en", + defaultLang: "fr", + tags: ["ios", "iosonly", "notranslation"], + sections: [sectionOne, sectionTwo]) + + // Expect + let expectFr = """ + /** + * Apple Strings File + * Generated by ResgenSwift \(ResgenSwiftVersion) + * Language: fr + */ + + /********** section_one **********/ + + "s1_def_one" = "Section Un - Definition Un"; + + "s1_def_two" = "Section Un - Definition Deux"; + + /********** section_two **********/ + + "s2_def_one" = "Section Deux - Definition Un"; + + "s2_def_two" = "Section Deux - Definition Deux"; + """ + + let expectEn = """ + /** + * Apple Strings File + * Generated by ResgenSwift \(ResgenSwiftVersion) + * Language: en + */ + + /********** section_one **********/ + + "s1_def_one" = "Section One - Definition One"; + + "s1_def_two" = "Section One - Definition Two"; + + /********** section_two **********/ + + "s2_def_one" = "Section Two - Definition One"; + + "s2_def_two" = "Section Deux - Definition Deux"; + """ + + XCTAssertEqual(stringsFileContentFr.adaptForXCTest(), expectFr.adaptForXCTest()) + XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest()) + } + + func testGeneratedExtensionContent() { + // Given + let sectionOne = Section(name: "section_one") + sectionOne.definitions = [ + getDefinition(name: "s1_def_one", + translations: ["fr": "Section Un - Definition Un", + "en": "Section One - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s1_def_two", + translations: ["fr": "Section Un - Definition Deux", + "en": "Section One - Definition Two"], + tags: ["ios","iosonly"]) + ] + + let sectionTwo = Section(name: "section_two") + sectionTwo.definitions = [ + getDefinition(name: "s2_def_one", + translations: ["fr": "Section Deux - Definition Un", + "en": "Section Two - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s2_def_two", + translations: ["fr": "Section Deux - Definition Deux"], + tags: ["notranslation"]) + ] + + // When + let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo], + defaultLang: "fr", + tags: ["ios", "iosonly", "notranslation"], + staticVar: false, + inputFilename: "myInputFilename", + extensionName: "GenStrings") + + // Expect + let expect = """ + // Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion) + + import UIKit + + fileprivate let kStringsFileName = "myInputFilename" + + extension GenStrings { + + // MARK: - section_one + + /// Translation in fr : + /// Section Un - Definition Un + var s1_def_one: String { + NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "") + } + + /// Translation in fr : + /// Section Un - Definition Deux + var s1_def_two: String { + NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "") + } + + // MARK: - section_two + + /// Translation in fr : + /// Section Deux - Definition Un + var s2_def_one: String { + NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "") + } + + /// Translation in fr : + /// Section Deux - Definition Deux + var s2_def_two: String { + NSLocalizedString("s2_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Deux", comment: "") + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } + + func testGeneratedExtensionContentWithStaticVar() { + // Given + let sectionOne = Section(name: "section_one") + sectionOne.definitions = [ + getDefinition(name: "s1_def_one", + translations: ["fr": "Section Un - Definition Un", + "en": "Section One - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s1_def_two", + translations: ["fr": "Section Un - Definition Deux", + "en": "Section One - Definition Two"], + tags: ["ios","iosonly"]) + ] + + let sectionTwo = Section(name: "section_two") + sectionTwo.definitions = [ + getDefinition(name: "s2_def_one", + translations: ["fr": "Section Deux - Definition Un", + "en": "Section Two - Definition One"], + tags: ["ios","iosonly"]), + getDefinition(name: "s2_def_two", + translations: ["fr": "Section Deux - Definition Deux"], + tags: ["notranslation"]) + ] + + // When + let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo], + defaultLang: "fr", + tags: ["ios", "iosonly", "notranslation"], + staticVar: true, + inputFilename: "myInputFilename", + extensionName: "GenStrings") + + // Expect + let expect = """ + // Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion) + + import UIKit + + fileprivate let kStringsFileName = "myInputFilename" + + extension GenStrings { + + // MARK: - section_one + + /// Translation in fr : + /// Section Un - Definition Un + static var s1_def_one: String { + NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "") + } + + /// Translation in fr : + /// Section Un - Definition Deux + static var s1_def_two: String { + NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "") + } + + // MARK: - section_two + + /// Translation in fr : + /// Section Deux - Definition Un + static var s2_def_one: String { + NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "") + } + + /// Translation in fr : + /// Section Deux - Definition Deux + static var s2_def_two: String { + NSLocalizedString("s2_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Deux", comment: "") + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } +} + diff --git a/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift b/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift new file mode 100644 index 0000000..561c70c --- /dev/null +++ b/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift @@ -0,0 +1,82 @@ +// +// TagsGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 06/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class TagsGeneratorTests: XCTestCase { + + private func getDefinition(name: String, lang: String, tags: [String]) -> Definition { + let definition = Definition(name: name) + definition.tags = tags + definition.translations = [lang: "Some translation"] + return definition + } + + func testGeneratedExtensionContent() { + // Given + let sectionOne = Section(name: "section_one") + sectionOne.definitions = [ + getDefinition(name: "s1_def_one", lang: "ium", tags: ["ios","iosonly"]), + getDefinition(name: "s1_def_two", lang: "ium", tags: ["ios","iosonly"]), + ] + + let sectionTwo = Section(name: "section_two") + sectionTwo.definitions = [ + getDefinition(name: "s2_def_one", lang: "ium", tags: ["ios","iosonly"]), + getDefinition(name: "s2_def_two", lang: "ium", tags: ["droid","droidonly"]) + ] + + let sectionThree = Section(name: "section_three") + sectionThree.definitions = [ + getDefinition(name: "s3_def_one", lang: "ium", tags: ["droid","droidonly"]), + getDefinition(name: "s3_def_two", lang: "ium", tags: ["droid","droidonly"]) + ] + + // When + let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], + lang: "ium", + tags: ["ios", "iosonly"], + staticVar: false, + extensionName: "GenTags") + // Expect Tags + let expect = """ + // Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion) + + import UIKit + + extension GenTags { + // MARK: - section_one + + /// Translation in ium : + /// Some translation + var s1_def_one: String { + "Some translation" + } + + /// Translation in ium : + /// Some translation + var s1_def_two: String { + "Some translation" + } + + // MARK: - section_two + + /// Translation in ium : + /// Some translation + var s2_def_one: String { + "Some translation" + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } +} -- 2.39.5 From fe85ff9551e064952bbeaa0e27909a0771aa1676 Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Tue, 6 Sep 2022 17:04:40 +0200 Subject: [PATCH 10/10] Delete old schemes --- .../xcschemes/ColorToolCore.xcscheme | 78 ------ .../xcschemes/FontToolCore.xcscheme | 78 ------ .../xcshareddata/xcschemes/Imagium.xcscheme | 78 ------ .../xcschemes/ResgenSwift-Package.xcscheme | 251 ------------------ 4 files changed, 485 deletions(-) delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/ColorToolCore.xcscheme delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/FontToolCore.xcscheme delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme delete mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ColorToolCore.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ColorToolCore.xcscheme deleted file mode 100644 index 2149977..0000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/ColorToolCore.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/FontToolCore.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/FontToolCore.xcscheme deleted file mode 100644 index cb899de..0000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/FontToolCore.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme deleted file mode 100644 index aa41879..0000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme deleted file mode 100644 index be2a28d..0000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme +++ /dev/null @@ -1,251 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- 2.39.5