Refactor + Bugs fixes on Strings + Gestion de la génération des images
This commit is contained in:
41
Sources/ColorTool/ColorExtensionGenerator.swift
Normal file
41
Sources/ColorTool/ColorExtensionGenerator.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// ColorExtensionGenerator.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 20/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ColorExtensionGenerator {
|
||||
|
||||
let colors: [GenColor]
|
||||
let extensionClassname: String
|
||||
let isUIColorExtension: Bool
|
||||
|
||||
func getHeader() -> String {
|
||||
"""
|
||||
// Generated from ColorToolCore at \(Date())
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
35
Sources/ColorTool/ColorToolError.swift
Normal file
35
Sources/ColorTool/ColorToolError.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// ColorToolError.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 20/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ColorToolError: Error {
|
||||
case badFormat(String)
|
||||
case writeAsset(String)
|
||||
case writeExtension(String, String)
|
||||
case fileNotExists(String)
|
||||
case badColorDefinition(String, String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .badFormat(let info):
|
||||
return "error:[ColorTool] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\""
|
||||
|
||||
case .writeAsset(let info):
|
||||
return "error:[ColorTool] An error occured while writing color in Xcasset: \(info)"
|
||||
|
||||
case .writeExtension(let filename, let info):
|
||||
return "error:[ColorTool] An error occured while writing extension in \(filename): \(info)"
|
||||
|
||||
case .fileNotExists(let filename):
|
||||
return "error:[ColorTool] File \(filename) does not exists"
|
||||
|
||||
case .badColorDefinition(let lightColor, let darkColor):
|
||||
return "error:[ColorTool]One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-"
|
||||
}
|
||||
}
|
||||
}
|
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 = ""
|
||||
}
|
42
Sources/ColorTool/ColorXcassetHelper.swift
Normal file
42
Sources/ColorTool/ColorXcassetHelper.swift
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// ColorXcassetHelper.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 20/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CLIToolCore
|
||||
|
||||
struct ColorXcassetHelper {
|
||||
|
||||
let xcassetsPath: String
|
||||
let colors: [GenColor]
|
||||
|
||||
func generateXcassetColors() {
|
||||
colors.forEach {
|
||||
generateColorSetAssets(from: $0)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate ColorSet in XCAssets file
|
||||
private func generateColorSetAssets(from color: GenColor) {
|
||||
// Create ColorSet
|
||||
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
||||
Shell.shell("mkdir", "-p", "\(colorSetPath)")
|
||||
|
||||
// Create Contents.json in ColorSet
|
||||
let contentsJsonPath = "\(colorSetPath)/Contents.json"
|
||||
Shell.shell("touch", "\(contentsJsonPath)")
|
||||
|
||||
// Write content in Contents.json
|
||||
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
|
||||
do {
|
||||
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = ColorToolError.writeAsset(error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
ColorTool.exit(withError: error)
|
||||
}
|
||||
}
|
||||
}
|
92
Sources/ColorTool/GenColor.swift
Normal file
92
Sources/ColorTool/GenColor.swift
Normal file
@ -0,0 +1,92 @@
|
||||
//
|
||||
// GenColor.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 20/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct GenColor {
|
||||
let name: String
|
||||
let light: String
|
||||
let dark: String
|
||||
|
||||
// Generate Contents.json content
|
||||
func contentsJSON() -> String {
|
||||
let lightARGB = light.colorComponent()
|
||||
let darkARGB = dark.colorComponent()
|
||||
|
||||
let allComponents = [
|
||||
lightARGB.alpha, lightARGB.red, lightARGB.green, lightARGB.blue,
|
||||
darkARGB.alpha, darkARGB.red, darkARGB.green, darkARGB.blue
|
||||
].map {
|
||||
$0.isEmpty
|
||||
}
|
||||
|
||||
guard allComponents.contains(true) == false else {
|
||||
let error = ColorToolError.badColorDefinition(light, dark)
|
||||
print(error.localizedDescription)
|
||||
ColorTool.exit(withError: error)
|
||||
}
|
||||
|
||||
return """
|
||||
{
|
||||
"colors": [
|
||||
{
|
||||
"color": {
|
||||
"color-space": "srgb",
|
||||
"components": {
|
||||
"alpha": "0x\(lightARGB.alpha)",
|
||||
"blue": "0x\(lightARGB.blue)",
|
||||
"green": "0x\(lightARGB.green)",
|
||||
"red": "0x\(lightARGB.red)",
|
||||
}
|
||||
},
|
||||
"idiom": "universal"
|
||||
},
|
||||
{
|
||||
"appearances": [
|
||||
{
|
||||
"appearance": "luminosity",
|
||||
"value": "dark"
|
||||
}
|
||||
],
|
||||
"color": {
|
||||
"color-space": "srgb",
|
||||
"components": {
|
||||
"alpha": "0x\(darkARGB.alpha)",
|
||||
"blue": "0x\(darkARGB.blue)",
|
||||
"green": "0x\(darkARGB.green)",
|
||||
"red": "0x\(darkARGB.red)",
|
||||
}
|
||||
},
|
||||
"idiom": "universal"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
func getColorProperty() -> String {
|
||||
"""
|
||||
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
||||
@objc var \(name): UIColor {
|
||||
UIColor(named: "\(name)")!
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
func getColorStaticProperty() -> String {
|
||||
"""
|
||||
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
||||
static var \(name): UIColor {
|
||||
UIColor(named: "\(name)")!
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
156
Sources/ColorTool/main.swift
Normal file
156
Sources/ColorTool/main.swift
Normal file
@ -0,0 +1,156 @@
|
||||
//
|
||||
// main.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 20/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CLIToolCore
|
||||
import ArgumentParser
|
||||
|
||||
enum ColorStyle: String, Decodable {
|
||||
case light
|
||||
case all
|
||||
}
|
||||
|
||||
struct ColorTool: ParsableCommand {
|
||||
static let defaultExtensionName = "UIColor"
|
||||
static let assetsColorsFolderName = "Colors"
|
||||
|
||||
@OptionGroup var options: ColorToolOptions
|
||||
|
||||
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 \(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: options.xcassetsPath) else {
|
||||
let error = ColorToolError.fileNotExists(options.xcassetsPath)
|
||||
print(error.localizedDescription)
|
||||
ColorTool.exit(withError: error)
|
||||
}
|
||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||
let error = ColorToolError.fileNotExists(options.inputFile)
|
||||
print(error.localizedDescription)
|
||||
ColorTool.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
||||
print("[ColorTool] Colors are already up to date :) ")
|
||||
return
|
||||
}
|
||||
print("[ColorTool] Will generate colors")
|
||||
|
||||
// Delete current colors
|
||||
Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
|
||||
|
||||
// Get colors
|
||||
let colorsToGen = getColorsGen()
|
||||
|
||||
// Generate all colors in xcassets
|
||||
let colorAssetHelper = ColorXcassetHelper(xcassetsPath: options.xcassetsPath, colors: colorsToGen)
|
||||
colorAssetHelper.generateXcassetColors()
|
||||
|
||||
// Generate extension
|
||||
let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen,
|
||||
extensionClassname: options.extensionName,
|
||||
isUIColorExtension: isUIColorExtension())
|
||||
let extensionHeader = extensionGenerator.getHeader()
|
||||
let extensionProperties = extensionGenerator.getProperties()
|
||||
let extensionFooter = extensionGenerator.getFooter()
|
||||
|
||||
generateExtensionFile(extensionHeader, extensionProperties, extensionFooter)
|
||||
|
||||
print("[ColorTool] Colors generated")
|
||||
}
|
||||
|
||||
private func generateExtensionFile(_ args: String...) {
|
||||
// Create file if not exists
|
||||
let fileManager = FileManager()
|
||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
||||
Shell.shell("touch", "\(extensionFilePath)")
|
||||
}
|
||||
|
||||
// Create extension content
|
||||
let extensionContent = args.joined(separator: "\n")
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = ColorToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||
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
|
||||
|
||||
private func isUIColorExtension() -> Bool {
|
||||
options.extensionName == Self.defaultExtensionName
|
||||
}
|
||||
}
|
||||
|
||||
ColorTool.main()
|
||||
|
||||
/*
|
||||
Command samples:
|
||||
|
||||
1. UIColor extension without suffix
|
||||
swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style all --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "UIColor"
|
||||
|
||||
2. UIColor extension with custom suffix
|
||||
swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style all --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "UIColor" --extension-suffix "SampleApp"
|
||||
|
||||
3. Custom extension with only light theme colors (R2Color)
|
||||
swift run -c release ColorToolCore -f ./SampleFiles/Colors/sampleColors1.txt --style light --xcassets-path "./SampleFiles/Colors/colors.xcassets" --extension-output-path "./SampleFiles/Colors/Generated/" --extension-name "R2Color"
|
||||
*/
|
Reference in New Issue
Block a user