From a54a2644479294e0d47da6e194d59b0f3382613f Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Mon, 29 Aug 2022 13:38:46 +0200 Subject: [PATCH] Bugs fixes, Lint fixes, Refactoring --- .../Generated/UIColor+GenAllScript.swift | 2 +- .../Fonts/Generated/UIFont+GenAllScript.swift | 63 ++++--- .../Generated/UIImage+GenAllScript.swift | 2 +- .../Generated/String+GenAllScript.swift | 2 +- .../Tags/Generated/Tags+GenAllScript.swift | 2 +- .../ColorTool/ColorExtensionGenerator.swift | 42 ----- Sources/ColorTool/ColorToolOptions.swift | 8 +- .../Generator/ColorExtensionGenerator.swift | 67 +++++++ .../{ => Generator}/ColorXcassetHelper.swift | 9 +- Sources/ColorTool/Model/ColorStyle.swift | 13 ++ Sources/ColorTool/{ => Model}/GenColor.swift | 4 +- .../ColorTool/Parser/ColorFileParser.swift | 49 +++++ Sources/ColorTool/main.swift | 171 +++++++----------- .../FontTool/FontToolContentGenerator.swift | 59 ------ Sources/FontTool/FontToolError.swift | 10 +- Sources/FontTool/FontToolHelper.swift | 32 +++- .../Generator/FontPlistGenerator.swift | 22 +++ .../Generator/FontToolContentGenerator.swift | 83 +++++++++ Sources/FontTool/Model/FontName.swift | 32 ++++ Sources/FontTool/Parser/FontFileParser.swift | 16 ++ Sources/FontTool/main.swift | 142 ++++++--------- .../FileManagerExtensions.swift | 0 .../ImageExtensionGenerator.swift | 2 +- .../{ => Generator}/XcassetsGenerator.swift | 34 ++-- .../Imagium/{ => Model}/ConvertArgument.swift | 0 .../ParsedImage.swift} | 4 +- Sources/Imagium/Model/PlatormTag.swift | 13 ++ .../{ => Parser}/ImageFileParser.swift | 6 +- Sources/Imagium/main.swift | 17 +- .../Generator/StringsFileGenerator.swift | 2 +- Sources/Strings/Stringium/Stringium.swift | 52 ++++-- .../Strings/Stringium/StringiumOptions.swift | 31 +++- Sources/Strings/Tag/Tags.swift | 29 ++- Sources/Strings/Twine/Twine.swift | 23 ++- Sources/Strings/Twine/TwineOptions.swift | 10 +- 35 files changed, 652 insertions(+), 401 deletions(-) delete mode 100644 Sources/ColorTool/ColorExtensionGenerator.swift create mode 100644 Sources/ColorTool/Generator/ColorExtensionGenerator.swift rename Sources/ColorTool/{ => Generator}/ColorXcassetHelper.swift (80%) create mode 100644 Sources/ColorTool/Model/ColorStyle.swift rename Sources/ColorTool/{ => Model}/GenColor.swift (98%) create mode 100644 Sources/ColorTool/Parser/ColorFileParser.swift delete mode 100644 Sources/FontTool/FontToolContentGenerator.swift create mode 100644 Sources/FontTool/Generator/FontPlistGenerator.swift create mode 100644 Sources/FontTool/Generator/FontToolContentGenerator.swift create mode 100644 Sources/FontTool/Model/FontName.swift create mode 100644 Sources/FontTool/Parser/FontFileParser.swift rename Sources/Imagium/{ => Extensions}/FileManagerExtensions.swift (100%) rename Sources/Imagium/{ => Generator}/ImageExtensionGenerator.swift (94%) rename Sources/Imagium/{ => Generator}/XcassetsGenerator.swift (83%) rename Sources/Imagium/{ => Model}/ConvertArgument.swift (100%) rename Sources/Imagium/{ImageToGen.swift => Model/ParsedImage.swift} (98%) create mode 100644 Sources/Imagium/Model/PlatormTag.swift rename Sources/Imagium/{ => Parser}/ImageFileParser.swift (86%) diff --git a/SampleFiles/Colors/Generated/UIColor+GenAllScript.swift b/SampleFiles/Colors/Generated/UIColor+GenAllScript.swift index 43de86c..b86c911 100644 --- a/SampleFiles/Colors/Generated/UIColor+GenAllScript.swift +++ b/SampleFiles/Colors/Generated/UIColor+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated from ColorToolCore at 2022-07-22 15:38:00 +0000 +// Generated by ResgenSwift.ColorTool 1.0.0 import UIKit diff --git a/SampleFiles/Fonts/Generated/UIFont+GenAllScript.swift b/SampleFiles/Fonts/Generated/UIFont+GenAllScript.swift index 398823b..8ea97fa 100644 --- a/SampleFiles/Fonts/Generated/UIFont+GenAllScript.swift +++ b/SampleFiles/Fonts/Generated/UIFont+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated from FontToolCore +// Generated by ResgenSwift.FontTool 1.0.0 import UIKit @@ -19,44 +19,43 @@ extension UIFont { // MARK: - Getter - static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoItalic.rawValue, size: size)! - } + static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoItalic.rawValue, size: size)! + } - static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoLightItalic.rawValue, size: size)! - } + static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoLightItalic.rawValue, size: size)! + } - static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoHairline.rawValue, size: size)! - } + static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoHairline.rawValue, size: size)! + } - static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoBold.rawValue, size: size)! - } + static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoBold.rawValue, size: size)! + } - static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoBlack.rawValue, size: size)! - } + static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoBlack.rawValue, size: size)! + } - static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoRegular.rawValue, size: size)! - } + static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoRegular.rawValue, size: size)! + } - static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)! - } + static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)! + } - static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)! - } + static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)! + } - static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoLight.rawValue, size: size)! - } - - static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in - UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)! - } + static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoLight.rawValue, size: size)! + } + static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)! + } } \ No newline at end of file diff --git a/SampleFiles/Images/Generated/UIImage+GenAllScript.swift b/SampleFiles/Images/Generated/UIImage+GenAllScript.swift index 56f64d3..0d1bf02 100644 --- a/SampleFiles/Images/Generated/UIImage+GenAllScript.swift +++ b/SampleFiles/Images/Generated/UIImage+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated from Imagium at 2022-07-22 15:38:05 +0000 +// Generated by ResgenSwift.Imagium 1.0.0 // Images from sampleImages import UIKit diff --git a/SampleFiles/Strings/Generated/String+GenAllScript.swift b/SampleFiles/Strings/Generated/String+GenAllScript.swift index 2538963..98cb8ef 100644 --- a/SampleFiles/Strings/Generated/String+GenAllScript.swift +++ b/SampleFiles/Strings/Generated/String+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated from Strings-Stringium at 2022-07-22 15:38:03 +0000 +// Generated by ResgenSwift.Strings.Stringium 1.0.0 import UIKit diff --git a/SampleFiles/Tags/Generated/Tags+GenAllScript.swift b/SampleFiles/Tags/Generated/Tags+GenAllScript.swift index 3a617c5..01fc191 100644 --- a/SampleFiles/Tags/Generated/Tags+GenAllScript.swift +++ b/SampleFiles/Tags/Generated/Tags+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated from Strings-Tags at 2022-07-22 15:38:04 +0000 +// Generated by ResgenSwift.Strings.Tags 1.0.0 // typelias Tags = String diff --git a/Sources/ColorTool/ColorExtensionGenerator.swift b/Sources/ColorTool/ColorExtensionGenerator.swift deleted file mode 100644 index 75e8442..0000000 --- a/Sources/ColorTool/ColorExtensionGenerator.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// ColorExtensionGenerator.swift -// -// -// Created by Thibaut Schmitt on 20/12/2021. -// - -import Foundation -import ToolCore - -struct ColorExtensionGenerator { - - let colors: [GenColor] - let extensionClassname: String - let isUIColorExtension: Bool - - func getHeader() -> String { - """ - // Generated by ResgenSwift.ColorToolCore \(ResgenSwiftVersion) - - import UIKit - - extension \(extensionClassname) {\n - """ - } - - func getFooter() -> String { - """ - } - """ - } - - func getProperties() -> String { - colors.map { - if extensionClassname == ColorTool.defaultExtensionName { - return $0.getColorStaticProperty() - } - return $0.getColorProperty() - } - .joined(separator: "\n\n") - } -} diff --git a/Sources/ColorTool/ColorToolOptions.swift b/Sources/ColorTool/ColorToolOptions.swift index 0cda982..e0c6ee3 100644 --- a/Sources/ColorTool/ColorToolOptions.swift +++ b/Sources/ColorTool/ColorToolOptions.swift @@ -16,7 +16,7 @@ struct ColorToolOptions: ParsableArguments { var inputFile: String @Option(help: "Color style to generate: light for light colors only, or all for dark and light colors") - var style: String + fileprivate var style: String @Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var xcassetsPath: String @@ -30,3 +30,9 @@ struct ColorToolOptions: ParsableArguments { @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift") var extensionSuffix: String = "" } + +extension ColorToolOptions { + var colorStyle: ColorStyle { + ColorStyle(rawValue: style) ?? .all + } +} diff --git a/Sources/ColorTool/Generator/ColorExtensionGenerator.swift b/Sources/ColorTool/Generator/ColorExtensionGenerator.swift new file mode 100644 index 0000000..9847900 --- /dev/null +++ b/Sources/ColorTool/Generator/ColorExtensionGenerator.swift @@ -0,0 +1,67 @@ +// +// ColorExtensionGenerator.swift +// +// +// Created by Thibaut Schmitt on 20/12/2021. +// + +import Foundation +import ToolCore + +struct ColorExtensionGenerator { + + let colors: [ParsedColor] + 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), + Self.getProperties(for: colors, withStaticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + + // Write content + let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) + do { + try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + } catch (let error) { + let error = ColorToolError.writeExtension(extensionFilePath, error.localizedDescription) + print(error.localizedDescription) + ColorTool.exit(withError: error) + } + } + + private static func getHeader(extensionClassname: String) -> String { + """ + // Generated by ResgenSwift.\(ColorTool.toolName) \(ResgenSwiftVersion) + + import UIKit + + extension \(extensionClassname) {\n + """ + } + + private static func getFooter() -> String { + """ + } + """ + } + + private static func getProperties(for colors: [ParsedColor], withStaticVar staticVar: Bool) -> String { + colors.map { + if staticVar { + return $0.getColorStaticProperty() + } + return $0.getColorProperty() + } + .joined(separator: "\n\n") + } +} diff --git a/Sources/ColorTool/ColorXcassetHelper.swift b/Sources/ColorTool/Generator/ColorXcassetHelper.swift similarity index 80% rename from Sources/ColorTool/ColorXcassetHelper.swift rename to Sources/ColorTool/Generator/ColorXcassetHelper.swift index 6eb3191..1223e5e 100644 --- a/Sources/ColorTool/ColorXcassetHelper.swift +++ b/Sources/ColorTool/Generator/ColorXcassetHelper.swift @@ -10,17 +10,14 @@ import ToolCore struct ColorXcassetHelper { - let xcassetsPath: String - let colors: [GenColor] - - func generateXcassetColors() { + static func generateXcassetColors(colors: [ParsedColor], to xcassetsPath: String) { colors.forEach { - generateColorSetAssets(from: $0) + Self.generateColorSetAssets(from: $0, to: xcassetsPath) } } // Generate ColorSet in XCAssets file - private func generateColorSetAssets(from color: GenColor) { + private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) { // Create ColorSet let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset" Shell.shell("mkdir", "-p", "\(colorSetPath)") diff --git a/Sources/ColorTool/Model/ColorStyle.swift b/Sources/ColorTool/Model/ColorStyle.swift new file mode 100644 index 0000000..aae3d97 --- /dev/null +++ b/Sources/ColorTool/Model/ColorStyle.swift @@ -0,0 +1,13 @@ +// +// File.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +enum ColorStyle: String, Decodable { + case light + case all +} diff --git a/Sources/ColorTool/GenColor.swift b/Sources/ColorTool/Model/GenColor.swift similarity index 98% rename from Sources/ColorTool/GenColor.swift rename to Sources/ColorTool/Model/GenColor.swift index 2258fb0..458f337 100644 --- a/Sources/ColorTool/GenColor.swift +++ b/Sources/ColorTool/Model/GenColor.swift @@ -1,5 +1,5 @@ // -// GenColor.swift +// ParsedColor.swift // // // Created by Thibaut Schmitt on 20/12/2021. @@ -7,7 +7,7 @@ import Foundation -struct GenColor { +struct ParsedColor { let name: String let light: String let dark: String diff --git a/Sources/ColorTool/Parser/ColorFileParser.swift b/Sources/ColorTool/Parser/ColorFileParser.swift new file mode 100644 index 0000000..64f5a26 --- /dev/null +++ b/Sources/ColorTool/Parser/ColorFileParser.swift @@ -0,0 +1,49 @@ +// +// File.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +class ColorFileParser { + static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] { + // Get content of input file + let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) + 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 + .removeTrailingWhitespace() + .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") + return nil + } + + let colorContent = colorLineCleanedUp.split(separator: " ") + + guard colorContent.count >= 2 else { + let error = ColorToolError.badFormat(colorLine) + print(error.localizedDescription) + ColorTool.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])) + } + } + } +} diff --git a/Sources/ColorTool/main.swift b/Sources/ColorTool/main.swift index 246a865..4ab321c 100644 --- a/Sources/ColorTool/main.swift +++ b/Sources/ColorTool/main.swift @@ -9,134 +9,101 @@ import Foundation import ToolCore import ArgumentParser -enum ColorStyle: String, Decodable { - case light - case all -} - struct ColorTool: ParsableCommand { + + // MARK: - CommandConfiguration + + static var configuration = CommandConfiguration( + abstract: "A utility for generate colors assets and their getters.", + version: "0.1.0" + ) + + // MARK: - Static + + static let toolName = "ColorTool" 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: ColorToolOptions - var colorStyle: ColorStyle { ColorStyle(rawValue: options.style) ?? .all } - var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } + // MARK: - Run public func run() throws { - print("[ColorTool] Starting colors generation") - - print("[ColorTool] Will use inputFile \(options.inputFile) to generate \(colorStyle) colors in xcassets \(options.xcassetsPath)") - print("[ColorTool] Extension will be \(extensionFilePath)") + 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 } + + print("[\(Self.toolName)] Will generate colors") + + // Delete current colors + deleteCurrentColors() + + // Get colors to generate + let parsedColors = ColorFileParser.parse(options.inputFile, + colorStyle: options.colorStyle) + + // Generate all colors in xcassets + ColorXcassetHelper.generateXcassetColors(colors: parsedColors, + to: options.xcassetsPath) + + // Generate extension + ColorExtensionGenerator.writeExtensionFile(colors: parsedColors, + staticVar: generateStaticVariable, + extensionName: options.extensionName, + extensionFilePath: extensionFilePath) + + print("[\(Self.toolName)] Colors generated") + } + + // MARK: - Requirements + + private func checkRequirements() -> Bool { let fileManager = FileManager() - guard fileManager.fileExists(atPath: options.xcassetsPath) else { - let error = ColorToolError.fileNotExists(options.xcassetsPath) - print(error.localizedDescription) - ColorTool.exit(withError: error) - } + + // Check if input file exists guard fileManager.fileExists(atPath: options.inputFile) else { let error = ColorToolError.fileNotExists(options.inputFile) print(error.localizedDescription) ColorTool.exit(withError: error) } - - // Check if needed to regenerate - guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { - print("[ColorTool] Colors are already up to date :) ") - return - } - print("[ColorTool] Will generate colors") - // Delete current colors - Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*") - - // Get colors - let colorsToGen = getColorsGen() - - // Generate all colors in xcassets - let colorAssetHelper = ColorXcassetHelper(xcassetsPath: options.xcassetsPath, colors: colorsToGen) - colorAssetHelper.generateXcassetColors() - - // Generate extension - let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen, - extensionClassname: options.extensionName, - isUIColorExtension: isUIColorExtension()) - let extensionHeader = extensionGenerator.getHeader() - let extensionProperties = extensionGenerator.getProperties() - let extensionFooter = extensionGenerator.getFooter() - - generateExtensionFile(extensionHeader, extensionProperties, extensionFooter) - - print("[ColorTool] Colors generated") - } - - private func generateExtensionFile(_ 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) - } catch (let error) { - let error = ColorToolError.writeExtension(extensionFilePath, error.localizedDescription) + // Check if xcassets file exists + guard fileManager.fileExists(atPath: options.xcassetsPath) else { + let error = ColorToolError.fileNotExists(options.xcassetsPath) print(error.localizedDescription) ColorTool.exit(withError: error) } - } - - private func getColorsGen() -> [GenColor] { - // Get content of input file - let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8) - 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 - .removeTrailingWhitespace() - .replacingOccurrences(of: "=", with: "") // Keep compat with current file format - - guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else { - print("[ColorTool] ⚠️ 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) - print(error.localizedDescription) - ColorTool.exit(withError: error) - } - - switch colorStyle { - case .light: - return GenColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) - - case .all: - if colorContent.count == 3 { - return GenColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2])) - } - return GenColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1])) - } + // Check if needed to regenerate + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { + print("[\(Self.toolName)] Colors are already up to date :) ") + return false } + + return true } // MARK: - Helpers - private func isUIColorExtension() -> Bool { - options.extensionName == Self.defaultExtensionName + private func deleteCurrentColors() { + Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*") } } diff --git a/Sources/FontTool/FontToolContentGenerator.swift b/Sources/FontTool/FontToolContentGenerator.swift deleted file mode 100644 index 5d82dba..0000000 --- a/Sources/FontTool/FontToolContentGenerator.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// FontToolContentGenerator.swift -// -// -// Created by Thibaut Schmitt on 13/12/2021. -// - -import Foundation -import ToolCore - -class FontToolContentGenerator { - - static func getExtensionHeader(fontsNames: [String]) -> String { - """ - // Generated by ResgenSwift.FontToolCore \(ResgenSwiftVersion) - - import UIKit - - """ - } - - static func getFontNameEnum(fontsNames: [String]) -> String { - var enumDefinition = "\tenum FontName: String {\n" - - fontsNames.forEach { - //debugPrint("Name: \($0.removeCharacters(from: "[]+-_"))") - enumDefinition += "\t\tcase \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n" - } - enumDefinition += "\t}\n" - - return enumDefinition - } - - static func getFontMethods(fontsNames: [String], isUIFontExtension: Bool) -> String { - var methods = "\t// MARK: - Getter\n" - - fontsNames - .unique() - .forEach { - let fontNameSanitize = $0.removeCharacters(from: "[]+-_") - - if isUIFontExtension { - methods += """ - \n\tstatic let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in - \tUIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)! - \t}\n - """ - } else { - methods += """ - \n\tfunc \(fontNameSanitize)(withSize size: CGFloat) -> UIFont { - \tUIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)! - \t}\n - """ - } - } - - return methods - } -} diff --git a/Sources/FontTool/FontToolError.swift b/Sources/FontTool/FontToolError.swift index 1eb6512..8c68069 100644 --- a/Sources/FontTool/FontToolError.swift +++ b/Sources/FontTool/FontToolError.swift @@ -11,17 +11,21 @@ enum FontToolError: Error { case fcScan(String, Int32, String?) case inputFolderNotFound(String) case fileNotExists(String) + case writeExtension(String, String) var localizedDescription: String { switch self { case .fcScan(let path, let code, let output): - return "error:[FontTool] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no 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")" case .inputFolderNotFound(let inputFolder): - return " error:[FontTool] Input folder not found: \(inputFolder)" + return " error:[\(FontTool.toolName)] Input folder not found: \(inputFolder)" case .fileNotExists(let filename): - return " error:[FontTool] File \(filename) does not exists" + return " error:[\(FontTool.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)" } } } diff --git a/Sources/FontTool/FontToolHelper.swift b/Sources/FontTool/FontToolHelper.swift index af758fa..22123e8 100644 --- a/Sources/FontTool/FontToolHelper.swift +++ b/Sources/FontTool/FontToolHelper.swift @@ -9,7 +9,32 @@ import Foundation import ToolCore class FontToolHelper { - static func getFontsFilenames(fromInputFolder inputFolder: String) -> [String] { + + static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] { + let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder) + .filter { fontNameWithPath in + let fontName = URL(fileURLWithPath: fontNameWithPath) + .deletingPathExtension() + .lastPathComponent + + if fonts.contains(fontName) { + return true + } + return false + } + + let fontsFilesnamesWithPath = fontsFilenames.map { + "\(inputFolder)/\($0)" + } + + return fontsFilesnamesWithPath.compactMap { + Self.getFontName(atPath: $0) + } + } + + // MARK: - Private + + private static func getFontsFilenames(fromInputFolder inputFolder: String) -> [String] { // Get a enumerator for all files let fileManager = FileManager() guard fileManager.fileExists(atPath: inputFolder) else { @@ -32,11 +57,6 @@ class FontToolHelper { return fontsFileNames } - static func getFontsNames(fontsFileNames: [String]) -> [String] { - // Get font name (font name and font file name can be different) - fontsFileNames.compactMap { getFontName(atPath: $0) } - } - private static func getFontName(atPath path: String) -> String { //print("fc-scan --format %{postscriptname} \(path)") // Get real font name diff --git a/Sources/FontTool/Generator/FontPlistGenerator.swift b/Sources/FontTool/Generator/FontPlistGenerator.swift new file mode 100644 index 0000000..e127fef --- /dev/null +++ b/Sources/FontTool/Generator/FontPlistGenerator.swift @@ -0,0 +1,22 @@ +// +// File.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +class FontPlistGenerator { + static func generatePlistUIAppsFontContent(for fonts: [FontName]) -> String { + var plistData = "UIAppFonts\n\t\n" + fonts + .compactMap { $0 } + .forEach { + plistData += "\t\t\($0)\n" + } + plistData += "\t\n*/" + + return plistData + } +} diff --git a/Sources/FontTool/Generator/FontToolContentGenerator.swift b/Sources/FontTool/Generator/FontToolContentGenerator.swift new file mode 100644 index 0000000..018071a --- /dev/null +++ b/Sources/FontTool/Generator/FontToolContentGenerator.swift @@ -0,0 +1,83 @@ +// +// FontToolContentGenerator.swift +// +// +// Created by Thibaut Schmitt on 13/12/2021. +// + +import Foundation +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), + Self.getFontNameEnum(fontsNames: fontsNames), + Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + + // Write content + let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) + do { + try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) + } catch (let error) { + let error = FontToolError.writeExtension(extensionFilePath, error.localizedDescription) + print(error.localizedDescription) + FontTool.exit(withError: error) + } + } + + private static func getHeader(extensionClassname: String) -> String { + """ + // Generated by ResgenSwift.\(FontTool.toolName) \(ResgenSwiftVersion) + + import UIKit + + extension \(extensionClassname) {\n + """ + } + + private static func getFontNameEnum(fontsNames: [String]) -> String { + var enumDefinition = "\tenum FontName: String {\n" + + fontsNames.forEach { + enumDefinition += "\t\tcase \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n" + } + enumDefinition += "\t}\n" + + return enumDefinition + } + + private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String { + let pragma = "\t// MARK: - Getter" + + var propertiesOrMethods: [String] = fontsNames + .unique() + .map { + if staticVar { + return $0.staticProperty + } else { + return $0.method + } + } + + propertiesOrMethods.insert(pragma, at: 0) + return propertiesOrMethods.joined(separator: "\n\n") + } + + private static func getFooter() -> String { + """ + } + """ + } +} diff --git a/Sources/FontTool/Model/FontName.swift b/Sources/FontTool/Model/FontName.swift new file mode 100644 index 0000000..e904ee1 --- /dev/null +++ b/Sources/FontTool/Model/FontName.swift @@ -0,0 +1,32 @@ +// +// File.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +typealias FontName = String + +extension FontName { + var fontNameSanitize: String { + self.removeCharacters(from: "[]+-_") + } + + var method: String { + """ + func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont { + UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)! + } + """ + } + + var staticProperty: String { + """ + static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in + UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)! + } + """ + } +} diff --git a/Sources/FontTool/Parser/FontFileParser.swift b/Sources/FontTool/Parser/FontFileParser.swift new file mode 100644 index 0000000..bbf1a89 --- /dev/null +++ b/Sources/FontTool/Parser/FontFileParser.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +class FontFileParser { + static func parse(_ inputFile: String) -> [String] { + let inputFileContent = try! String(contentsOfFile: inputFile, + encoding: .utf8) + return inputFileContent.components(separatedBy: CharacterSet.newlines) + } +} diff --git a/Sources/FontTool/main.swift b/Sources/FontTool/main.swift index 2949b5a..7992a69 100644 --- a/Sources/FontTool/main.swift +++ b/Sources/FontTool/main.swift @@ -10,19 +10,72 @@ import ToolCore import ArgumentParser struct FontTool: ParsableCommand { - static var configuration = CommandConfiguration(abstract: "Generate fonts plist info and extension to access fonts easily.") + + // MARK: - CommandConfiguration + + static var configuration = CommandConfiguration( + abstract: "A utility to generate an helpful etension to access your custom font from code and also Info.plist UIAppsFont content.", + version: "0.1.0" + ) + + // MARK: - Static + + static let toolName = "FontTool" 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: FontOptions - var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } - var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } + // MARK: - Run public func run() throws { - print("[FontTool] Starting fonts generation") + print("[\(Self.toolName)] Starting fonts generation") // Check requirements + guard checkRequirements() else { return } + + print("[\(Self.toolName)] Will generate fonts") + + // Get fonts to generate + let fontsToGenerate = FontFileParser.parse(options.inputFile) + + // Get real font names + let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath + let fontsNames = FontToolHelper.getFontPostScriptName(for: fontsToGenerate, + inputFolder: inputFolder) + + // Generate extension + FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames, + staticVar: generateStaticVariable, + extensionName: options.extensionName, + extensionFilePath: extensionFilePath) + + print("Info.plist information:") + print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))") + + print("[\(Self.toolName)] Fonts generated") + } + + // MARK: - Requirements + + private func checkRequirements() -> Bool { let fileManager = FileManager() + + // Check input file exists guard fileManager.fileExists(atPath: options.inputFile) else { let error = FontToolError.fileNotExists(options.inputFile) print(error.localizedDescription) @@ -31,86 +84,11 @@ struct FontTool: ParsableCommand { // Check if needed to regenerate guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else { - print("[FontTool] Fonts are already up to date :) ") - return - } - print("[FontTool] Will generate fonts") - - // Get fonts to generate - let fontsToGenerate = getFontsToGenerate() - - let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath - let fontsFilenames = FontToolHelper - .getFontsFilenames(fromInputFolder: inputFolder) - .filter { fontNameWithPath in - let fontName = URL(fileURLWithPath: fontNameWithPath) - .deletingPathExtension() - .lastPathComponent - - if fontsToGenerate.contains(fontName) { - return true - } - return false - } - - let fontsFilesnamesWithPath = fontsFilenames.map { "\(inputFolder)/\($0)" } - let fontsNames = FontToolHelper.getFontsNames(fontsFileNames: fontsFilesnamesWithPath) - - // Adding fontsFilenames to header (ex: path/to/font.ttf) to make check of regeneration faster - let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsFilenames) - - let extensionDefinitionOpening = "extension \(options.extensionName) {\n" - let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsNames) - let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsNames, isUIFontExtension: isUIFontExtension()) - let extensionDefinitionClosing = "}" - - generateExtensionFile(extensionHeader, - extensionDefinitionOpening, - extensionFontsNamesEnum, - extensionFontsMethods, - extensionDefinitionClosing) - - print("Info.plist information:") - print("\(generatePlistUIAppFonts(fontsNames: fontsNames))") - - print("[FontTool] Fonts generated") - } - - private func generateExtensionFile(_ args: String...) { - // Create file if not exists - let fileManager = FileManager() - if fileManager.fileExists(atPath: extensionFilePath) == false { - Shell.shell("touch", "\(extensionFilePath)") + print("[\(Self.toolName)] Fonts are already up to date :) ") + return false } - // Create extension content - let extensionContent = args.joined(separator: "\n") - - // Write content - let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) - try! extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8) - } - - private func generatePlistUIAppFonts(fontsNames: [String]) -> String { - var plistData = "UIAppFonts\n\t\n" - fontsNames - .compactMap { $0 } - .forEach { - plistData += "\t\t\($0)\n" - } - plistData += "\t\n*/" - - return plistData - } - // MARK: - Helpers - - private func getFontsToGenerate() -> [String] { - let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8) - return inputFileContent.components(separatedBy: CharacterSet.newlines) - } - - private func isUIFontExtension() -> Bool { - options.extensionName == Self.defaultExtensionName + return true } } diff --git a/Sources/Imagium/FileManagerExtensions.swift b/Sources/Imagium/Extensions/FileManagerExtensions.swift similarity index 100% rename from Sources/Imagium/FileManagerExtensions.swift rename to Sources/Imagium/Extensions/FileManagerExtensions.swift diff --git a/Sources/Imagium/ImageExtensionGenerator.swift b/Sources/Imagium/Generator/ImageExtensionGenerator.swift similarity index 94% rename from Sources/Imagium/ImageExtensionGenerator.swift rename to Sources/Imagium/Generator/ImageExtensionGenerator.swift index f7fde30..9dcf1e2 100644 --- a/Sources/Imagium/ImageExtensionGenerator.swift +++ b/Sources/Imagium/Generator/ImageExtensionGenerator.swift @@ -12,7 +12,7 @@ class ImageExtensionGenerator { // MARK: - Extension files - static func writeStringsFiles(images: [ImageToGen], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) { + 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() diff --git a/Sources/Imagium/XcassetsGenerator.swift b/Sources/Imagium/Generator/XcassetsGenerator.swift similarity index 83% rename from Sources/Imagium/XcassetsGenerator.swift rename to Sources/Imagium/Generator/XcassetsGenerator.swift index 39ae382..17cee8d 100644 --- a/Sources/Imagium/XcassetsGenerator.swift +++ b/Sources/Imagium/Generator/XcassetsGenerator.swift @@ -22,7 +22,7 @@ class XcassetsGenerator { // MARK: - Assets generation - func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) { + func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) { let fileManager = FileManager() let svgConverter = Imagium.getSvgConverterPath() let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath) @@ -30,30 +30,30 @@ class XcassetsGenerator { var generatedAssetsPaths = [String]() // Generate new assets - imagesToGenerate.forEach { imageToGen in + imagesToGenerate.forEach { parsedImage in // Get image path let imageData: (path: String, ext: String) = { for subfile in allSubFiles { - if subfile.hasSuffix("/" + imageToGen.name + ".svg") { + if subfile.hasSuffix("/" + parsedImage.name + ".svg") { return (subfile, "svg") } - if subfile.hasSuffix("/" + imageToGen.name + ".png") { + if subfile.hasSuffix("/" + parsedImage.name + ".png") { return (subfile, "png") } - if subfile.hasSuffix("/" + imageToGen.name + ".jpg") { + if subfile.hasSuffix("/" + parsedImage.name + ".jpg") { return (subfile, "jpg") } - if subfile.hasSuffix("/" + imageToGen.name + ".jepg") { + if subfile.hasSuffix("/" + parsedImage.name + ".jepg") { return (subfile, "jepg") } } - let error = ImagiumError.unknownImageExtension(imageToGen.name) + let error = ImagiumError.unknownImageExtension(parsedImage.name) print(error.localizedDescription) Imagium.exit(withError: error) }() // Create imageset folder - let imagesetName = "\(imageToGen.name).imageset" + let imagesetName = "\(parsedImage.name).imageset" let imagesetPath = "\(xcassetsPath)/\(imagesetName)" Shell.shell("mkdir", "-p", imagesetPath) @@ -61,18 +61,18 @@ class XcassetsGenerator { generatedAssetsPaths.append(imagesetName) // Generate output images path - let output1x = "\(imagesetPath)/\(imageToGen.name).\(XcassetsGenerator.outputImageExtension)" - let output2x = "\(imagesetPath)/\(imageToGen.name)@2x.\(XcassetsGenerator.outputImageExtension)" - let output3x = "\(imagesetPath)/\(imageToGen.name)@3x.\(XcassetsGenerator.outputImageExtension)" + let output1x = "\(imagesetPath)/\(parsedImage.name).\(XcassetsGenerator.outputImageExtension)" + let output2x = "\(imagesetPath)/\(parsedImage.name)@2x.\(XcassetsGenerator.outputImageExtension)" + let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)" // Check if we need to convert image - if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) { - print("\(imageToGen.name) -> Not regenerating") + if self.shouldBypassGeneration(for: parsedImage, xcassetImagePath: output1x) { + print("\(parsedImage.name) -> Not regenerating") return } // Convert image - let convertArguments = imageToGen.convertArguments + let convertArguments = parsedImage.convertArguments if imageData.ext == "svg" { // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png @@ -102,7 +102,7 @@ class XcassetsGenerator { } // Write Content.json - let imagesetContentJson = imageToGen.contentJson + let imagesetContentJson = parsedImage.contentJson let contentJsonFilePath = "\(imagesetPath)/Contents.json" if fileManager.fileExists(atPath: contentJsonFilePath) == false { Shell.shell("touch", "\(contentJsonFilePath)") @@ -111,7 +111,7 @@ class XcassetsGenerator { let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath) try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8) - print("\(imageToGen.name) -> Generated") + print("\(parsedImage.name) -> Generated") } // Success info @@ -147,7 +147,7 @@ class XcassetsGenerator { // MARK: - Helpers: bypass generation - private func shouldBypassGeneration(for image: ImageToGen, xcassetImagePath: String) -> Bool { + private func shouldBypassGeneration(for image: ParsedImage, xcassetImagePath: String) -> Bool { guard forceGeneration == false else { return false } diff --git a/Sources/Imagium/ConvertArgument.swift b/Sources/Imagium/Model/ConvertArgument.swift similarity index 100% rename from Sources/Imagium/ConvertArgument.swift rename to Sources/Imagium/Model/ConvertArgument.swift diff --git a/Sources/Imagium/ImageToGen.swift b/Sources/Imagium/Model/ParsedImage.swift similarity index 98% rename from Sources/Imagium/ImageToGen.swift rename to Sources/Imagium/Model/ParsedImage.swift index 760c821..800facb 100644 --- a/Sources/Imagium/ImageToGen.swift +++ b/Sources/Imagium/Model/ParsedImage.swift @@ -1,5 +1,5 @@ // -// File.swift +// ParsedImage.swift // // // Created by Thibaut Schmitt on 24/01/2022. @@ -7,7 +7,7 @@ import Foundation -struct ImageToGen { +struct ParsedImage { let name: String let tags: String let width: Int diff --git a/Sources/Imagium/Model/PlatormTag.swift b/Sources/Imagium/Model/PlatormTag.swift new file mode 100644 index 0000000..b7fca9c --- /dev/null +++ b/Sources/Imagium/Model/PlatormTag.swift @@ -0,0 +1,13 @@ +// +// PlatormTag.swift +// +// +// Created by Thibaut Schmitt on 29/08/2022. +// + +import Foundation + +enum PlatormTag: String { + case droid = "d" + case ios = "i" +} diff --git a/Sources/Imagium/ImageFileParser.swift b/Sources/Imagium/Parser/ImageFileParser.swift similarity index 86% rename from Sources/Imagium/ImageFileParser.swift rename to Sources/Imagium/Parser/ImageFileParser.swift index 7355963..1b173fe 100644 --- a/Sources/Imagium/ImageFileParser.swift +++ b/Sources/Imagium/Parser/ImageFileParser.swift @@ -9,11 +9,11 @@ import Foundation class ImageFileParser { - static func parse(_ inputFile: String, platform: PlatormTag) -> [ImageToGen] { + static func parse(_ inputFile: String, platform: PlatormTag) -> [ParsedImage] { let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) let stringsByLines = inputFileContent.components(separatedBy: .newlines) - var imagesToGenerate = [ImageToGen]() + var imagesToGenerate = [ParsedImage]() // Parse file stringsByLines.forEach { @@ -36,7 +36,7 @@ class ImageFileParser { return Int(splittedLine[3])! }() - let image = ImageToGen(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height) + let image = ParsedImage(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height) imagesToGenerate.append(image) } diff --git a/Sources/Imagium/main.swift b/Sources/Imagium/main.swift index fb2c529..5102ff2 100644 --- a/Sources/Imagium/main.swift +++ b/Sources/Imagium/main.swift @@ -9,21 +9,22 @@ import Foundation import ArgumentParser import ToolCore -enum PlatormTag: String { - case droid = "d" - case ios = "i" -} - struct Imagium: ParsableCommand { + // MARK: - CommandConfiguration + static var configuration = CommandConfiguration( - abstract: "A utility for generate images.", + abstract: "A utility for generate images and an extension to access them easily.", version: "0.1.0" ) + // MARK: - Static + static let toolName = "Imagium" static let defaultExtensionName = "UIImage" + // MARK: - Properties + var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" } var inputFilenameWithoutExt: String { @@ -32,8 +33,12 @@ struct Imagium: ParsableCommand { .lastPathComponent } + // MARK: - Command Options + @OptionGroup var options: ImagiumOptions + // MARK: - Run + mutating func run() { print("[\(Self.toolName)] Starting images generation") diff --git a/Sources/Strings/Generator/StringsFileGenerator.swift b/Sources/Strings/Generator/StringsFileGenerator.swift index bc1579d..83971fc 100644 --- a/Sources/Strings/Generator/StringsFileGenerator.swift +++ b/Sources/Strings/Generator/StringsFileGenerator.swift @@ -137,7 +137,7 @@ class StringsFileGenerator { private static func getHeader(stringsFilename: String, extensionClassname: String) -> String { """ - // Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion) + // Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion) import UIKit diff --git a/Sources/Strings/Stringium/Stringium.swift b/Sources/Strings/Stringium/Stringium.swift index cbc7bf8..e11eaf1 100644 --- a/Sources/Strings/Stringium/Stringium.swift +++ b/Sources/Strings/Stringium/Stringium.swift @@ -10,37 +10,49 @@ import ToolCore import ArgumentParser struct Stringium: ParsableCommand { - static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.") + + // MARK: - Command Configuration + + static var configuration = CommandConfiguration( + abstract: "Generate strings with custom scripts.", + version: "0.1.0" + ) + + // MARK: - Static static let toolName = "Stringium" static let defaultExtensionName = "String" static let noTranslationTag: String = "notranslation" - var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } + // 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 langs: [String] { - options.langsRaw - .split(separator: " ") - .map { String($0) } - } var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile) .deletingPathExtension() .lastPathComponent } - var stringsFileOutputPath: String { - var outputPath = options.outputPathRaw - if outputPath.last == "/" { - outputPath = String(outputPath.dropLast()) - } - return outputPath + + var generateStaticVariable: Bool { + options.extensionName == Self.defaultExtensionName } + // MARK: - Command options + // The `@OptionGroup` attribute includes the flags, options, and // arguments defined by another `ParsableArguments` type. @OptionGroup var options: StringiumOptions + // MARK: - Run + mutating func run() { print("[\(Self.toolName)] Starting strings generation") @@ -54,17 +66,17 @@ struct Stringium: ParsableCommand { // Generate strings files StringsFileGenerator.writeStringsFiles(sections: sections, - langs: langs, + langs: options.langs, defaultLang: options.defaultLang, - tags: ["ios", "iosonly", Self.noTranslationTag], - outputPath: stringsFileOutputPath, + tags: options.tags, + outputPath: options.stringsFileOutputPath, inputFilenameWithoutExt: inputFilenameWithoutExt) // Generate extension StringsFileGenerator.writeExtensionFiles(sections: sections, defaultLang: options.defaultLang, - tags: ["ios", "iosonly", Self.noTranslationTag], - staticVar: options.extensionName == Self.defaultExtensionName, + tags: options.tags, + staticVar: generateStaticVariable, inputFilename: inputFilenameWithoutExt, extensionName: options.extensionName, extensionFilePath: extensionFilePath) @@ -85,13 +97,13 @@ struct Stringium: ParsableCommand { } // Langs - guard langs.isEmpty == false else { + guard options.langs.isEmpty == false else { let error = StringiumError.langsListEmpty print(error.localizedDescription) Stringium.exit(withError: error) } - guard langs.contains(options.defaultLang) else { + guard options.langs.contains(options.defaultLang) else { let error = StringiumError.defaultLangsNotInLangs print(error.localizedDescription) Stringium.exit(withError: error) diff --git a/Sources/Strings/Stringium/StringiumOptions.swift b/Sources/Strings/Stringium/StringiumOptions.swift index b5b0130..61d715a 100644 --- a/Sources/Strings/Stringium/StringiumOptions.swift +++ b/Sources/Strings/Stringium/StringiumOptions.swift @@ -16,20 +16,45 @@ struct StringiumOptions: ParsableArguments { var inputFile: String @Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) - var outputPathRaw: String + fileprivate var outputPathRaw: String @Option(name: .customLong("langs"), help: "Langs to generate.") - var langsRaw: String + fileprivate var langsRaw: String @Option(help: "Default langs.") var defaultLang: String + @Option(name: .customLong("tags"), help: "Tags to generate.") + fileprivate var tagsRaw: String = "ios iosonly iosOnly notranslation" + @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String @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 - @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+String{extensionSuffix}.swift") + @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+{extensionSuffix}.swift") var extensionSuffix: String = "" } + +extension StringiumOptions { + var stringsFileOutputPath: String { + var outputPath = outputPathRaw + if outputPath.last == "/" { + outputPath = String(outputPath.dropLast()) + } + return outputPath + } + + var langs: [String] { + langsRaw + .split(separator: " ") + .map { String($0) } + } + + var tags: [String] { + tagsRaw + .split(separator: " ") + .map { String($0) } + } +} diff --git a/Sources/Strings/Tag/Tags.swift b/Sources/Strings/Tag/Tags.swift index 18278cf..eacc793 100644 --- a/Sources/Strings/Tag/Tags.swift +++ b/Sources/Strings/Tag/Tags.swift @@ -10,19 +10,42 @@ import ToolCore import ArgumentParser struct Tags: ParsableCommand { - static var configuration = CommandConfiguration(abstract: "Generate tags extension file.") + + // MARK: - Command Configuration + + static var configuration = CommandConfiguration( + abstract: "Generate tags extension file.", + version: "0.1.0" + ) + + + // MARK: - Static static let toolName = "Tags" static let defaultExtensionName = "Tags" static let noTranslationTag: String = "notranslation" - var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" } + // 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 // The `@OptionGroup` attribute includes the flags, options, and // arguments defined by another `ParsableArguments` type. @OptionGroup var options: TagsOptions + // MARK: - Run + mutating func run() { print("[\(Self.toolName)] Starting tagss generation") @@ -38,7 +61,7 @@ struct Tags: ParsableCommand { TagsGenerator.writeExtensionFiles(sections: sections, lang: options.lang, tags: ["ios", "iosonly", Self.noTranslationTag], - staticVar: options.extensionName == Self.defaultExtensionName, + staticVar: generateStaticVariable, extensionName: options.extensionName, extensionFilePath: extensionFilePath) diff --git a/Sources/Strings/Twine/Twine.swift b/Sources/Strings/Twine/Twine.swift index 53626fd..7653d9c 100644 --- a/Sources/Strings/Twine/Twine.swift +++ b/Sources/Strings/Twine/Twine.swift @@ -10,22 +10,35 @@ import ToolCore import ArgumentParser struct Twine: ParsableCommand { - static var configuration = CommandConfiguration(abstract: "Generate strings with twine.") + + // MARK: - Command Configuration + + static var configuration = CommandConfiguration( + abstract: "Generate strings with twine.", + version: "0.1.0" + ) + + // MARK: - Static static let toolName = "Twine" static let defaultExtensionName = "String" static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine" - var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } } + // MARK: - Properties + var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile) .deletingPathExtension() .lastPathComponent } + // MARK: - Command Options + // The `@OptionGroup` attribute includes the flags, options, and // arguments defined by another `ParsableArguments` type. @OptionGroup var options: TwineOptions + // MARK: - Run + mutating func run() { print("[\(Self.toolName)] Starting strings generation") @@ -35,7 +48,7 @@ struct Twine: ParsableCommand { print("[\(Self.toolName)] Will generate strings") // Generate strings files (lproj files) - for lang in langs { + for lang in options.langs { Shell.shell(Self.twineExecutable, "generate-localization-file", options.inputFile, "--lang", "\(lang)", @@ -68,13 +81,13 @@ struct Twine: ParsableCommand { } // Langs - guard langs.isEmpty == false else { + guard options.langs.isEmpty == false else { let error = TwineError.langsListEmpty print(error.localizedDescription) Twine.exit(withError: error) } - guard langs.contains(options.defaultLang) else { + guard options.langs.contains(options.defaultLang) else { let error = TwineError.defaultLangsNotInLangs print(error.localizedDescription) Twine.exit(withError: error) diff --git a/Sources/Strings/Twine/TwineOptions.swift b/Sources/Strings/Twine/TwineOptions.swift index 4d1d559..1c020ec 100644 --- a/Sources/Strings/Twine/TwineOptions.swift +++ b/Sources/Strings/Twine/TwineOptions.swift @@ -19,7 +19,7 @@ struct TwineOptions: ParsableArguments { var outputPath: String @Option(name: .customLong("langs"), help: "Langs to generate.") - var langsRaw: String + fileprivate var langsRaw: String @Option(help: "Default langs.") var defaultLang: String @@ -27,3 +27,11 @@ struct TwineOptions: ParsableArguments { @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var extensionOutputPath: String } + +extension TwineOptions { + var langs: [String] { + langsRaw + .split(separator: " ") + .map { String($0) } + } +}