Bugs fixes, Lint fixes, Refactoring
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
This commit is contained in:
parent
e3f90e0d48
commit
a54a264447
@ -1,4 +1,4 @@
|
|||||||
// Generated from ColorToolCore at 2022-07-22 15:38:00 +0000
|
// Generated by ResgenSwift.ColorTool 1.0.0
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Generated from FontToolCore
|
// Generated by ResgenSwift.FontTool 1.0.0
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@ -58,5 +58,4 @@ extension UIFont {
|
|||||||
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
// Images from sampleImages
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
@ -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
|
import UIKit
|
||||||
|
|
||||||
|
@ -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
|
// typelias Tags = String
|
||||||
|
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ struct ColorToolOptions: ParsableArguments {
|
|||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
@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() })
|
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var xcassetsPath: String
|
var xcassetsPath: String
|
||||||
@ -30,3 +30,9 @@ struct ColorToolOptions: ParsableArguments {
|
|||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ColorToolOptions {
|
||||||
|
var colorStyle: ColorStyle {
|
||||||
|
ColorStyle(rawValue: style) ?? .all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
67
Sources/ColorTool/Generator/ColorExtensionGenerator.swift
Normal file
67
Sources/ColorTool/Generator/ColorExtensionGenerator.swift
Normal file
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -10,17 +10,14 @@ import ToolCore
|
|||||||
|
|
||||||
struct ColorXcassetHelper {
|
struct ColorXcassetHelper {
|
||||||
|
|
||||||
let xcassetsPath: String
|
static func generateXcassetColors(colors: [ParsedColor], to xcassetsPath: String) {
|
||||||
let colors: [GenColor]
|
|
||||||
|
|
||||||
func generateXcassetColors() {
|
|
||||||
colors.forEach {
|
colors.forEach {
|
||||||
generateColorSetAssets(from: $0)
|
Self.generateColorSetAssets(from: $0, to: xcassetsPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ColorSet in XCAssets file
|
// Generate ColorSet in XCAssets file
|
||||||
private func generateColorSetAssets(from color: GenColor) {
|
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
|
||||||
// Create ColorSet
|
// Create ColorSet
|
||||||
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
||||||
Shell.shell("mkdir", "-p", "\(colorSetPath)")
|
Shell.shell("mkdir", "-p", "\(colorSetPath)")
|
13
Sources/ColorTool/Model/ColorStyle.swift
Normal file
13
Sources/ColorTool/Model/ColorStyle.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 29/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum ColorStyle: String, Decodable {
|
||||||
|
case light
|
||||||
|
case all
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// GenColor.swift
|
// ParsedColor.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 20/12/2021.
|
// Created by Thibaut Schmitt on 20/12/2021.
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct GenColor {
|
struct ParsedColor {
|
||||||
let name: String
|
let name: String
|
||||||
let light: String
|
let light: String
|
||||||
let dark: String
|
let dark: String
|
49
Sources/ColorTool/Parser/ColorFileParser.swift
Normal file
49
Sources/ColorTool/Parser/ColorFileParser.swift
Normal file
@ -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]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,134 +9,101 @@ import Foundation
|
|||||||
import ToolCore
|
import ToolCore
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
enum ColorStyle: String, Decodable {
|
|
||||||
case light
|
|
||||||
case all
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ColorTool: ParsableCommand {
|
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 defaultExtensionName = "UIColor"
|
||||||
static let assetsColorsFolderName = "Colors"
|
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
|
@OptionGroup var options: ColorToolOptions
|
||||||
|
|
||||||
var colorStyle: ColorStyle { ColorStyle(rawValue: options.style) ?? .all }
|
// MARK: - Run
|
||||||
var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" }
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[ColorTool] Starting colors generation")
|
print("[\(Self.toolName)] Starting colors generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate \(options.colorStyle) colors in xcassets \(options.xcassetsPath)")
|
||||||
print("[ColorTool] Will use inputFile \(options.inputFile) to generate \(colorStyle) colors in xcassets \(options.xcassetsPath)")
|
|
||||||
print("[ColorTool] Extension will be \(extensionFilePath)")
|
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
let fileManager = FileManager()
|
guard checkRequirements() else { return }
|
||||||
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
|
||||||
let error = ColorToolError.fileNotExists(options.xcassetsPath)
|
print("[\(Self.toolName)] Will generate colors")
|
||||||
print(error.localizedDescription)
|
|
||||||
ColorTool.exit(withError: error)
|
// 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()
|
||||||
|
|
||||||
|
// Check if input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = ColorToolError.fileNotExists(options.inputFile)
|
let error = ColorToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
ColorTool.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// 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: extensionFilePath) else {
|
||||||
print("[ColorTool] Colors are already up to date :) ")
|
print("[\(Self.toolName)] Colors are already up to date :) ")
|
||||||
return
|
return false
|
||||||
}
|
|
||||||
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...) {
|
return true
|
||||||
// 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)
|
|
||||||
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]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func isUIColorExtension() -> Bool {
|
private func deleteCurrentColors() {
|
||||||
options.extensionName == Self.defaultExtensionName
|
Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,17 +11,21 @@ enum FontToolError: Error {
|
|||||||
case fcScan(String, Int32, String?)
|
case fcScan(String, Int32, String?)
|
||||||
case inputFolderNotFound(String)
|
case inputFolderNotFound(String)
|
||||||
case fileNotExists(String)
|
case fileNotExists(String)
|
||||||
|
case writeExtension(String, String)
|
||||||
|
|
||||||
var localizedDescription: String {
|
var localizedDescription: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .fcScan(let path, let code, let output):
|
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):
|
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):
|
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)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,32 @@ import Foundation
|
|||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
class FontToolHelper {
|
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
|
// Get a enumerator for all files
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
guard fileManager.fileExists(atPath: inputFolder) else {
|
guard fileManager.fileExists(atPath: inputFolder) else {
|
||||||
@ -32,11 +57,6 @@ class FontToolHelper {
|
|||||||
return fontsFileNames
|
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 {
|
private static func getFontName(atPath path: String) -> String {
|
||||||
//print("fc-scan --format %{postscriptname} \(path)")
|
//print("fc-scan --format %{postscriptname} \(path)")
|
||||||
// Get real font name
|
// Get real font name
|
||||||
|
22
Sources/FontTool/Generator/FontPlistGenerator.swift
Normal file
22
Sources/FontTool/Generator/FontPlistGenerator.swift
Normal file
@ -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 = "<key>UIAppFonts</key>\n\t<array>\n"
|
||||||
|
fonts
|
||||||
|
.compactMap { $0 }
|
||||||
|
.forEach {
|
||||||
|
plistData += "\t\t<string>\($0)</string>\n"
|
||||||
|
}
|
||||||
|
plistData += "\t</array>\n*/"
|
||||||
|
|
||||||
|
return plistData
|
||||||
|
}
|
||||||
|
}
|
83
Sources/FontTool/Generator/FontToolContentGenerator.swift
Normal file
83
Sources/FontTool/Generator/FontToolContentGenerator.swift
Normal file
@ -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 {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
32
Sources/FontTool/Model/FontName.swift
Normal file
32
Sources/FontTool/Model/FontName.swift
Normal file
@ -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)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
16
Sources/FontTool/Parser/FontFileParser.swift
Normal file
16
Sources/FontTool/Parser/FontFileParser.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -10,19 +10,72 @@ import ToolCore
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct FontTool: ParsableCommand {
|
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"
|
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
|
@OptionGroup var options: FontOptions
|
||||||
|
|
||||||
var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" }
|
// MARK: - Run
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[FontTool] Starting fonts generation")
|
print("[\(Self.toolName)] Starting fonts generation")
|
||||||
|
|
||||||
// Check requirements
|
// 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()
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Check input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = FontToolError.fileNotExists(options.inputFile)
|
let error = FontToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
@ -31,86 +84,11 @@ struct FontTool: ParsableCommand {
|
|||||||
|
|
||||||
// Check if needed to regenerate
|
// 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: extensionFilePath) else {
|
||||||
print("[FontTool] Fonts are already up to date :) ")
|
print("[\(Self.toolName)] 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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let fontsFilesnamesWithPath = fontsFilenames.map { "\(inputFolder)/\($0)" }
|
return true
|
||||||
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)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = "<key>UIAppFonts</key>\n\t<array>\n"
|
|
||||||
fontsNames
|
|
||||||
.compactMap { $0 }
|
|
||||||
.forEach {
|
|
||||||
plistData += "\t\t<string>\($0)</string>\n"
|
|
||||||
}
|
|
||||||
plistData += "\t</array>\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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class ImageExtensionGenerator {
|
|||||||
|
|
||||||
// MARK: - Extension files
|
// 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
|
// Get header/footer
|
||||||
let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
|
let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
|
||||||
let extensionFooter = Self.getFooter()
|
let extensionFooter = Self.getFooter()
|
@ -22,7 +22,7 @@ class XcassetsGenerator {
|
|||||||
|
|
||||||
// MARK: - Assets generation
|
// MARK: - Assets generation
|
||||||
|
|
||||||
func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) {
|
func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) {
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
let svgConverter = Imagium.getSvgConverterPath()
|
let svgConverter = Imagium.getSvgConverterPath()
|
||||||
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
||||||
@ -30,30 +30,30 @@ class XcassetsGenerator {
|
|||||||
var generatedAssetsPaths = [String]()
|
var generatedAssetsPaths = [String]()
|
||||||
|
|
||||||
// Generate new assets
|
// Generate new assets
|
||||||
imagesToGenerate.forEach { imageToGen in
|
imagesToGenerate.forEach { parsedImage in
|
||||||
// Get image path
|
// Get image path
|
||||||
let imageData: (path: String, ext: String) = {
|
let imageData: (path: String, ext: String) = {
|
||||||
for subfile in allSubFiles {
|
for subfile in allSubFiles {
|
||||||
if subfile.hasSuffix("/" + imageToGen.name + ".svg") {
|
if subfile.hasSuffix("/" + parsedImage.name + ".svg") {
|
||||||
return (subfile, "svg")
|
return (subfile, "svg")
|
||||||
}
|
}
|
||||||
if subfile.hasSuffix("/" + imageToGen.name + ".png") {
|
if subfile.hasSuffix("/" + parsedImage.name + ".png") {
|
||||||
return (subfile, "png")
|
return (subfile, "png")
|
||||||
}
|
}
|
||||||
if subfile.hasSuffix("/" + imageToGen.name + ".jpg") {
|
if subfile.hasSuffix("/" + parsedImage.name + ".jpg") {
|
||||||
return (subfile, "jpg")
|
return (subfile, "jpg")
|
||||||
}
|
}
|
||||||
if subfile.hasSuffix("/" + imageToGen.name + ".jepg") {
|
if subfile.hasSuffix("/" + parsedImage.name + ".jepg") {
|
||||||
return (subfile, "jepg")
|
return (subfile, "jepg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let error = ImagiumError.unknownImageExtension(imageToGen.name)
|
let error = ImagiumError.unknownImageExtension(parsedImage.name)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Imagium.exit(withError: error)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Create imageset folder
|
// Create imageset folder
|
||||||
let imagesetName = "\(imageToGen.name).imageset"
|
let imagesetName = "\(parsedImage.name).imageset"
|
||||||
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
||||||
Shell.shell("mkdir", "-p", imagesetPath)
|
Shell.shell("mkdir", "-p", imagesetPath)
|
||||||
|
|
||||||
@ -61,18 +61,18 @@ class XcassetsGenerator {
|
|||||||
generatedAssetsPaths.append(imagesetName)
|
generatedAssetsPaths.append(imagesetName)
|
||||||
|
|
||||||
// Generate output images path
|
// Generate output images path
|
||||||
let output1x = "\(imagesetPath)/\(imageToGen.name).\(XcassetsGenerator.outputImageExtension)"
|
let output1x = "\(imagesetPath)/\(parsedImage.name).\(XcassetsGenerator.outputImageExtension)"
|
||||||
let output2x = "\(imagesetPath)/\(imageToGen.name)@2x.\(XcassetsGenerator.outputImageExtension)"
|
let output2x = "\(imagesetPath)/\(parsedImage.name)@2x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
let output3x = "\(imagesetPath)/\(imageToGen.name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
|
||||||
// Check if we need to convert image
|
// Check if we need to convert image
|
||||||
if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) {
|
if self.shouldBypassGeneration(for: parsedImage, xcassetImagePath: output1x) {
|
||||||
print("\(imageToGen.name) -> Not regenerating")
|
print("\(parsedImage.name) -> Not regenerating")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert image
|
// Convert image
|
||||||
let convertArguments = imageToGen.convertArguments
|
let convertArguments = parsedImage.convertArguments
|
||||||
if imageData.ext == "svg" {
|
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 -h 300 -o path/to/output.png
|
||||||
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -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
|
// Write Content.json
|
||||||
let imagesetContentJson = imageToGen.contentJson
|
let imagesetContentJson = parsedImage.contentJson
|
||||||
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
||||||
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
|
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
|
||||||
Shell.shell("touch", "\(contentJsonFilePath)")
|
Shell.shell("touch", "\(contentJsonFilePath)")
|
||||||
@ -111,7 +111,7 @@ class XcassetsGenerator {
|
|||||||
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
||||||
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
|
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
|
||||||
|
|
||||||
print("\(imageToGen.name) -> Generated")
|
print("\(parsedImage.name) -> Generated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success info
|
// Success info
|
||||||
@ -147,7 +147,7 @@ class XcassetsGenerator {
|
|||||||
|
|
||||||
// MARK: - Helpers: bypass generation
|
// 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 {
|
guard forceGeneration == false else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// ParsedImage.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 24/01/2022.
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct ImageToGen {
|
struct ParsedImage {
|
||||||
let name: String
|
let name: String
|
||||||
let tags: String
|
let tags: String
|
||||||
let width: Int
|
let width: Int
|
13
Sources/Imagium/Model/PlatormTag.swift
Normal file
13
Sources/Imagium/Model/PlatormTag.swift
Normal file
@ -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"
|
||||||
|
}
|
@ -9,11 +9,11 @@ import Foundation
|
|||||||
|
|
||||||
class ImageFileParser {
|
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 inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||||
|
|
||||||
var imagesToGenerate = [ImageToGen]()
|
var imagesToGenerate = [ParsedImage]()
|
||||||
|
|
||||||
// Parse file
|
// Parse file
|
||||||
stringsByLines.forEach {
|
stringsByLines.forEach {
|
||||||
@ -36,7 +36,7 @@ class ImageFileParser {
|
|||||||
return Int(splittedLine[3])!
|
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)
|
imagesToGenerate.append(image)
|
||||||
}
|
}
|
||||||
|
|
@ -9,21 +9,22 @@ import Foundation
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
enum PlatormTag: String {
|
|
||||||
case droid = "d"
|
|
||||||
case ios = "i"
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Imagium: ParsableCommand {
|
struct Imagium: ParsableCommand {
|
||||||
|
|
||||||
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
static var configuration = 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"
|
version: "0.1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "Imagium"
|
static let toolName = "Imagium"
|
||||||
static let defaultExtensionName = "UIImage"
|
static let defaultExtensionName = "UIImage"
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" }
|
var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" }
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
var inputFilenameWithoutExt: String {
|
var inputFilenameWithoutExt: String {
|
||||||
@ -32,8 +33,12 @@ struct Imagium: ParsableCommand {
|
|||||||
.lastPathComponent
|
.lastPathComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Command Options
|
||||||
|
|
||||||
@OptionGroup var options: ImagiumOptions
|
@OptionGroup var options: ImagiumOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting images generation")
|
print("[\(Self.toolName)] Starting images generation")
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ class StringsFileGenerator {
|
|||||||
|
|
||||||
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
@ -10,37 +10,49 @@ import ToolCore
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct Stringium: ParsableCommand {
|
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 toolName = "Stringium"
|
||||||
static let defaultExtensionName = "String"
|
static let defaultExtensionName = "String"
|
||||||
static let noTranslationTag: String = "notranslation"
|
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 extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
|
|
||||||
var langs: [String] {
|
|
||||||
options.langsRaw
|
|
||||||
.split(separator: " ")
|
|
||||||
.map { String($0) }
|
|
||||||
}
|
|
||||||
var inputFilenameWithoutExt: String {
|
var inputFilenameWithoutExt: String {
|
||||||
URL(fileURLWithPath: options.inputFile)
|
URL(fileURLWithPath: options.inputFile)
|
||||||
.deletingPathExtension()
|
.deletingPathExtension()
|
||||||
.lastPathComponent
|
.lastPathComponent
|
||||||
}
|
}
|
||||||
var stringsFileOutputPath: String {
|
|
||||||
var outputPath = options.outputPathRaw
|
var generateStaticVariable: Bool {
|
||||||
if outputPath.last == "/" {
|
options.extensionName == Self.defaultExtensionName
|
||||||
outputPath = String(outputPath.dropLast())
|
|
||||||
}
|
|
||||||
return outputPath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Command options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
// arguments defined by another `ParsableArguments` type.
|
// arguments defined by another `ParsableArguments` type.
|
||||||
@OptionGroup var options: StringiumOptions
|
@OptionGroup var options: StringiumOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting strings generation")
|
print("[\(Self.toolName)] Starting strings generation")
|
||||||
|
|
||||||
@ -54,17 +66,17 @@ struct Stringium: ParsableCommand {
|
|||||||
|
|
||||||
// Generate strings files
|
// Generate strings files
|
||||||
StringsFileGenerator.writeStringsFiles(sections: sections,
|
StringsFileGenerator.writeStringsFiles(sections: sections,
|
||||||
langs: langs,
|
langs: options.langs,
|
||||||
defaultLang: options.defaultLang,
|
defaultLang: options.defaultLang,
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
tags: options.tags,
|
||||||
outputPath: stringsFileOutputPath,
|
outputPath: options.stringsFileOutputPath,
|
||||||
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
||||||
defaultLang: options.defaultLang,
|
defaultLang: options.defaultLang,
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
tags: options.tags,
|
||||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
staticVar: generateStaticVariable,
|
||||||
inputFilename: inputFilenameWithoutExt,
|
inputFilename: inputFilenameWithoutExt,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: extensionFilePath)
|
||||||
@ -85,13 +97,13 @@ struct Stringium: ParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Langs
|
// Langs
|
||||||
guard langs.isEmpty == false else {
|
guard options.langs.isEmpty == false else {
|
||||||
let error = StringiumError.langsListEmpty
|
let error = StringiumError.langsListEmpty
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Stringium.exit(withError: error)
|
Stringium.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard langs.contains(options.defaultLang) else {
|
guard options.langs.contains(options.defaultLang) else {
|
||||||
let error = StringiumError.defaultLangsNotInLangs
|
let error = StringiumError.defaultLangsNotInLangs
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Stringium.exit(withError: error)
|
Stringium.exit(withError: error)
|
||||||
|
@ -16,20 +16,45 @@ struct StringiumOptions: ParsableArguments {
|
|||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@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.")
|
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
||||||
var langsRaw: String
|
fileprivate var langsRaw: String
|
||||||
|
|
||||||
@Option(help: "Default langs.")
|
@Option(help: "Default langs.")
|
||||||
var defaultLang: String
|
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() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
|
@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
|
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 = ""
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,19 +10,42 @@ import ToolCore
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct Tags: ParsableCommand {
|
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 toolName = "Tags"
|
||||||
static let defaultExtensionName = "Tags"
|
static let defaultExtensionName = "Tags"
|
||||||
static let noTranslationTag: String = "notranslation"
|
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 extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||||
|
var generateStaticVariable: Bool {
|
||||||
|
options.extensionName == Self.defaultExtensionName
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Command Options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
// arguments defined by another `ParsableArguments` type.
|
// arguments defined by another `ParsableArguments` type.
|
||||||
@OptionGroup var options: TagsOptions
|
@OptionGroup var options: TagsOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting tagss generation")
|
print("[\(Self.toolName)] Starting tagss generation")
|
||||||
|
|
||||||
@ -38,7 +61,7 @@ struct Tags: ParsableCommand {
|
|||||||
TagsGenerator.writeExtensionFiles(sections: sections,
|
TagsGenerator.writeExtensionFiles(sections: sections,
|
||||||
lang: options.lang,
|
lang: options.lang,
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
staticVar: generateStaticVariable,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: extensionFilePath)
|
||||||
|
|
||||||
|
@ -10,22 +10,35 @@ import ToolCore
|
|||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct Twine: ParsableCommand {
|
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 toolName = "Twine"
|
||||||
static let defaultExtensionName = "String"
|
static let defaultExtensionName = "String"
|
||||||
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
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)
|
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
||||||
.deletingPathExtension()
|
.deletingPathExtension()
|
||||||
.lastPathComponent
|
.lastPathComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Command Options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
// The `@OptionGroup` attribute includes the flags, options, and
|
||||||
// arguments defined by another `ParsableArguments` type.
|
// arguments defined by another `ParsableArguments` type.
|
||||||
@OptionGroup var options: TwineOptions
|
@OptionGroup var options: TwineOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting strings generation")
|
print("[\(Self.toolName)] Starting strings generation")
|
||||||
|
|
||||||
@ -35,7 +48,7 @@ struct Twine: ParsableCommand {
|
|||||||
print("[\(Self.toolName)] Will generate strings")
|
print("[\(Self.toolName)] Will generate strings")
|
||||||
|
|
||||||
// Generate strings files (lproj files)
|
// Generate strings files (lproj files)
|
||||||
for lang in langs {
|
for lang in options.langs {
|
||||||
Shell.shell(Self.twineExecutable,
|
Shell.shell(Self.twineExecutable,
|
||||||
"generate-localization-file", options.inputFile,
|
"generate-localization-file", options.inputFile,
|
||||||
"--lang", "\(lang)",
|
"--lang", "\(lang)",
|
||||||
@ -68,13 +81,13 @@ struct Twine: ParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Langs
|
// Langs
|
||||||
guard langs.isEmpty == false else {
|
guard options.langs.isEmpty == false else {
|
||||||
let error = TwineError.langsListEmpty
|
let error = TwineError.langsListEmpty
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Twine.exit(withError: error)
|
Twine.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard langs.contains(options.defaultLang) else {
|
guard options.langs.contains(options.defaultLang) else {
|
||||||
let error = TwineError.defaultLangsNotInLangs
|
let error = TwineError.defaultLangsNotInLangs
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Twine.exit(withError: error)
|
Twine.exit(withError: error)
|
||||||
|
@ -19,7 +19,7 @@ struct TwineOptions: ParsableArguments {
|
|||||||
var outputPath: String
|
var outputPath: String
|
||||||
|
|
||||||
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
||||||
var langsRaw: String
|
fileprivate var langsRaw: String
|
||||||
|
|
||||||
@Option(help: "Default langs.")
|
@Option(help: "Default langs.")
|
||||||
var defaultLang: String
|
var defaultLang: String
|
||||||
@ -27,3 +27,11 @@ struct TwineOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TwineOptions {
|
||||||
|
var langs: [String] {
|
||||||
|
langsRaw
|
||||||
|
.split(separator: " ")
|
||||||
|
.map { String($0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user