feat(RES-34): Fix plist font filename (#14)
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good

Reviewed-on: #14
This commit is contained in:
2025-05-05 09:53:05 +02:00
parent 8442c89944
commit 756de4f1de
96 changed files with 3028 additions and 2852 deletions

View File

@ -1,124 +1,134 @@
//
// main.swift
//
//
//
// Created by Thibaut Schmitt on 20/12/2021.
//
import ToolCore
@preconcurrency import ArgumentParser
import Foundation
import ArgumentParser
import ToolCore
struct Colors: ParsableCommand {
// MARK: - CommandConfiguration
static var configuration = CommandConfiguration(
static let configuration = CommandConfiguration(
abstract: "A utility for generate colors assets and their getters.",
version: ResgenSwiftVersion
)
// MARK: - Static
static let toolName = "Color"
static let defaultExtensionName = "Color"
static let defaultExtensionNameUIKit = "UIColor"
static let assetsColorsFolderName = "Colors"
// MARK: - Command options
@OptionGroup var options: ColorsToolOptions
// MARK: - Run
public func run() throws {
func run() throws {
print("[\(Self.toolName)] Starting colors generation")
// Check requirements
guard checkRequirements() else { return }
print("[\(Self.toolName)] Will generate colors")
// Delete current colors
deleteCurrentColors()
// Get colors to generate
let parsedColors = ColorFileParser.parse(options.inputFile,
colorStyle: options.style)
let parsedColors = ColorFileParser.parse(
options.inputFile,
colorStyle: options.style
)
// -> Time: 0.0020350217819213867 seconds
// Generate all colors in xcassets
ColorXcassetHelper.generateXcassetColors(colors: parsedColors,
to: options.xcassetsPath)
ColorXcassetHelper.generateXcassetColors(
colors: parsedColors,
to: options.xcassetsPath
)
// -> Time: 3.4505380392074585 seconds
// Generate extension
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true)
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true
)
// Generate extension
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false)
ColorExtensionGenerator.writeExtensionFile(
colors: parsedColors,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false
)
print("[\(Self.toolName)] Colors generated")
}
// MARK: - Requirements
private func checkRequirements() -> Bool {
let fileManager = FileManager()
// Check if input file exists
guard fileManager.fileExists(atPath: options.inputFile) else {
let error = ColorsToolError.fileNotExists(options.inputFile)
print(error.description)
Colors.exit(withError: error)
Self.exit(withError: error)
}
// Check if xcassets file exists
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
let error = ColorsToolError.fileNotExists(options.xcassetsPath)
print(error.description)
Colors.exit(withError: error)
Self.exit(withError: error)
}
// Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameUIKit else {
let error = ColorsToolError.extensionNamesCollision(options.extensionName)
print(error.description)
Colors.exit(withError: error)
Self.exit(withError: error)
}
// Check if needed to regenerate
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath) else {
guard GeneratorChecker.shouldGenerate(
force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
) else {
print("[\(Self.toolName)] Colors are already up to date :) ")
return false
}
return true
}
// MARK: - Helpers
private func deleteCurrentColors() {
let fileManager = FileManager()
let assetsColorPath = "\(options.xcassetsPath)/Colors"
if fileManager.fileExists(atPath: assetsColorPath) {
do {
try fileManager.removeItem(atPath: assetsColorPath)
} catch {
let error = ColorsToolError.deleteExistingColors("\(options.xcassetsPath)/Colors")
print(error.description)
Colors.exit(withError: error)
Self.exit(withError: error)
}
}
}

View File

@ -8,6 +8,7 @@
import Foundation
enum ColorsToolError: Error {
case extensionNamesCollision(String)
case badFormat(String)
case writeAsset(String)
@ -16,30 +17,30 @@ enum ColorsToolError: Error {
case fileNotExists(String)
case badColorDefinition(String, String)
case deleteExistingColors(String)
var description: String {
switch self {
case .extensionNamesCollision(let extensionName):
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
case .badFormat(let info):
return "error: [\(Colors.toolName)] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\""
case .writeAsset(let info):
return "error: [\(Colors.toolName)] An error occured while writing color in Xcasset: \(info)"
case .createAssetFolder(let assetsFolder):
return "error: [\(Colors.toolName)] An error occured while creating colors folder `\(assetsFolder)`"
case .writeExtension(let filename, let info):
case let .writeExtension(filename, info):
return "error: [\(Colors.toolName)] An error occured while writing extension in \(filename): \(info)"
case .fileNotExists(let filename):
return "error: [\(Colors.toolName)] File \(filename) does not exists"
case .badColorDefinition(let lightColor, let darkColor):
case let .badColorDefinition(lightColor, darkColor):
return "error: [\(Colors.toolName)] One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-"
case .deleteExistingColors(let assetsFolder):
return "error: [\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`"
}

View File

@ -1,38 +1,41 @@
//
// ColorsToolOptions.swift
//
//
//
// Created by Thibaut Schmitt on 17/01/2022.
//
import Foundation
import ArgumentParser
import Foundation
// swiftlint:disable no_grouping_extension
struct ColorsToolOptions: 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: ColorStyle
@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: "Tell if it will generate static properties or not")
var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an Color extension.")
var extensionName: String = Colors.defaultExtensionName
@Option(help: "SwiftUI Extension name. If not specified, it will generate an UIColor extension.")
var extensionNameUIKit: String = Colors.defaultExtensionNameUIKit
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
var extensionSuffix: String?
}
@ -40,29 +43,29 @@ struct ColorsToolOptions: ParsableArguments {
// MARK: - Computed var
extension ColorsToolOptions {
// MARK: - SwiftUI
var extensionFileName: String {
if let extensionSuffix = extensionSuffix {
if let extensionSuffix {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
}
// MARK: - UIKit
var extensionFileNameUIKit: String {
if let extensionSuffix = extensionSuffix {
if let extensionSuffix {
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
}
return "\(extensionNameUIKit).swift"
}
var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
}

View File

@ -1,6 +1,6 @@
//
// ColorExtensionGenerator.swift
//
//
//
// Created by Thibaut Schmitt on 20/12/2021.
//
@ -9,38 +9,44 @@ import Foundation
import ToolCore
struct ColorExtensionGenerator {
let colors: [ParsedColor]
let extensionClassname: String
// MARK: - UIKit
static func writeExtensionFile(colors: [ParsedColor],
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool) {
static func writeExtensionFile(
colors: [ParsedColor],
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool
) {
// Create extension content
let extensionContent = Self.getExtensionContent(colors: colors,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI)
let extensionContent = Self.getExtensionContent(
colors: colors,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI
)
// Write content
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do {
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
} catch let error {
} catch {
let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
print(error.description)
Colors.exit(withError: error)
}
}
static func getExtensionContent(colors: [ParsedColor],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool) -> String {
static func getExtensionContent(
colors: [ParsedColor],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool
) -> String {
[
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
Self.getProperties(for: colors, withStaticVar: staticVar, isSwiftUI: isSwiftUI),
@ -48,7 +54,7 @@ struct ColorExtensionGenerator {
]
.joined(separator: "\n")
}
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
"""
// Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion)
@ -58,17 +64,19 @@ struct ColorExtensionGenerator {
extension \(extensionClassname) {\n
"""
}
private static func getFooter() -> String {
"""
}
"""
}
private static func getProperties(for colors: [ParsedColor],
withStaticVar staticVar: Bool,
isSwiftUI: Bool) -> String {
private static func getProperties(
for colors: [ParsedColor],
withStaticVar staticVar: Bool,
isSwiftUI: Bool
) -> String {
colors.map {
$0.getColorProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
}

View File

@ -1,6 +1,6 @@
//
// ColorXcassetHelper.swift
//
//
//
// Created by Thibaut Schmitt on 20/12/2021.
//
@ -8,37 +8,39 @@
import Foundation
import ToolCore
struct ColorXcassetHelper {
enum ColorXcassetHelper {
static func generateXcassetColors(colors: [ParsedColor], to xcassetsPath: String) {
colors.forEach {
Self.generateColorSetAssets(from: $0, to: xcassetsPath)
}
}
// Generate ColorSet in XCAssets file
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
// Create ColorSet
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
let contentsJsonPath = "\(colorSetPath)/Contents.json"
let fileManager = FileManager()
if fileManager.fileExists(atPath: colorSetPath) == false {
do {
try fileManager.createDirectory(atPath: colorSetPath,
withIntermediateDirectories: true)
try fileManager.createDirectory(
atPath: colorSetPath,
withIntermediateDirectories: true
)
} catch {
let error = ColorsToolError.createAssetFolder(colorSetPath)
print(error.description)
Colors.exit(withError: error)
}
}
// Write content in Contents.json
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
do {
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8)
} catch let error {
} catch {
let error = ColorsToolError.writeAsset(error.localizedDescription)
print(error.description)
Colors.exit(withError: error)

View File

@ -5,13 +5,14 @@
// Created by Thibaut Schmitt on 29/08/2022.
//
import Foundation
import ArgumentParser
import Foundation
enum ColorStyle: String, Decodable, ExpressibleByArgument {
case light
case all
static var allValueStrings: [String] {
[
Self.light.rawValue,

View File

@ -8,28 +8,29 @@
import Foundation
struct ParsedColor {
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 = ColorsToolError.badColorDefinition(light, dark)
print(error.description)
Colors.exit(withError: error)
}
return """
{
"colors": [
@ -71,9 +72,9 @@ struct ParsedColor {
}
"""
}
// MARK: - UIKit
func getColorProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
if isSwiftUI {
return """

View File

@ -7,44 +7,45 @@
import Foundation
class ColorFileParser {
enum ColorFileParser {
static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] {
// Get content of input file
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) // swiftlint:disable:this force_try
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
// Iterate on each line of input file
return parseLines(lines: colorsByLines, colorStyle: colorStyle)
}
static func parseLines(lines: [String], colorStyle: ColorStyle) -> [ParsedColor] {
lines
.enumerated()
.compactMap { lineNumber, colorLine in
// Required format:
// colorName = "#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB"
.compactMap { _, colorLine in // swiftlint:disable:this unused_enumerated
// Required format:
// colorName = "#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB"
let colorLineCleanedUp = colorLine
.removeLeadingWhitespace()
.removeTrailingWhitespace()
.replacingOccurrences(of: "=", with: "") // Keep compat with current file format
guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else {
// debugPrint("[\(Colors.toolName)] BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line")
return nil
}
let colorContent = colorLineCleanedUp.split(separator: " ")
guard colorContent.count >= 2 else {
let error = ColorsToolError.badFormat(colorLine)
print(error.description)
Colors.exit(withError: error)
}
switch colorStyle {
case .light:
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
case .all:
if colorContent.count == 3 {
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2]))