Refactor + Bugs fixes on Strings + Gestion de la génération des images
This commit is contained in:
@ -28,4 +28,24 @@ public class Shell {
|
||||
|
||||
return (terminationStatus: task.terminationStatus, output: output)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func shell(_ args: [String]) -> (terminationStatus: Int32, output: String?) {
|
||||
let task = Process()
|
||||
task.launchPath = "/usr/bin/env"
|
||||
task.arguments = args
|
||||
|
||||
let pipe = Pipe()
|
||||
task.standardOutput = pipe
|
||||
task.launch()
|
||||
task.waitUntilExit()
|
||||
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
|
||||
guard let output: String = String(data: data, encoding: .utf8) else {
|
||||
return (terminationStatus: task.terminationStatus, output: nil)
|
||||
}
|
||||
|
||||
return (terminationStatus: task.terminationStatus, output: output)
|
||||
}
|
||||
}
|
||||
|
32
Sources/ColorTool/ColorToolOptions.swift
Normal file
32
Sources/ColorTool/ColorToolOptions.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// ColorToolOptions.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 17/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
struct ColorToolOptions: ParsableArguments {
|
||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
||||
var style: String
|
||||
|
||||
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var xcassetsPath: String
|
||||
|
||||
@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 UIColor extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = ColorTool.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
@ -18,70 +18,51 @@ struct ColorTool: ParsableCommand {
|
||||
static let defaultExtensionName = "UIColor"
|
||||
static let assetsColorsFolderName = "Colors"
|
||||
|
||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
@OptionGroup var options: ColorToolOptions
|
||||
|
||||
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
||||
var style: String
|
||||
|
||||
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var xcassetsPath: String
|
||||
|
||||
@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 UIColor extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Self.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||
var extensionSuffix: String = ""
|
||||
|
||||
var colorStyle: ColorStyle { ColorStyle(rawValue: style) ?? .all }
|
||||
var extensionFileName: String { "\(extensionName)+Color\(extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
|
||||
var colorStyle: ColorStyle { ColorStyle(rawValue: options.style) ?? .all }
|
||||
var extensionFileName: String { "\(options.extensionName)+Color\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
|
||||
public func run() throws {
|
||||
print("[ColorTool] Starting colors generation")
|
||||
|
||||
print("[ColorTool] Will use inputFile \(inputFile) to generate \(colorStyle) colors in xcassets \(xcassetsPath)")
|
||||
print("[ColorTool] Will use inputFile \(options.inputFile) to generate \(colorStyle) colors in xcassets \(options.xcassetsPath)")
|
||||
print("[ColorTool] Extension will be \(extensionFilePath)")
|
||||
|
||||
// Check requirements
|
||||
let fileManager = FileManager()
|
||||
guard fileManager.fileExists(atPath: xcassetsPath) else {
|
||||
let error = ColorToolError.fileNotExists(xcassetsPath)
|
||||
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
||||
let error = ColorToolError.fileNotExists(options.xcassetsPath)
|
||||
print(error.localizedDescription)
|
||||
ColorTool.exit(withError: error)
|
||||
}
|
||||
guard fileManager.fileExists(atPath: inputFile) else {
|
||||
let error = ColorToolError.fileNotExists(inputFile)
|
||||
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: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
|
||||
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", "\(xcassetsPath)/Colors/*")
|
||||
Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
|
||||
|
||||
// Get colors
|
||||
let colorsToGen = getColorsGen()
|
||||
|
||||
// Generate all colors in xcassets
|
||||
let colorAssetHelper = ColorXcassetHelper(xcassetsPath: xcassetsPath, colors: colorsToGen)
|
||||
let colorAssetHelper = ColorXcassetHelper(xcassetsPath: options.xcassetsPath, colors: colorsToGen)
|
||||
colorAssetHelper.generateXcassetColors()
|
||||
|
||||
// Generate extension
|
||||
let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen,
|
||||
extensionClassname: extensionName,
|
||||
extensionClassname: options.extensionName,
|
||||
isUIColorExtension: isUIColorExtension())
|
||||
let extensionHeader = extensionGenerator.getHeader()
|
||||
let extensionProperties = extensionGenerator.getProperties()
|
||||
@ -115,8 +96,8 @@ struct ColorTool: ParsableCommand {
|
||||
|
||||
private func getColorsGen() -> [GenColor] {
|
||||
// Get content of input file
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||
let colorsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||
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
|
||||
@ -155,7 +136,7 @@ struct ColorTool: ParsableCommand {
|
||||
// MARK: - Helpers
|
||||
|
||||
private func isUIColorExtension() -> Bool {
|
||||
extensionName == Self.defaultExtensionName
|
||||
options.extensionName == Self.defaultExtensionName
|
||||
}
|
||||
}
|
||||
|
26
Sources/FontTool/FontOptions.swift
Normal file
26
Sources/FontTool/FontOptions.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// FontOptions.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 17/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
struct FontOptions: ParsableArguments {
|
||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@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 UIFont extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = FontTool.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
@ -10,39 +10,27 @@ import CLIToolCore
|
||||
import ArgumentParser
|
||||
|
||||
struct FontTool: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate fonts plist info and extension to access fonts easily.")
|
||||
static let defaultExtensionName = "UIFont"
|
||||
|
||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
@OptionGroup var options: FontOptions
|
||||
|
||||
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@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 UIFont extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Self.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||
var extensionSuffix: String = ""
|
||||
|
||||
var extensionFileName: String { "\(extensionName)+Font\(extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
|
||||
var extensionFileName: String { "\(options.extensionName)options.+Font\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
|
||||
public func run() throws {
|
||||
print("[FontTool] Starting fonts generation")
|
||||
|
||||
// Check requirements
|
||||
let fileManager = FileManager()
|
||||
guard fileManager.fileExists(atPath: inputFile) else {
|
||||
let error = FontToolError.fileNotExists(inputFile)
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = FontToolError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
FontTool.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[FontTool] Fonts are already up to date :) ")
|
||||
return
|
||||
}
|
||||
@ -51,7 +39,7 @@ struct FontTool: ParsableCommand {
|
||||
// Get fonts to generate
|
||||
let fontsToGenerate = getFontsToGenerate()
|
||||
|
||||
let inputFolder = URL(fileURLWithPath: inputFile).deletingLastPathComponent().relativePath
|
||||
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
|
||||
let fontsFilenames = FontToolHelper
|
||||
.getFontsFilenames(fromInputFolder: inputFolder)
|
||||
.filter { fontNameWithPath in
|
||||
@ -71,7 +59,7 @@ struct FontTool: ParsableCommand {
|
||||
// Adding fontsFilenames to header (ex: path/to/font.ttf) to make check of regeneration faster
|
||||
let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsFilenames)
|
||||
|
||||
let extensionDefinitionOpening = "extension \(extensionName) {\n"
|
||||
let extensionDefinitionOpening = "extension \(options.extensionName) {\n"
|
||||
let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsNames)
|
||||
let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsNames, isUIFontExtension: isUIFontExtension())
|
||||
let extensionDefinitionClosing = "}"
|
||||
@ -117,23 +105,13 @@ struct FontTool: ParsableCommand {
|
||||
// MARK: - Helpers
|
||||
|
||||
private func getFontsToGenerate() -> [String] {
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||
return inputFileContent.components(separatedBy: .newlines)
|
||||
let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8)
|
||||
return inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||
}
|
||||
|
||||
private func isUIFontExtension() -> Bool {
|
||||
extensionName == Self.defaultExtensionName
|
||||
options.extensionName == Self.defaultExtensionName
|
||||
}
|
||||
}
|
||||
|
||||
FontTool.main()
|
||||
|
||||
/*
|
||||
|
||||
1. R2Font extension
|
||||
swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated --extension-name R2Font
|
||||
|
||||
1. UIFont defualt extension (will gen static property)
|
||||
swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated
|
||||
|
||||
*/
|
13
Sources/Imagium/ConvertArgument.swift
Normal file
13
Sources/Imagium/ConvertArgument.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// ConvertArgument.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ConvertArgument {
|
||||
let width: String?
|
||||
let height: String?
|
||||
}
|
56
Sources/Imagium/FileManagerExtensions.swift
Normal file
56
Sources/Imagium/FileManagerExtensions.swift
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// FileManagerExtensions.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension FileManager {
|
||||
func getAllRegularFileIn(directory: String) -> [String] {
|
||||
var files = [String]()
|
||||
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
||||
let error = ImagiumError.unknown("Cannot enumerate file in \(directory)")
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
|
||||
for case let fileURL as URL in enumerator {
|
||||
do {
|
||||
let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey])
|
||||
if fileAttributes.isRegularFile! {
|
||||
files.append(fileURL.relativePath)
|
||||
}
|
||||
} catch {
|
||||
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func getAllImageSetFolderIn(directory: String) -> [String] {
|
||||
var files = [String]()
|
||||
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
||||
let error = ImagiumError.unknown("Cannot enumerate imageset directory in \(directory)")
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
|
||||
for case let fileURL as URL in enumerator {
|
||||
do {
|
||||
let fileAttributes = try fileURL.resourceValues(forKeys:[.isDirectoryKey])
|
||||
if fileAttributes.isDirectory! && fileURL.lastPathComponent.hasSuffix(".imageset") {
|
||||
files.append(fileURL.lastPathComponent)
|
||||
}
|
||||
} catch {
|
||||
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
}
|
87
Sources/Imagium/ImageExtensionGenerator.swift
Normal file
87
Sources/Imagium/ImageExtensionGenerator.swift
Normal file
@ -0,0 +1,87 @@
|
||||
//
|
||||
// ImageExtensionGenerator.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 14/02/2022.
|
||||
//
|
||||
|
||||
import CLIToolCore
|
||||
import Foundation
|
||||
|
||||
class ImageExtensionGenerator {
|
||||
|
||||
// MARK: - Extension files
|
||||
|
||||
static func writeStringsFiles(images: [ImageToGen], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
||||
// Get header/footer
|
||||
let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
|
||||
let extensionFooter = Self.getFooter()
|
||||
|
||||
// Create content
|
||||
let extensionContent: String = {
|
||||
var content = ""
|
||||
images.forEach { img in
|
||||
if staticVar {
|
||||
content += "\n\(img.getStaticImageProperty())"
|
||||
} else {
|
||||
content += "\n\(img.getImageProperty())"
|
||||
}
|
||||
content += "\n "
|
||||
}
|
||||
return content
|
||||
}()
|
||||
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// Generate extension
|
||||
Self.generateExtensionFile(extensionFilePath: extensionFilePath, extensionHeader, extensionContent, extensionFooter)
|
||||
}
|
||||
|
||||
// MARK: - pragm
|
||||
|
||||
private static func generateExtensionFile(extensionFilePath: String, _ args: String...) {
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// Create extension content
|
||||
let extensionContent = args.joined(separator: "\n")
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = ImagiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
|
||||
private static func getHeader(inputFilename: String, extensionClassname: String) -> String {
|
||||
"""
|
||||
// Generated from Imagium at \(Date())
|
||||
// Images from \(inputFilename)
|
||||
|
||||
import UIKit
|
||||
|
||||
extension \(extensionClassname) {
|
||||
"""
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
"""
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
//@objc var onboarding_foreground3: UIImage {
|
||||
// return UIImage(named: "onboarding_foreground3")!
|
||||
// }
|
47
Sources/Imagium/ImageFileParser.swift
Normal file
47
Sources/Imagium/ImageFileParser.swift
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// ImageFileParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ImageFileParser {
|
||||
|
||||
static func parse(_ inputFile: String, platform: PlatormTag) -> [ImageToGen] {
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||
|
||||
var imagesToGenerate = [ImageToGen]()
|
||||
|
||||
// Parse file
|
||||
stringsByLines.forEach {
|
||||
guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else {
|
||||
return
|
||||
}
|
||||
|
||||
let splittedLine = $0.split(separator: " ")
|
||||
|
||||
let width: Int = {
|
||||
if splittedLine[2] == "?" {
|
||||
return -1
|
||||
}
|
||||
return Int(splittedLine[2])!
|
||||
}()
|
||||
let height: Int = {
|
||||
if splittedLine[3] == "?" {
|
||||
return -1
|
||||
}
|
||||
return Int(splittedLine[3])!
|
||||
}()
|
||||
|
||||
let image = ImageToGen(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height)
|
||||
imagesToGenerate.append(image)
|
||||
}
|
||||
|
||||
return imagesToGenerate.filter {
|
||||
$0.tags.contains(platform.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
90
Sources/Imagium/ImageToGen.swift
Normal file
90
Sources/Imagium/ImageToGen.swift
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ImageToGen {
|
||||
let name: String
|
||||
let tags: String
|
||||
let width: Int
|
||||
let height: Int
|
||||
|
||||
// MARK: - Convert
|
||||
|
||||
var convertArguments: (x1: ConvertArgument, x2: ConvertArgument, x3: ConvertArgument) {
|
||||
var width1x = ""
|
||||
var height1x = ""
|
||||
var width2x = ""
|
||||
var height2x = ""
|
||||
var width3x = ""
|
||||
var height3x = ""
|
||||
|
||||
if width != -1 {
|
||||
width1x = "\(width)"
|
||||
width2x = "\(width * 2)"
|
||||
width3x = "\(width * 3)"
|
||||
}
|
||||
|
||||
if height != -1 {
|
||||
height1x = "\(height)"
|
||||
height2x = "\(height * 2)"
|
||||
height3x = "\(height * 3)"
|
||||
}
|
||||
|
||||
return (x1: ConvertArgument(width: width1x, height: height1x),
|
||||
x2: ConvertArgument(width: width2x, height: height2x),
|
||||
x3: ConvertArgument(width: width3x, height: height3x))
|
||||
}
|
||||
|
||||
// MARK: - Assets
|
||||
|
||||
var contentJson: String {
|
||||
"""
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "\(name).\(XcassetsGenerator.outputImageExtension)"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "\(name)@2x.\(XcassetsGenerator.outputImageExtension)"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "\(name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "ResgenSwift-Imagium"
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
// MARK: - Extension property
|
||||
|
||||
func getImageProperty() -> String {
|
||||
"""
|
||||
var \(name): UIImage {
|
||||
UIImage(named: "\(name)")!
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
func getStaticImageProperty() -> String {
|
||||
"""
|
||||
static var \(name): UIImage {
|
||||
UIImage(named: "\(name)")!
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
43
Sources/Imagium/ImagiumError.swift
Normal file
43
Sources/Imagium/ImagiumError.swift
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// ImagiumError.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ImagiumError: Error {
|
||||
case inputFolderNotFound(String)
|
||||
case fileNotExists(String)
|
||||
case unknownImageExtension(String)
|
||||
case getFileAttributed(String, String)
|
||||
case rsvgConvertNotFound
|
||||
case writeFile(String, String)
|
||||
case unknown(String)
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .inputFolderNotFound(let inputFolder):
|
||||
return " error:[\(Imagium.toolName)] Input folder not found: \(inputFolder)"
|
||||
|
||||
case .fileNotExists(let filename):
|
||||
return " error:[\(Imagium.toolName)] File \(filename) does not exists"
|
||||
|
||||
case .unknownImageExtension(let filename):
|
||||
return " error:[\(Imagium.toolName)] File \(filename) have an unhandled file extension. Cannot generate image."
|
||||
|
||||
case .getFileAttributed(let filename, let errorDescription):
|
||||
return " error:[\(Imagium.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)"
|
||||
|
||||
case .rsvgConvertNotFound:
|
||||
return " error:[\(Imagium.toolName)] Can't find rsvg-convert (can be installed with 'brew remove imagemagick && brew install imagemagick --with-librsvg')"
|
||||
|
||||
case .writeFile(let subErrorDescription, let filename):
|
||||
return " error:[\(Imagium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||
|
||||
case .unknown(let errorDescription):
|
||||
return " error:[\(Imagium.toolName)] Unknown error: \(errorDescription)"
|
||||
}
|
||||
}
|
||||
}
|
41
Sources/Imagium/ImagiumOptions.swift
Normal file
41
Sources/Imagium/ImagiumOptions.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// ImagiumOptions.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
struct ImagiumOptions: ParsableArguments {
|
||||
@Flag(name: .customShort("f"), help: "Should force script execution")
|
||||
var forceExecution = false
|
||||
|
||||
@Flag(name: .customShort("F"), help: "Regenerate all images")
|
||||
var forceExecutionAndGeneration = false
|
||||
|
||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(help: "Xcassets path where to generate images.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var xcassetsPath: String
|
||||
|
||||
@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 UIImage extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Imagium.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
|
||||
--xcassets-path "./Images/imagium.xcassets" \
|
||||
--extension-output-path "./Images/Generated" \
|
||||
--extension-name "UIImage" \
|
||||
--extension-suffix "GenAllScript"
|
||||
*/
|
189
Sources/Imagium/XcassetsGenerator.swift
Normal file
189
Sources/Imagium/XcassetsGenerator.swift
Normal file
@ -0,0 +1,189 @@
|
||||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CLIToolCore
|
||||
|
||||
class XcassetsGenerator {
|
||||
|
||||
static let outputImageExtension = "png"
|
||||
|
||||
let forceGeneration: Bool
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(forceGeneration: Bool) {
|
||||
self.forceGeneration = forceGeneration
|
||||
}
|
||||
|
||||
// MARK: - Assets generation
|
||||
|
||||
func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) {
|
||||
let fileManager = FileManager()
|
||||
let svgConverter = Imagium.getSvgConverterPath()
|
||||
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
||||
|
||||
var generatedAssetsPaths = [String]()
|
||||
|
||||
// Generate new assets
|
||||
imagesToGenerate.forEach { imageToGen in
|
||||
// Get image path
|
||||
let imageData: (path: String, ext: String) = {
|
||||
for subfile in allSubFiles {
|
||||
if subfile.hasSuffix("/" + imageToGen.name + ".svg") {
|
||||
return (subfile, "svg")
|
||||
}
|
||||
if subfile.hasSuffix("/" + imageToGen.name + ".png") {
|
||||
return (subfile, "png")
|
||||
}
|
||||
if subfile.hasSuffix("/" + imageToGen.name + ".jpg") {
|
||||
return (subfile, "jpg")
|
||||
}
|
||||
if subfile.hasSuffix("/" + imageToGen.name + ".jepg") {
|
||||
return (subfile, "jepg")
|
||||
}
|
||||
}
|
||||
let error = ImagiumError.unknownImageExtension(imageToGen.name)
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}()
|
||||
|
||||
// Create imageset folder
|
||||
let imagesetName = "\(imageToGen.name).imageset"
|
||||
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
||||
Shell.shell("mkdir", "-p", imagesetPath)
|
||||
|
||||
// Store managed images path
|
||||
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)"
|
||||
|
||||
// Check if we need to convert image
|
||||
if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) {
|
||||
print("\(imageToGen.name) -> Not regenerating")
|
||||
return
|
||||
}
|
||||
|
||||
// Convert image
|
||||
let convertArguments = imageToGen.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
|
||||
// /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
|
||||
var command1x = ["\(svgConverter)", "\(imageData.path)"]
|
||||
var command2x = ["\(svgConverter)", "\(imageData.path)"]
|
||||
var command3x = ["\(svgConverter)", "\(imageData.path)"]
|
||||
|
||||
self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
|
||||
self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
|
||||
self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
|
||||
|
||||
command1x.append(contentsOf: ["-o", output1x])
|
||||
command2x.append(contentsOf: ["-o", output2x])
|
||||
command3x.append(contentsOf: ["-o", output3x])
|
||||
|
||||
Shell.shell(command1x)
|
||||
Shell.shell(command2x)
|
||||
Shell.shell(command3x)
|
||||
} else {
|
||||
// convert path/to/image.png -resize 200x300 path/to/output.png
|
||||
// convert path/to/image.png -resize 200x path/to/output.png
|
||||
// convert path/to/image.png -resize x300 path/to/output.png
|
||||
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x)
|
||||
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x)
|
||||
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x)
|
||||
}
|
||||
|
||||
// Write Content.json
|
||||
let imagesetContentJson = imageToGen.contentJson
|
||||
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
||||
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
|
||||
Shell.shell("touch", "\(contentJsonFilePath)")
|
||||
}
|
||||
|
||||
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
||||
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
|
||||
|
||||
print("\(imageToGen.name) -> Generated")
|
||||
}
|
||||
|
||||
// Success info
|
||||
let generatedAssetsCount = generatedAssetsPaths.count
|
||||
print("Images generated: \(generatedAssetsCount)")
|
||||
|
||||
// Delete old assets
|
||||
let allImagesetName = Set(fileManager.getAllImageSetFolderIn(directory: xcassetsPath))
|
||||
let imagesetToRemove = allImagesetName.subtracting(Set(generatedAssetsPaths))
|
||||
|
||||
imagesetToRemove.forEach {
|
||||
print("Will remove: \($0)")
|
||||
}
|
||||
|
||||
imagesetToRemove.forEach { itemToRemove in
|
||||
try! fileManager.removeItem(atPath: "\(xcassetsPath)/\(itemToRemove)")
|
||||
}
|
||||
print("Removed \(imagesetToRemove.count) images")
|
||||
}
|
||||
|
||||
// MARK: - Helpers: SVG command
|
||||
|
||||
private func addConvertArgument(command: inout [String], convertArgument: ConvertArgument) {
|
||||
if let width = convertArgument.width, width.isEmpty == false {
|
||||
command.append("-w")
|
||||
command.append("\(width)")
|
||||
}
|
||||
if let height = convertArgument.height, height.isEmpty == false {
|
||||
command.append("-h")
|
||||
command.append("\(height)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers: bypass generation
|
||||
|
||||
private func shouldBypassGeneration(for image: ImageToGen, xcassetImagePath: String) -> Bool {
|
||||
guard forceGeneration == false else {
|
||||
return false
|
||||
}
|
||||
|
||||
let fileManager = FileManager()
|
||||
|
||||
// File not exists -> do not bypass
|
||||
guard fileManager.fileExists(atPath: xcassetImagePath) else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Info unavailable -> do not bypass
|
||||
let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath)
|
||||
let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath)
|
||||
guard taskWidth.terminationStatus == 0,
|
||||
taskHeight.terminationStatus == 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
let currentWidth = Int(taskWidth.output ?? "-1") ?? -1
|
||||
let currentheight = Int(taskHeight.output ?? "-1") ?? -1
|
||||
|
||||
// Info unavailable -> do not bypass
|
||||
guard currentWidth > 0 && currentheight > 0 else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check width and height
|
||||
if image.width != -1 && currentWidth == image.width {
|
||||
return true
|
||||
}
|
||||
if image.height != -1 && currentheight == image.height {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
111
Sources/Imagium/main.swift
Normal file
111
Sources/Imagium/main.swift
Normal file
@ -0,0 +1,111 @@
|
||||
//
|
||||
// Imagium.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 24/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
import CLIToolCore
|
||||
|
||||
enum PlatormTag: String {
|
||||
case droid = "d"
|
||||
case ios = "i"
|
||||
}
|
||||
|
||||
struct Imagium: ParsableCommand {
|
||||
|
||||
static var configuration = CommandConfiguration(
|
||||
abstract: "A utility for generate images.",
|
||||
version: "0.1.0"
|
||||
)
|
||||
|
||||
static let toolName = "Imagium"
|
||||
static let defaultExtensionName = "UIImage"
|
||||
|
||||
var extensionFileName: String { "\(options.extensionName)+Image\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
var inputFilenameWithoutExt: String {
|
||||
URL(fileURLWithPath: options.inputFile)
|
||||
.deletingPathExtension()
|
||||
.lastPathComponent
|
||||
}
|
||||
|
||||
@OptionGroup var options: ImagiumOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting images generation")
|
||||
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
print("[\(Self.toolName)] Will generate images")
|
||||
|
||||
// Parse input file
|
||||
let imagesToGenerate = ImageFileParser.parse(options.inputFile, platform: PlatormTag.ios)
|
||||
|
||||
// Generate xcassets files
|
||||
let inputFolder = URL(fileURLWithPath: options.inputFile)
|
||||
.deletingLastPathComponent()
|
||||
.relativePath
|
||||
let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration)
|
||||
xcassetsGenerator.generateXcassets(inputPath: inputFolder,
|
||||
imagesToGenerate: imagesToGenerate,
|
||||
xcassetsPath: options.xcassetsPath)
|
||||
|
||||
// Generate extension
|
||||
ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate,
|
||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||
inputFilename: inputFilenameWithoutExt,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: extensionFilePath)
|
||||
|
||||
|
||||
print("[\(Self.toolName)] Images generated")
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
guard options.forceExecutionAndGeneration == false else {
|
||||
return true
|
||||
}
|
||||
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = ImagiumError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
|
||||
// RSVG-Converter
|
||||
_ = Imagium.getSvgConverterPath()
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[\(Self.toolName)] Images are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
@discardableResult
|
||||
static func getSvgConverterPath() -> String {
|
||||
let taskSvgConverter = Shell.shell("which", "rsvg-convert")
|
||||
if taskSvgConverter.terminationStatus == 0 {
|
||||
return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
let error = ImagiumError.rsvgConvertNotFound
|
||||
print(error.localizedDescription)
|
||||
Imagium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
|
||||
Imagium.main()
|
@ -1 +1,13 @@
|
||||
print("Welcome ResgenSwift")
|
||||
|
||||
/*
|
||||
Make resgen as main command and font/color... as subcommands. It could look like:
|
||||
Resgen
|
||||
-> ColorTool
|
||||
-> FontTool
|
||||
-> ImageTool
|
||||
-> Strings
|
||||
-> Twine
|
||||
-> Stringium
|
||||
-> Tag
|
||||
*/
|
||||
|
@ -8,121 +8,120 @@
|
||||
import Foundation
|
||||
import CLIToolCore
|
||||
|
||||
extension Strings {
|
||||
class StringsFileGenerator {
|
||||
|
||||
// MARK: - Strings Files
|
||||
|
||||
static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
|
||||
var stringsFilesContent = [String: String]()
|
||||
for lang in langs {
|
||||
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
|
||||
defaultLang: defaultLang,
|
||||
tags: tags,
|
||||
sections: sections)
|
||||
}
|
||||
|
||||
// Write strings file content
|
||||
langs.forEach { lang in
|
||||
guard let fileContent = stringsFilesContent[lang] else { return }
|
||||
|
||||
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
||||
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
||||
do {
|
||||
try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
class StringsFileGenerator {
|
||||
|
||||
// MARK: - Strings Files
|
||||
|
||||
static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
|
||||
var stringsFilesContent = [String: String]()
|
||||
for lang in langs {
|
||||
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
|
||||
defaultLang: defaultLang,
|
||||
tags: tags,
|
||||
sections: sections)
|
||||
}
|
||||
|
||||
private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
||||
var stringsFileContent = """
|
||||
// Write strings file content
|
||||
langs.forEach { lang in
|
||||
guard let fileContent = stringsFilesContent[lang] else { return }
|
||||
|
||||
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
||||
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
||||
do {
|
||||
try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
||||
var stringsFileContent = """
|
||||
/**
|
||||
* Apple Strings File
|
||||
* Generated by ResgenSwift 1.0.0
|
||||
* Language: \(lang)
|
||||
*/\n
|
||||
"""
|
||||
|
||||
sections.forEach { section in
|
||||
// Check that at least one string will be generated
|
||||
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
|
||||
return // Go to next section
|
||||
}
|
||||
|
||||
stringsFileContent += "\n/********** \(section.name) **********/\n\n"
|
||||
section.definitions.forEach { definition in
|
||||
let translationOpt: String? = {
|
||||
if definition.tags.contains(Stringium.noTranslationTag) {
|
||||
return definition.translations[defaultLang]
|
||||
}
|
||||
return definition.translations[lang]
|
||||
}()
|
||||
|
||||
if let translation = translationOpt {
|
||||
stringsFileContent += "\"\(definition.name)\" = \"\(translation)\";\n\n"
|
||||
} else {
|
||||
let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stringsFileContent
|
||||
}
|
||||
|
||||
// MARK: - Extension file
|
||||
|
||||
static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
||||
let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
|
||||
let extensionFooter = Self.getFooter()
|
||||
|
||||
let extensionContent: String = {
|
||||
var content = ""
|
||||
sections.forEach { section in
|
||||
// Check that at least one string will be generated
|
||||
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
|
||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||
return // Go to next section
|
||||
}
|
||||
|
||||
stringsFileContent += "\n/********** \(section.name) **********/\n\n"
|
||||
content += "\n\t// MARK: - \(section.name)"
|
||||
section.definitions.forEach { definition in
|
||||
let translationOpt: String? = {
|
||||
if definition.tags.contains(Stringium.noTranslationTag) {
|
||||
return definition.translations[defaultLang]
|
||||
}
|
||||
return definition.translations[lang]
|
||||
}()
|
||||
|
||||
if let translation = translationOpt {
|
||||
stringsFileContent += "\"\(definition.name)\" = \"\(translation)\"\n\n"
|
||||
if staticVar {
|
||||
content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
|
||||
} else {
|
||||
let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
|
||||
}
|
||||
}
|
||||
content += "\n"
|
||||
}
|
||||
|
||||
return stringsFileContent
|
||||
return content
|
||||
}()
|
||||
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// MARK: - Extension file
|
||||
// Create extension content
|
||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||
|
||||
static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
||||
let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
|
||||
let extensionFooter = Self.getFooter()
|
||||
|
||||
let extensionContent: String = {
|
||||
var content = ""
|
||||
sections.forEach { section in
|
||||
// Check that at least one string will be generated
|
||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||
return // Go to next section
|
||||
}
|
||||
|
||||
content += "\n\t// MARK: - \(section.name)"
|
||||
section.definitions.forEach { definition in
|
||||
if staticVar {
|
||||
content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
|
||||
} else {
|
||||
content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
|
||||
}
|
||||
}
|
||||
content += "\n"
|
||||
}
|
||||
return content
|
||||
}()
|
||||
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// Create extension content
|
||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
||||
}
|
||||
|
||||
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
||||
"""
|
||||
// Generated from Strings-Stringium at \(Date())
|
||||
|
||||
@ -132,12 +131,11 @@ extension Strings {
|
||||
|
||||
extension \(extensionClassname) {
|
||||
"""
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
"""
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,54 +9,53 @@ import Foundation
|
||||
import CLIToolCore
|
||||
import CoreVideo
|
||||
|
||||
extension Strings {
|
||||
class TagsGenerator {
|
||||
static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||
let extensionHeader = Self.getHeader(extensionClassname: extensionName)
|
||||
let extensionFooter = Self.getFooter()
|
||||
|
||||
let extensionContent: String = {
|
||||
var content = ""
|
||||
sections.forEach { section in
|
||||
// Check that at least one string will be generated
|
||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||
return // Go to next section
|
||||
}
|
||||
|
||||
content += "\n\t// MARK: - \(section.name)"
|
||||
section.definitions.forEach { definition in
|
||||
if staticVar {
|
||||
content += "\n\n\(definition.getStaticProperty(forLang: lang))"
|
||||
} else {
|
||||
content += "\n\n\(definition.getProperty(forLang: lang))"
|
||||
}
|
||||
}
|
||||
content += "\n"
|
||||
class TagsGenerator {
|
||||
static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||
let extensionHeader = Self.getHeader(extensionClassname: extensionName)
|
||||
let extensionFooter = Self.getFooter()
|
||||
|
||||
let extensionContent: String = {
|
||||
var content = ""
|
||||
sections.forEach { section in
|
||||
// Check that at least one string will be generated
|
||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||
return // Go to next section
|
||||
}
|
||||
return content
|
||||
}()
|
||||
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// Create extension content
|
||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
|
||||
content += "\n\t// MARK: - \(section.name)"
|
||||
section.definitions.forEach { definition in
|
||||
if staticVar {
|
||||
content += "\n\n\(definition.getStaticProperty(forLang: lang))"
|
||||
} else {
|
||||
content += "\n\n\(definition.getProperty(forLang: lang))"
|
||||
}
|
||||
}
|
||||
content += "\n"
|
||||
}
|
||||
return content
|
||||
}()
|
||||
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
private static func getHeader(extensionClassname: String) -> String {
|
||||
// Create extension content
|
||||
let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
}
|
||||
|
||||
private static func getHeader(extensionClassname: String) -> String {
|
||||
"""
|
||||
// Generated from Strings-Tags at \(Date())
|
||||
|
||||
@ -66,12 +65,11 @@ extension Strings {
|
||||
|
||||
extension \(extensionClassname) {
|
||||
"""
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
"""
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,102 +7,100 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strings {
|
||||
class Definition {
|
||||
let name: String
|
||||
var tags = [String]()
|
||||
var comment: String?
|
||||
var translations = [String: String]()
|
||||
var reference: String?
|
||||
var isPlurals = false
|
||||
|
||||
var isValid: Bool {
|
||||
name.isEmpty == false &&
|
||||
translations.isEmpty == false
|
||||
class Definition {
|
||||
let name: String
|
||||
var tags = [String]()
|
||||
var comment: String?
|
||||
var translations = [String: String]()
|
||||
var reference: String?
|
||||
var isPlurals = false
|
||||
|
||||
var isValid: Bool {
|
||||
name.isEmpty == false &&
|
||||
translations.isEmpty == false
|
||||
}
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
static func match(_ line: String) -> Definition? {
|
||||
guard line.range(of: "\\[(.*?)]$", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
let definitionName = line
|
||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||
.removeLeadingTrailingWhitespace()
|
||||
|
||||
return Definition(name: definitionName)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
func getNSLocalizedStringProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
static func match(_ line: String) -> Definition? {
|
||||
guard line.range(of: "\\[(.*?)]", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let definitionName = line
|
||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||
.removeLeadingTrailingWhitespace()
|
||||
|
||||
return Definition(name: definitionName)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
func getNSLocalizedStringProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
return """
|
||||
return """
|
||||
/// Translation in \(lang) :
|
||||
/// \(translation)
|
||||
var \(name): String {
|
||||
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
return """
|
||||
return """
|
||||
/// Translation in \(lang) :
|
||||
/// \(translation)
|
||||
static var \(name): String {
|
||||
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
// MARK: - Raw strings
|
||||
|
||||
func getProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// MARK: - Raw strings
|
||||
|
||||
func getProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
return """
|
||||
return """
|
||||
/// Translation in \(lang) :
|
||||
/// \(translation)
|
||||
var \(name): String {
|
||||
"\(translation)"
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
func getStaticProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
func getStaticProperty(forLang lang: String) -> String {
|
||||
guard let translation = translations[lang] else {
|
||||
let error = StringiumError.langNotDefined(lang, name, reference != nil)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
return """
|
||||
return """
|
||||
/// Translation in \(lang) :
|
||||
/// \(translation)
|
||||
static var \(name): String {
|
||||
"\(translation)"
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,35 +7,33 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strings {
|
||||
class Section {
|
||||
let name: String // OnBoarding
|
||||
var definitions = [Definition]()
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
class Section {
|
||||
let name: String // OnBoarding
|
||||
var definitions = [Definition]()
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
static func match(_ line: String) -> Section? {
|
||||
guard line.range(of: "\\[\\[(.*?)]]$", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||
return nil
|
||||
}
|
||||
|
||||
static func match(_ line: String) -> Section? {
|
||||
guard line.range(of: "\\[\\[(.*?)]]", options: .regularExpression, range: nil, locale: nil) != nil else {
|
||||
return nil
|
||||
let sectionName = line
|
||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||
.removeLeadingTrailingWhitespace()
|
||||
return Section(name: sectionName)
|
||||
}
|
||||
|
||||
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
||||
let allTags = definitions.flatMap { $0.tags }
|
||||
|
||||
for tag in tags {
|
||||
if allTags.contains(tag) {
|
||||
return true
|
||||
}
|
||||
|
||||
let sectionName = line
|
||||
.replacingOccurrences(of: ["[", "]"], with: "")
|
||||
.removeLeadingTrailingWhitespace()
|
||||
return Section(name: sectionName)
|
||||
}
|
||||
|
||||
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
||||
let allTags = definitions.flatMap { $0.tags }
|
||||
|
||||
for tag in tags {
|
||||
if allTags.contains(tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -7,88 +7,91 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strings {
|
||||
|
||||
class TwineFileParser {
|
||||
static func parse(_ inputFile: String) -> [Section] {
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||
class TwineFileParser {
|
||||
static func parse(_ inputFile: String) -> [Section] {
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||
|
||||
var sections = [Section]()
|
||||
|
||||
// Parse file
|
||||
stringsByLines.forEach {
|
||||
// Section
|
||||
if let section = Section.match($0) {
|
||||
sections.append(section)
|
||||
return
|
||||
}
|
||||
|
||||
var sections = [Section]()
|
||||
// Definition
|
||||
if let definition = Definition.match($0) {
|
||||
sections.last?.definitions.append(definition)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse file
|
||||
stringsByLines.forEach {
|
||||
// Section
|
||||
if let section = Section.match($0) {
|
||||
sections.append(section)
|
||||
return
|
||||
}
|
||||
// Definition content
|
||||
if $0.isEmpty == false {
|
||||
// fr = Test => ["fr ", " Test"]
|
||||
let splitLine = $0
|
||||
.removeLeadingTrailingWhitespace()
|
||||
.split(separator: "=")
|
||||
|
||||
// Definition
|
||||
if let definition = Definition.match($0) {
|
||||
sections.last?.definitions.append(definition)
|
||||
return
|
||||
}
|
||||
guard let lastDefinition = sections.last?.definitions.last,
|
||||
let leftElement = splitLine.first else {
|
||||
return
|
||||
}
|
||||
|
||||
// Definition content
|
||||
if $0.isEmpty == false {
|
||||
// fr = Test => ["fr ", " Test"]
|
||||
let splitLine = $0
|
||||
.removeLeadingTrailingWhitespace()
|
||||
.split(separator: "=")
|
||||
|
||||
guard let lastDefinition = sections.last?.definitions.last,
|
||||
let leftElement = splitLine.first,
|
||||
let rightElement = splitLine.last else {
|
||||
return
|
||||
}
|
||||
|
||||
// "fr " => "fr"
|
||||
let leftHand = String(leftElement.dropLast())
|
||||
// " Test" => "Test"
|
||||
let rightHand = String(rightElement.dropFirst())
|
||||
|
||||
// Handle comments, tags and translation
|
||||
switch leftHand {
|
||||
case "comments":
|
||||
lastDefinition.comment = rightHand
|
||||
|
||||
case "tags":
|
||||
lastDefinition.tags = rightHand
|
||||
.split(separator: ",")
|
||||
.map { String($0) }
|
||||
|
||||
case "ref":
|
||||
lastDefinition.reference = rightHand
|
||||
|
||||
default:
|
||||
lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
|
||||
// Is a plurals strings (fr:one = Test)
|
||||
// Will be handle later
|
||||
//if leftHand.split(separator: ":").count > 1 {
|
||||
// lastDefinition.isPlurals = true
|
||||
//}
|
||||
let rightElement: String = {
|
||||
if let last = splitLine.last, splitLine.count == 2 {
|
||||
return String(last)
|
||||
}
|
||||
return ""
|
||||
}()
|
||||
|
||||
// "fr " => "fr"
|
||||
let leftHand = String(leftElement).removeTrailingWhitespace()
|
||||
// " Test" => "Test"
|
||||
let rightHand = String(rightElement).removeLeadingWhitespace()
|
||||
|
||||
// Handle comments, tags and translation
|
||||
switch leftHand {
|
||||
case "comments":
|
||||
lastDefinition.comment = rightHand
|
||||
|
||||
case "tags":
|
||||
lastDefinition.tags = rightHand
|
||||
.split(separator: ",")
|
||||
.map { String($0) }
|
||||
|
||||
case "ref":
|
||||
lastDefinition.reference = rightHand
|
||||
|
||||
default:
|
||||
lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
|
||||
// Is a plurals strings (fr:one = Test)
|
||||
// Will be handle later
|
||||
//if leftHand.split(separator: ":").count > 1 {
|
||||
// lastDefinition.isPlurals = true
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep only valid definition
|
||||
var invalidDefinitionNames = [String]()
|
||||
sections.forEach { section in
|
||||
section.definitions = section.definitions
|
||||
.filter {
|
||||
if $0.isValid == false {
|
||||
invalidDefinitionNames.append($0.name)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if invalidDefinitionNames.count > 0 {
|
||||
print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
|
||||
// Keep only valid definition
|
||||
var invalidDefinitionNames = [String]()
|
||||
sections.forEach { section in
|
||||
section.definitions = section.definitions
|
||||
.filter {
|
||||
if $0.isValid == false {
|
||||
invalidDefinitionNames.append($0.name)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if invalidDefinitionNames.count > 0 {
|
||||
print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
}
|
||||
|
@ -9,103 +9,100 @@ import Foundation
|
||||
import CLIToolCore
|
||||
import ArgumentParser
|
||||
|
||||
extension Strings {
|
||||
struct Stringium: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
|
||||
|
||||
struct Stringium: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
|
||||
|
||||
static let toolName = "Stringium"
|
||||
static let defaultExtensionName = "String"
|
||||
static let noTranslationTag: String = "notranslation"
|
||||
|
||||
var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
|
||||
var langs: [String] {
|
||||
options.langsRaw
|
||||
.split(separator: " ")
|
||||
.map { String($0) }
|
||||
static let toolName = "Stringium"
|
||||
static let defaultExtensionName = "String"
|
||||
static let noTranslationTag: String = "notranslation"
|
||||
|
||||
var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).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())
|
||||
}
|
||||
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
|
||||
return outputPath
|
||||
}
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: StringiumOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting strings generation")
|
||||
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
print("[\(Self.toolName)] Will generate strings")
|
||||
|
||||
// Parse input file
|
||||
let sections = TwineFileParser.parse(options.inputFile)
|
||||
|
||||
// Generate strings files
|
||||
StringsFileGenerator.writeStringsFiles(sections: sections,
|
||||
langs: langs,
|
||||
defaultLang: options.defaultLang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
outputPath: stringsFileOutputPath,
|
||||
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
||||
|
||||
// Generate extension
|
||||
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
||||
defaultLang: options.defaultLang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||
inputFilename: inputFilenameWithoutExt,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: extensionFilePath)
|
||||
|
||||
print("[\(Self.toolName)] Strings generated")
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = StringiumError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: StringiumOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting strings generation")
|
||||
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
print("[\(Self.toolName)] Will generate strings")
|
||||
|
||||
// Parse input file
|
||||
let sections = TwineFileParser.parse(options.inputFile)
|
||||
|
||||
// Generate strings files
|
||||
StringsFileGenerator.writeStringsFiles(sections: sections,
|
||||
langs: langs,
|
||||
defaultLang: options.defaultLang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
outputPath: stringsFileOutputPath,
|
||||
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
||||
|
||||
// Generate extension
|
||||
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
||||
defaultLang: options.defaultLang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||
inputFilename: inputFilenameWithoutExt,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: extensionFilePath)
|
||||
|
||||
print("[\(Self.toolName)] Strings generated")
|
||||
// Langs
|
||||
guard langs.isEmpty == false else {
|
||||
let error = StringiumError.langsListEmpty
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = StringiumError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// Langs
|
||||
guard langs.isEmpty == false else {
|
||||
let error = StringiumError.langsListEmpty
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
guard langs.contains(options.defaultLang) else {
|
||||
let error = StringiumError.defaultLangsNotInLangs
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
guard langs.contains(options.defaultLang) else {
|
||||
let error = StringiumError.defaultLangsNotInLangs
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -7,34 +7,32 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strings {
|
||||
enum StringiumError: Error {
|
||||
case fileNotExists(String)
|
||||
case langsListEmpty
|
||||
case defaultLangsNotInLangs
|
||||
case writeFile(String, String)
|
||||
case langNotDefined(String, String, Bool)
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .fileNotExists(let filename):
|
||||
return " error:[\(Stringium.toolName)] File \(filename) does not exists "
|
||||
|
||||
case .langsListEmpty:
|
||||
return " error:[\(Stringium.toolName)] Langs list is empty"
|
||||
|
||||
case .defaultLangsNotInLangs:
|
||||
return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
|
||||
|
||||
case .writeFile(let subErrorDescription, let filename):
|
||||
return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||
|
||||
case .langNotDefined(let lang, let definitionName, let isReference):
|
||||
if isReference {
|
||||
return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
|
||||
}
|
||||
return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
|
||||
enum StringiumError: Error {
|
||||
case fileNotExists(String)
|
||||
case langsListEmpty
|
||||
case defaultLangsNotInLangs
|
||||
case writeFile(String, String)
|
||||
case langNotDefined(String, String, Bool)
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .fileNotExists(let filename):
|
||||
return " error:[\(Stringium.toolName)] File \(filename) does not exists "
|
||||
|
||||
case .langsListEmpty:
|
||||
return " error:[\(Stringium.toolName)] Langs list is empty"
|
||||
|
||||
case .defaultLangsNotInLangs:
|
||||
return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
|
||||
|
||||
case .writeFile(let subErrorDescription, let filename):
|
||||
return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||
|
||||
case .langNotDefined(let lang, let definitionName, let isReference):
|
||||
if isReference {
|
||||
return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
|
||||
}
|
||||
return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,30 +8,28 @@
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
extension Strings {
|
||||
struct StringiumOptions: ParsableArguments {
|
||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var outputPathRaw: String
|
||||
|
||||
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
||||
var langsRaw: String
|
||||
|
||||
@Option(help: "Default langs.")
|
||||
var defaultLang: String
|
||||
|
||||
@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")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
struct StringiumOptions: ParsableArguments {
|
||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var outputPathRaw: String
|
||||
|
||||
@Option(name: .customLong("langs"), help: "Langs to generate.")
|
||||
var langsRaw: String
|
||||
|
||||
@Option(help: "Default langs.")
|
||||
var defaultLang: String
|
||||
|
||||
@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")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
|
@ -9,63 +9,60 @@ import Foundation
|
||||
import CLIToolCore
|
||||
import ArgumentParser
|
||||
|
||||
extension Strings {
|
||||
struct Tags: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
|
||||
|
||||
struct Tags: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
|
||||
static let toolName = "Tags"
|
||||
static let defaultExtensionName = "Tags"
|
||||
static let noTranslationTag: String = "notranslation"
|
||||
|
||||
var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: TagsOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting tagss generation")
|
||||
|
||||
static let toolName = "Tags"
|
||||
static let defaultExtensionName = "Tags"
|
||||
static let noTranslationTag: String = "notranslation"
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
||||
print("[\(Self.toolName)] Will generate tags")
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: TagsOptions
|
||||
// Parse input file
|
||||
let sections = TwineFileParser.parse(options.inputFile)
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting tagss generation")
|
||||
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
print("[\(Self.toolName)] Will generate tags")
|
||||
|
||||
// Parse input file
|
||||
let sections = TwineFileParser.parse(options.inputFile)
|
||||
|
||||
// Generate extension
|
||||
TagsGenerator.writeExtensionFiles(sections: sections,
|
||||
lang: options.lang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: extensionFilePath)
|
||||
|
||||
print("[\(Self.toolName)] Tags generated")
|
||||
// Generate extension
|
||||
TagsGenerator.writeExtensionFiles(sections: sections,
|
||||
lang: options.lang,
|
||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: extensionFilePath)
|
||||
|
||||
print("[\(Self.toolName)] Tags generated")
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = StringiumError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = StringiumError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Stringium.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[\(Self.toolName)] Tags are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[\(Self.toolName)] Tags are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -8,24 +8,22 @@
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
extension Strings {
|
||||
struct TagsOptions: ParsableArguments {
|
||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(help: "Lang to generate. (\"ium\" by default)")
|
||||
var lang: String = "ium"
|
||||
|
||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var extensionOutputPath: String
|
||||
|
||||
@Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Tags.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
struct TagsOptions: ParsableArguments {
|
||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var inputFile: String
|
||||
|
||||
@Option(help: "Lang to generate. (\"ium\" by default)")
|
||||
var lang: String = "ium"
|
||||
|
||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
var extensionOutputPath: String
|
||||
|
||||
@Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Tags.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
|
@ -9,89 +9,86 @@ import Foundation
|
||||
import CLIToolCore
|
||||
import ArgumentParser
|
||||
|
||||
extension Strings {
|
||||
struct Twine: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
|
||||
|
||||
struct Twine: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
|
||||
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) } }
|
||||
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
||||
.deletingPathExtension()
|
||||
.lastPathComponent
|
||||
}
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: TwineOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting strings generation")
|
||||
|
||||
static let toolName = "Twine"
|
||||
static let defaultExtensionName = "String"
|
||||
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } }
|
||||
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
||||
.deletingPathExtension()
|
||||
.lastPathComponent
|
||||
}
|
||||
print("[\(Self.toolName)] Will generate strings")
|
||||
|
||||
// The `@OptionGroup` attribute includes the flags, options, and
|
||||
// arguments defined by another `ParsableArguments` type.
|
||||
@OptionGroup var options: TwineOptions
|
||||
|
||||
mutating func run() {
|
||||
print("[\(Self.toolName)] Starting strings generation")
|
||||
|
||||
// Check requirements
|
||||
guard checkRequirements() else { return }
|
||||
|
||||
print("[\(Self.toolName)] Will generate strings")
|
||||
|
||||
// Generate strings files (lproj files)
|
||||
for lang in langs {
|
||||
Shell.shell(Self.twineExecutable,
|
||||
"generate-localization-file", options.inputFile,
|
||||
"--lang", "\(lang)",
|
||||
"\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
|
||||
"--tags=ios,iosonly,iosOnly")
|
||||
}
|
||||
|
||||
// Generate extension
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
|
||||
// Generate strings files (lproj files)
|
||||
for lang in langs {
|
||||
Shell.shell(Self.twineExecutable,
|
||||
"generate-localization-file", options.inputFile,
|
||||
"--format", "apple-swift",
|
||||
"--lang", "\(options.defaultLang)",
|
||||
extensionFilePath,
|
||||
"--lang", "\(lang)",
|
||||
"\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
|
||||
"--tags=ios,iosonly,iosOnly")
|
||||
|
||||
print("[\(Self.toolName)] Strings generated")
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
// Generate extension
|
||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
|
||||
Shell.shell(Self.twineExecutable,
|
||||
"generate-localization-file", options.inputFile,
|
||||
"--format", "apple-swift",
|
||||
"--lang", "\(options.defaultLang)",
|
||||
extensionFilePath,
|
||||
"--tags=ios,iosonly,iosOnly")
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = TwineError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
// Langs
|
||||
guard langs.isEmpty == false else {
|
||||
let error = TwineError.langsListEmpty
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
guard langs.contains(options.defaultLang) else {
|
||||
let error = TwineError.defaultLangsNotInLangs
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
// "R2String+" is hardcoded in Twine formatter
|
||||
let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
|
||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
print("[\(Self.toolName)] Strings generated")
|
||||
}
|
||||
|
||||
// MARK: - Requirements
|
||||
|
||||
private func checkRequirements() -> Bool {
|
||||
let fileManager = FileManager()
|
||||
|
||||
// Input file
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = TwineError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
// Langs
|
||||
guard langs.isEmpty == false else {
|
||||
let error = TwineError.langsListEmpty
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
guard langs.contains(options.defaultLang) else {
|
||||
let error = TwineError.defaultLangsNotInLangs
|
||||
print(error.localizedDescription)
|
||||
Twine.exit(withError: error)
|
||||
}
|
||||
|
||||
// "R2String+" is hardcoded in Twine formatter
|
||||
let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
|
||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -7,25 +7,21 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Strings {
|
||||
enum TwineError: Error {
|
||||
case fileNotExists(String)
|
||||
case langsListEmpty
|
||||
case defaultLangsNotInLangs
|
||||
|
||||
enum TwineError: Error {
|
||||
case fileNotExists(String)
|
||||
case langsListEmpty
|
||||
case defaultLangsNotInLangs
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .fileNotExists(let filename):
|
||||
return " error:[\(Twine.toolName)] File \(filename) does not exists "
|
||||
|
||||
case .langsListEmpty:
|
||||
return " error:[\(Twine.toolName)] Langs list is empty"
|
||||
|
||||
case .defaultLangsNotInLangs:
|
||||
return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
|
||||
}
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .fileNotExists(let filename):
|
||||
return " error:[\(Twine.toolName)] File \(filename) does not exists "
|
||||
|
||||
case .langsListEmpty:
|
||||
return " error:[\(Twine.toolName)] Langs list is empty"
|
||||
|
||||
case .defaultLangsNotInLangs:
|
||||
return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
struct TwineOptions: ParsableArguments {
|
||||
@Flag(name: .customShort("f"), help: "Should force generation")
|
||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||
var forceGeneration = false
|
||||
|
||||
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||
|
@ -14,7 +14,7 @@ struct Strings: ParsableCommand {
|
||||
abstract: "A utility for generate strings.",
|
||||
version: "0.1.0",
|
||||
|
||||
// Pass an array to `subcommands` to set up a nested tree of subcommands.
|
||||
// Pass an array to `subcommands` to set up a nested tree of subcommands.
|
||||
// With language support for type-level introspection, this could be
|
||||
// provided by automatically finding nested `ParsableCommand` types.
|
||||
subcommands: [Twine.self, Stringium.self, Tags.self]
|
||||
@ -26,3 +26,4 @@ struct Strings: ParsableCommand {
|
||||
}
|
||||
|
||||
Strings.main()
|
||||
|
||||
|
Reference in New Issue
Block a user