Publish v1.0
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Reviewed-on: #1
This commit is contained in:
parent
a99466f258
commit
6203700b0c
@ -1,78 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1310"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ColorToolCore"
|
|
||||||
BuildableName = "ColorToolCore"
|
|
||||||
BlueprintName = "ColorToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ColorToolCore"
|
|
||||||
BuildableName = "ColorToolCore"
|
|
||||||
BlueprintName = "ColorToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ColorToolCore"
|
|
||||||
BuildableName = "ColorToolCore"
|
|
||||||
BlueprintName = "ColorToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
@ -1,78 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1310"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "FontToolCore"
|
|
||||||
BuildableName = "FontToolCore"
|
|
||||||
BlueprintName = "FontToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "FontToolCore"
|
|
||||||
BuildableName = "FontToolCore"
|
|
||||||
BlueprintName = "FontToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "FontToolCore"
|
|
||||||
BuildableName = "FontToolCore"
|
|
||||||
BlueprintName = "FontToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
@ -1,78 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1310"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
@ -1,237 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1310"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ColorToolCore"
|
|
||||||
BuildableName = "ColorToolCore"
|
|
||||||
BlueprintName = "ColorToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "FontToolCore"
|
|
||||||
BuildableName = "FontToolCore"
|
|
||||||
BlueprintName = "FontToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ResgenSwift"
|
|
||||||
BuildableName = "ResgenSwift"
|
|
||||||
BlueprintName = "ResgenSwift"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "NO"
|
|
||||||
buildForArchiving = "NO"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ResgenSwiftTests"
|
|
||||||
BuildableName = "ResgenSwiftTests"
|
|
||||||
BlueprintName = "ResgenSwiftTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "CLIToolCore"
|
|
||||||
BuildableName = "CLIToolCore"
|
|
||||||
BlueprintName = "CLIToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "StringToolCore"
|
|
||||||
BuildableName = "StringToolCore"
|
|
||||||
BlueprintName = "StringToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "TwineToolCore"
|
|
||||||
BuildableName = "TwineToolCore"
|
|
||||||
BlueprintName = "TwineToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Strings"
|
|
||||||
BuildableName = "Strings"
|
|
||||||
BlueprintName = "Strings"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "FontTool"
|
|
||||||
BuildableName = "FontTool"
|
|
||||||
BlueprintName = "FontTool"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ToolCore"
|
|
||||||
BuildableName = "ToolCore"
|
|
||||||
BlueprintName = "ToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ResgenSwiftTests"
|
|
||||||
BuildableName = "ResgenSwiftTests"
|
|
||||||
BlueprintName = "ResgenSwiftTests"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "ColorToolCore"
|
|
||||||
BuildableName = "ColorToolCore"
|
|
||||||
BlueprintName = "ColorToolCore"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "Imagium"
|
|
||||||
BuildableName = "Imagium"
|
|
||||||
BlueprintName = "Imagium"
|
|
||||||
ReferencedContainer = "container:">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
@ -34,6 +34,34 @@
|
|||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "ToolCore"
|
||||||
|
BuildableName = "ToolCore"
|
||||||
|
BlueprintName = "ToolCore"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "ResgenSwift_ResgenSwift"
|
||||||
|
BuildableName = "ResgenSwift_ResgenSwift"
|
||||||
|
BlueprintName = "ResgenSwift_ResgenSwift"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
</BuildAction>
|
</BuildAction>
|
||||||
<TestAction
|
<TestAction
|
||||||
|
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@ -1,6 +1,8 @@
|
|||||||
library "openiumpipeline"
|
library "openiumpipeline"
|
||||||
|
|
||||||
//env.DEVELOPER_DIR="/Applications/Xcode_12.4.app/Contents/Developer"
|
env.DEVELOPER_DIR="/Applications/Xcode_13.3.0.app/Contents/Developer"
|
||||||
//env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-"
|
//env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-"
|
||||||
|
env.IS_PACKAGE_SWIFT=1
|
||||||
|
env.TARGETS_MACOS=1
|
||||||
|
|
||||||
iOSpipeline()
|
iOSpipeline()
|
||||||
|
@ -9,6 +9,15 @@
|
|||||||
"revision": "e1465042f195f374b94f915ba8ca49de24300a0d",
|
"revision": "e1465042f195f374b94f915ba8ca49de24300a0d",
|
||||||
"version": "1.0.2"
|
"version": "1.0.2"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "Yams",
|
||||||
|
"repositoryURL": "https://github.com/jpsim/Yams.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "01835dc202670b5bb90d07f3eae41867e9ed29f6",
|
||||||
|
"version": "5.0.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,53 +1,31 @@
|
|||||||
// swift-tools-version:5.3
|
// swift-tools-version:5.6
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "ResgenSwift",
|
name: "ResgenSwift",
|
||||||
platforms: [.macOS(.v10_12)],
|
platforms: [.macOS(.v12)],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// Dependencies declare other packages that this package depends on.
|
// Dependencies declare other packages that this package depends on.
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0")
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
||||||
|
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
.target(
|
.executableTarget(
|
||||||
name: "ResgenSwift",
|
name: "ResgenSwift",
|
||||||
dependencies: ["FontTool", "ColorTool", "Strings", "Imagium"]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "FontTool",
|
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"ToolCore",
|
"ToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
]
|
"Yams"
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "ColorTool",
|
|
||||||
dependencies: [
|
|
||||||
"ToolCore",
|
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
|
||||||
]
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Strings",
|
|
||||||
dependencies: [
|
|
||||||
"ToolCore",
|
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
|
||||||
],
|
|
||||||
sources: ["."] // Force include all subdirectories
|
|
||||||
),
|
|
||||||
.target(
|
|
||||||
name: "Imagium",
|
|
||||||
dependencies: [
|
|
||||||
"ToolCore",
|
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
// Helper targets
|
// Helper targets
|
||||||
.target(name: "ToolCore"),
|
.target(name: "ToolCore"),
|
||||||
|
|
||||||
// Test targets
|
// Test targets
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "ResgenSwiftTests",
|
name: "ResgenSwiftTests",
|
||||||
|
183
README.md
183
README.md
@ -4,18 +4,19 @@ ResgenSwift is a package, fully written in Swift, to help you automatize ressour
|
|||||||
|
|
||||||
> 🧐 For all commands, see samples files in `SampleFiles`
|
> 🧐 For all commands, see samples files in `SampleFiles`
|
||||||
|
|
||||||
# Fonts
|
## Fonts
|
||||||
|
|
||||||
Font generator generates an extension of `UIFont` (or a custom class). It also prints `UIAppFonts` to put in your project `.plist`.
|
Font generator generates an extension of `UIFont` (or a custom class). It also prints `UIAppFonts` to put in your project `.plist`.
|
||||||
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`.
|
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`.
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \
|
swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
|
||||||
--extension-output-path "./Fonts/Generated" \
|
--extension-output-path "./Fonts/Generated" \
|
||||||
--extension-name "AppFont" \
|
--extension-name "AppFont" \
|
||||||
--extension-suffix "GreatApp"
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -25,21 +26,21 @@ swift run -c release FontTool $FORCE_FLAG "./Fonts/fonts.txt" \
|
|||||||
3. `--extension-output-path`: path where to generate generated extension
|
3. `--extension-output-path`: path where to generate generated extension
|
||||||
4. `--extension-name` *(optional)* : name of thee class to add the extension
|
4. `--extension-name` *(optional)* : name of thee class to add the extension
|
||||||
5. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`)
|
5. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`)
|
||||||
|
6. `--static-members` *(optional)*: generate static properties or not
|
||||||
> ⚠️ If extension name is not set or is `UIFont`, it will generate static property on `UIFont` instead of method in your custom class.
|
|
||||||
|
|
||||||
|
|
||||||
# Colors
|
## Colors
|
||||||
|
|
||||||
Colors generator generates an extension of `UIColor` (or a custom class) along with colorsets in specified xcassets.
|
Colors generator generates an extension of `UIColor` (or a custom class) along with colorsets in specified xcassets.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release ColorTool $FORCE_FLAG "./Colors/colors.txt" \
|
swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
|
||||||
--style all \
|
--style all \
|
||||||
--xcassets-path "./Colors/colors.xcassets" \
|
--xcassets-path "./Colors/colors.xcassets" \
|
||||||
--extension-output-path "./Colors/Generated/" \
|
--extension-output-path "./Colors/Generated/" \
|
||||||
--extension-name "AppColor" \
|
--extension-name "AppColor" \
|
||||||
--extension-suffix "GreatApp"
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -50,23 +51,22 @@ swift run -c release ColorTool $FORCE_FLAG "./Colors/colors.txt" \
|
|||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
> ⚠️ If extension name is not set or is `UIColor`, it will generate static property on `UIColor`.
|
|
||||||
|
|
||||||
|
|
||||||
# Strings
|
## Strings
|
||||||
|
|
||||||
Strings command allows to generate `strings` files along with extensions to access those strings easily. It can do it 2 ways: Twine and Stringium. It is not recommended to use Twine except on legacy projects or while migrating to ResgenSwift, because it use https://github.com/openium/twine. Using Stringium is recommended because it does not required external dependency and allow more customisation.
|
Strings command allows to generate `strings` files along with extensions to access those strings easily. It can do it 2 ways: Twine and Stringium. It is not recommended to use Twine except on legacy projects or while migrating to ResgenSwift, because it use https://github.com/openium/twine. Using Stringium is recommended because it does not required external dependency and allow more customisation.
|
||||||
|
|
||||||
## Twine (not recommended)
|
### Twine (not recommended)
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \
|
swift run -c release ResgenSwift strings twine $FORCE_FLAG "./Twine/strings.txt" \
|
||||||
--output-path "./Twine/Generated" \
|
--output-path "./Twine/Generated" \
|
||||||
--langs "fr en en-us" \
|
--langs "fr en en-us" \
|
||||||
--default-lang "en" \
|
--default-lang "en" \
|
||||||
--extension-output-path "./Twine/Generated"
|
--extension-output-path "./Twine/Generated"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
|
||||||
@ -76,16 +76,17 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/strings.txt" \
|
|||||||
4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well
|
4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well
|
||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
|
|
||||||
## Stringium (recommended)
|
### Stringium (recommended)
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \
|
swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/strings.txt" \
|
||||||
--output-path "./Strings/Generated" \
|
--output-path "./Strings/Generated" \
|
||||||
--langs "fr en en-us" \
|
--langs "fr en en-us" \
|
||||||
--default-lang "en" \
|
--default-lang "en" \
|
||||||
--extension-output-path "./Strings/Generated" \
|
--extension-output-path "./Strings/Generated" \
|
||||||
--extension-name "AppString" \
|
--extension-name "AppString" \
|
||||||
--extension-suffix "GreatApp"
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -97,19 +98,20 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/strings.txt" \
|
|||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
> ⚠️ If extension name is not set or is `String`, it will generate static property on `String`.
|
|
||||||
|
|
||||||
# Tags
|
## Tags
|
||||||
|
|
||||||
Tags is also a subcommand of `Strings`. Input files are formatted the same way. Tags will generate properties which return exactly what is specified in the input file. It was designed to be used for analytics purpose and to be shared with any other platform to have the same analytics keys.
|
Tags is also a subcommand of `Strings`. Input files are formatted the same way. Tags will generate properties which return exactly what is specified in the input file. It was designed to be used for analytics purpose and to be shared with any other platform to have the same analytics keys.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||||
--lang "ium" \
|
--lang "ium" \
|
||||||
--extension-output-path "./Tags/Generated" \
|
--extension-output-path "./Tags/Generated" \
|
||||||
--extension-name "AppTags" \
|
--extension-name "AppTags" \
|
||||||
--extension-suffix "GreatApp"
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -120,19 +122,21 @@ swift run -c release Strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
|||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppTags+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
> ⚠️ If extension name is not set or is `Tags`, it will generate static property on `Tags`. This class may not exists in your project, just create an empty class named `Tags` is necessary.
|
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
|
||||||
|
|
||||||
# Images
|
## Images
|
||||||
|
|
||||||
Images generator will generate images assets along with extensions to access those images easily.
|
Images generator will generate images assets along with extensions to access those images easily.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \
|
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
|
||||||
--xcassets-path "./Images/app.xcassets" \
|
--xcassets-path "./Images/app.xcassets" \
|
||||||
--extension-output-path "./Images/Generated" \
|
--extension-output-path "./Images/Generated" \
|
||||||
--extension-name "AppImage" \
|
--extension-name "AppImage" \
|
||||||
--extension-suffix "GreatApp"
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -143,9 +147,120 @@ swift run -c release Imagium $FORCE_FLAG "./Images/images.txt" \
|
|||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
> ⚠️ If extension name is not set or is `UIImage`, it will generate static property on `UIImage`.
|
|
||||||
|
|
||||||
# TODO
|
## All at once
|
||||||
|
|
||||||
[ ] Allow static variable generation on custom extension
|
Another command exists to generate all ressources at the same time: `generate`. It use the following commands: `Fonts`, `Colors`, `Strings/Stringium`, `Strings/Tags`, `Images`.
|
||||||
|
|
||||||
|
All parameters can be specified in a configuration file in `Yaml`:
|
||||||
|
|
||||||
|
> Order of configuration types does not matter. Order them to fit your needs.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
colors:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
style: [light/all]
|
||||||
|
xcassetsPath: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
|
||||||
|
fonts:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
|
||||||
|
images:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
xcassetsPath: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
|
||||||
|
strings:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
outputPath: String
|
||||||
|
langs: String
|
||||||
|
defaultLang: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
|
||||||
|
tags:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
lang: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple configurations
|
||||||
|
|
||||||
|
In some case, you may need to have 2 colors files in your projects. You will need 2 colors configurations. Every configuration type is an array and can contains as many configurations as you need.
|
||||||
|
|
||||||
|
Sample for 2 colors configurations:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
...
|
||||||
|
colors:
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
style: [light/all]
|
||||||
|
xcassetsPath: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
-
|
||||||
|
inputFile: String
|
||||||
|
style: [light/all]
|
||||||
|
xcassetsPath: String
|
||||||
|
extensionOutputPath: String
|
||||||
|
extensionName: String?
|
||||||
|
extensionSuffix: String?
|
||||||
|
staticMembers: Bool?
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### No configuration
|
||||||
|
|
||||||
|
In some case, you may not need to generate tags for example. You must specified `tags` as an empty array :
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
...
|
||||||
|
tags: []
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
swift run -c release ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR}
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`)
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Simple run `./install.sh`. Binary will be install in `/usr/local/bin`. Then, use ResgenSwift like any other command:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR}
|
||||||
|
```
|
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)-"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// File.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class ColorFileParser {
|
|
||||||
static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] {
|
|
||||||
// Get content of input file
|
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
|
||||||
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
|
|
||||||
|
|
||||||
// Iterate on each line of input file
|
|
||||||
return colorsByLines.enumerated().compactMap { lineNumber, colorLine in
|
|
||||||
// Required format:
|
|
||||||
// colorName="#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB"
|
|
||||||
let colorLineCleanedUp = colorLine
|
|
||||||
.removeTrailingWhitespace()
|
|
||||||
.replacingOccurrences(of: "=", with: "") // Keep compat with current file format
|
|
||||||
|
|
||||||
guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else {
|
|
||||||
print("[\(ColorTool.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let colorContent = colorLineCleanedUp.split(separator: " ")
|
|
||||||
|
|
||||||
guard colorContent.count >= 2 else {
|
|
||||||
let error = ColorToolError.badFormat(colorLine)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
ColorTool.exit(withError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch colorStyle {
|
|
||||||
case .light:
|
|
||||||
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
|
|
||||||
|
|
||||||
case .all:
|
|
||||||
if colorContent.count == 3 {
|
|
||||||
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2]))
|
|
||||||
}
|
|
||||||
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
//
|
|
||||||
// ImageExtensionGenerator.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Thibaut Schmitt on 14/02/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ToolCore
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class ImageExtensionGenerator {
|
|
||||||
|
|
||||||
// MARK: - Extension files
|
|
||||||
|
|
||||||
static func writeStringsFiles(images: [ParsedImage], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
|
||||||
// Get header/footer
|
|
||||||
let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
|
|
||||||
let extensionFooter = Self.getFooter()
|
|
||||||
|
|
||||||
// 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 by ResgenSwift.Imagium \(ResgenSwiftVersion)
|
|
||||||
// Images from \(inputFilename)
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension \(extensionClassname) {
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//@objc var onboarding_foreground3: UIImage {
|
|
||||||
// return UIImage(named: "onboarding_foreground3")!
|
|
||||||
// }
|
|
@ -1,43 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ import ToolCore
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct ColorTool: ParsableCommand {
|
struct Colors: ParsableCommand {
|
||||||
|
|
||||||
// MARK: - CommandConfiguration
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
@ -20,32 +20,18 @@ struct ColorTool: ParsableCommand {
|
|||||||
|
|
||||||
// MARK: - Static
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "ColorTool"
|
static let toolName = "Color"
|
||||||
static let defaultExtensionName = "UIColor"
|
static let defaultExtensionName = "UIColor"
|
||||||
static let assetsColorsFolderName = "Colors"
|
static let assetsColorsFolderName = "Colors"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var extensionFileName: String {
|
|
||||||
if options.extensionSuffix.isEmpty == false {
|
|
||||||
return "\(options.extensionName)+\(options.extensionSuffix).swift"
|
|
||||||
}
|
|
||||||
return "\(options.extensionName).swift"
|
|
||||||
}
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
var generateStaticVariable: Bool {
|
|
||||||
options.extensionName == Self.defaultExtensionName
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command options
|
// MARK: - Command options
|
||||||
|
|
||||||
@OptionGroup var options: ColorToolOptions
|
@OptionGroup var options: ColorsToolOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[\(Self.toolName)] Starting colors generation")
|
print("[\(Self.toolName)] Starting colors generation")
|
||||||
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate \(options.colorStyle) colors in xcassets \(options.xcassetsPath)")
|
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
@ -58,16 +44,19 @@ struct ColorTool: ParsableCommand {
|
|||||||
// Get colors to generate
|
// Get colors to generate
|
||||||
let parsedColors = ColorFileParser.parse(options.inputFile,
|
let parsedColors = ColorFileParser.parse(options.inputFile,
|
||||||
colorStyle: options.colorStyle)
|
colorStyle: options.colorStyle)
|
||||||
|
// -> Time: 0.0020350217819213867 seconds
|
||||||
|
|
||||||
// Generate all colors in xcassets
|
// Generate all colors in xcassets
|
||||||
ColorXcassetHelper.generateXcassetColors(colors: parsedColors,
|
ColorXcassetHelper.generateXcassetColors(colors: parsedColors,
|
||||||
to: options.xcassetsPath)
|
to: options.xcassetsPath)
|
||||||
|
// -> Time: 3.4505380392074585 seconds
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
|
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
|
||||||
staticVar: generateStaticVariable,
|
staticVar: options.staticMembers,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: options.extensionFilePath)
|
||||||
|
// -> Time: 0.0010340213775634766 seconds
|
||||||
|
|
||||||
print("[\(Self.toolName)] Colors generated")
|
print("[\(Self.toolName)] Colors generated")
|
||||||
}
|
}
|
||||||
@ -79,20 +68,22 @@ struct ColorTool: ParsableCommand {
|
|||||||
|
|
||||||
// Check if input file exists
|
// Check if input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = ColorToolError.fileNotExists(options.inputFile)
|
let error = ColorsToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if xcassets file exists
|
// Check if xcassets file exists
|
||||||
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
||||||
let error = ColorToolError.fileNotExists(options.xcassetsPath)
|
let error = ColorsToolError.fileNotExists(options.xcassetsPath)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
print("[\(Self.toolName)] Colors are already up to date :) ")
|
print("[\(Self.toolName)] Colors are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -103,21 +94,17 @@ struct ColorTool: ParsableCommand {
|
|||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func deleteCurrentColors() {
|
private func deleteCurrentColors() {
|
||||||
Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
|
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.localizedDescription)
|
||||||
|
Colors.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"
|
|
||||||
*/
|
|
43
Sources/ResgenSwift/Colors/ColorsToolError.swift
Normal file
43
Sources/ResgenSwift/Colors/ColorsToolError.swift
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// ColorsToolError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 20/12/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum ColorsToolError: Error {
|
||||||
|
case badFormat(String)
|
||||||
|
case writeAsset(String)
|
||||||
|
case createAssetFolder(String)
|
||||||
|
case writeExtension(String, String)
|
||||||
|
case fileNotExists(String)
|
||||||
|
case badColorDefinition(String, String)
|
||||||
|
case deleteExistingColors(String)
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
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):
|
||||||
|
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):
|
||||||
|
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)`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ColorToolOptions.swift
|
// ColorsToolOptions.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 17/01/2022.
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct ColorToolOptions: ParsableArguments {
|
struct ColorsToolOptions: ParsableArguments {
|
||||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
var forceGeneration = false
|
var forceGeneration = false
|
||||||
|
|
||||||
@ -24,15 +24,31 @@ struct ColorToolOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@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 UIColor extension. Using default extension name will generate static property.")
|
@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
|
var extensionName: String = Colors.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ColorToolOptions {
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension ColorsToolOptions {
|
||||||
var colorStyle: ColorStyle {
|
var colorStyle: ColorStyle {
|
||||||
ColorStyle(rawValue: style) ?? .all
|
ColorStyle(rawValue: style) ?? .all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extensionFileName: String {
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionName).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,34 +14,34 @@ struct ColorExtensionGenerator {
|
|||||||
let extensionClassname: String
|
let extensionClassname: String
|
||||||
|
|
||||||
static func writeExtensionFile(colors: [ParsedColor], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
static func writeExtensionFile(colors: [ParsedColor], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||||
// Create file if not exists
|
|
||||||
let fileManager = FileManager()
|
|
||||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
|
||||||
Shell.shell("touch", "\(extensionFilePath)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create extension content
|
// Create extension content
|
||||||
let extensionContent = [
|
let extensionContent = Self.getExtensionContent(colors: colors,
|
||||||
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName)
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Colors.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getExtensionContent(colors: [ParsedColor], staticVar: Bool, extensionName: String) -> String {
|
||||||
|
[
|
||||||
Self.getHeader(extensionClassname: extensionName),
|
Self.getHeader(extensionClassname: extensionName),
|
||||||
Self.getProperties(for: colors, withStaticVar: staticVar),
|
Self.getProperties(for: colors, withStaticVar: staticVar),
|
||||||
Self.getFooter()
|
Self.getFooter()
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
|
|
||||||
// Write content
|
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
|
||||||
do {
|
|
||||||
try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
|
||||||
} catch (let error) {
|
|
||||||
let error = ColorToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
ColorTool.exit(withError: error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
private static func getHeader(extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.\(ColorTool.toolName) \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
@ -20,20 +20,28 @@ struct ColorXcassetHelper {
|
|||||||
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
|
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
|
||||||
// Create ColorSet
|
// Create ColorSet
|
||||||
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
||||||
Shell.shell("mkdir", "-p", "\(colorSetPath)")
|
|
||||||
|
|
||||||
// Create Contents.json in ColorSet
|
|
||||||
let contentsJsonPath = "\(colorSetPath)/Contents.json"
|
let contentsJsonPath = "\(colorSetPath)/Contents.json"
|
||||||
Shell.shell("touch", "\(contentsJsonPath)")
|
|
||||||
|
let fileManager = FileManager()
|
||||||
|
if fileManager.fileExists(atPath: colorSetPath) == false {
|
||||||
|
do {
|
||||||
|
try fileManager.createDirectory(atPath: colorSetPath,
|
||||||
|
withIntermediateDirectories: true)
|
||||||
|
} catch {
|
||||||
|
let error = ColorsToolError.createAssetFolder(colorSetPath)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Colors.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write content in Contents.json
|
// Write content in Contents.json
|
||||||
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
|
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
|
||||||
do {
|
do {
|
||||||
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: true, encoding: .utf8)
|
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch (let error) {
|
||||||
let error = ColorToolError.writeAsset(error.localizedDescription)
|
let error = ColorsToolError.writeAsset(error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// ColorStyle.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
// Created by Thibaut Schmitt on 29/08/2022.
|
@ -25,9 +25,9 @@ struct ParsedColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard allComponents.contains(true) == false else {
|
guard allComponents.contains(true) == false else {
|
||||||
let error = ColorToolError.badColorDefinition(light, dark)
|
let error = ColorsToolError.badColorDefinition(light, dark)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
ColorTool.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return """
|
return """
|
56
Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift
Normal file
56
Sources/ResgenSwift/Colors/Parser/ColorFileParser.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// ColorFileParser.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 29/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ColorFileParser {
|
||||||
|
static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] {
|
||||||
|
// Get content of input file
|
||||||
|
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||||
|
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
|
// Iterate on each line of input file
|
||||||
|
return 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"
|
||||||
|
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.localizedDescription)
|
||||||
|
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]))
|
||||||
|
}
|
||||||
|
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// FontOptions.swift
|
// FontsOptions.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 17/01/2022.
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct FontOptions: ParsableArguments {
|
struct FontsOptions: ParsableArguments {
|
||||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
var forceGeneration = false
|
var forceGeneration = false
|
||||||
|
|
||||||
@ -18,9 +18,27 @@ struct FontOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@Option(help: "Tell if it will generate static properties or methods")
|
||||||
|
var staticMembers: Bool = false
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
|
@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
|
var extensionName: String = Fonts.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension FontsOptions {
|
||||||
|
var extensionFileName: String {
|
||||||
|
if extensionSuffix.isEmpty == false {
|
||||||
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionName).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// FontTool.swift
|
// Fonts.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 13/12/2021.
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
@ -9,7 +9,7 @@ import ToolCore
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct FontTool: ParsableCommand {
|
struct Fonts: ParsableCommand {
|
||||||
|
|
||||||
// MARK: - CommandConfiguration
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
@ -20,30 +20,18 @@ struct FontTool: ParsableCommand {
|
|||||||
|
|
||||||
// MARK: - Static
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "FontTool"
|
static let toolName = "Fonts"
|
||||||
static let defaultExtensionName = "UIFont"
|
static let defaultExtensionName = "UIFont"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var extensionFileName: String {
|
|
||||||
if options.extensionSuffix.isEmpty == false {
|
|
||||||
return "\(options.extensionName)+\(options.extensionSuffix).swift"
|
|
||||||
}
|
|
||||||
return "\(options.extensionName).swift"
|
|
||||||
}
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
var generateStaticVariable: Bool {
|
|
||||||
options.extensionName == Self.defaultExtensionName
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command Options
|
// MARK: - Command Options
|
||||||
|
|
||||||
@OptionGroup var options: FontOptions
|
@OptionGroup var options: FontsOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
public func run() throws {
|
public func run() throws {
|
||||||
print("[\(Self.toolName)] Starting fonts generation")
|
print("[\(Self.toolName)] Starting fonts generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate fonts")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
@ -55,14 +43,14 @@ struct FontTool: ParsableCommand {
|
|||||||
|
|
||||||
// Get real font names
|
// Get real font names
|
||||||
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
|
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
|
||||||
let fontsNames = FontToolHelper.getFontPostScriptName(for: fontsToGenerate,
|
let fontsNames = FontsToolHelper.getFontPostScriptName(for: fontsToGenerate,
|
||||||
inputFolder: inputFolder)
|
inputFolder: inputFolder)
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
|
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
|
||||||
staticVar: generateStaticVariable,
|
staticVar: options.staticMembers,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: options.extensionFilePath)
|
||||||
|
|
||||||
print("Info.plist information:")
|
print("Info.plist information:")
|
||||||
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))")
|
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))")
|
||||||
@ -77,13 +65,15 @@ struct FontTool: ParsableCommand {
|
|||||||
|
|
||||||
// Check input file exists
|
// Check input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = FontToolError.fileNotExists(options.inputFile)
|
let error = FontsToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
FontTool.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
print("[\(Self.toolName)] Fonts are already up to date :) ")
|
print("[\(Self.toolName)] Fonts are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -91,5 +81,3 @@ struct FontTool: ParsableCommand {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FontTool.main()
|
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// FontToolError.swift
|
// FontsToolError.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 13/12/2021.
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum FontToolError: Error {
|
enum FontsToolError: Error {
|
||||||
case fcScan(String, Int32, String?)
|
case fcScan(String, Int32, String?)
|
||||||
case inputFolderNotFound(String)
|
case inputFolderNotFound(String)
|
||||||
case fileNotExists(String)
|
case fileNotExists(String)
|
||||||
@ -16,16 +16,16 @@ enum FontToolError: Error {
|
|||||||
var localizedDescription: String {
|
var localizedDescription: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .fcScan(let path, let code, let output):
|
case .fcScan(let path, let code, let output):
|
||||||
return "error:[\(FontTool.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")"
|
return "error:[\(Fonts.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")"
|
||||||
|
|
||||||
case .inputFolderNotFound(let inputFolder):
|
case .inputFolderNotFound(let inputFolder):
|
||||||
return " error:[\(FontTool.toolName)] Input folder not found: \(inputFolder)"
|
return " error:[\(Fonts.toolName)] Input folder not found: \(inputFolder)"
|
||||||
|
|
||||||
case .fileNotExists(let filename):
|
case .fileNotExists(let filename):
|
||||||
return " error:[\(FontTool.toolName)] File \(filename) does not exists"
|
return " error:[\(Fonts.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
case .writeExtension(let filename, let info):
|
case .writeExtension(let filename, let info):
|
||||||
return "error:[\(FontTool.toolName)] An error occured while writing extension in \(filename): \(info)"
|
return "error:[\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// FontToolHelper.swift
|
// FontsToolHelper.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 13/12/2021.
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
class FontToolHelper {
|
class FontsToolHelper {
|
||||||
|
|
||||||
static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] {
|
static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] {
|
||||||
let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder)
|
let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder)
|
||||||
@ -38,9 +38,9 @@ class FontToolHelper {
|
|||||||
// Get a enumerator for all files
|
// Get a enumerator for all files
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
guard fileManager.fileExists(atPath: inputFolder) else {
|
guard fileManager.fileExists(atPath: inputFolder) else {
|
||||||
let error = FontToolError.inputFolderNotFound(inputFolder)
|
let error = FontsToolError.inputFolderNotFound(inputFolder)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
FontTool.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)!
|
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)!
|
||||||
@ -60,12 +60,12 @@ class FontToolHelper {
|
|||||||
private static func getFontName(atPath path: String) -> String {
|
private static func getFontName(atPath path: String) -> String {
|
||||||
//print("fc-scan --format %{postscriptname} \(path)")
|
//print("fc-scan --format %{postscriptname} \(path)")
|
||||||
// Get real font name
|
// Get real font name
|
||||||
let task = Shell.shell("fc-scan", "--format", "%{postscriptname}", path)
|
let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path])
|
||||||
|
|
||||||
guard let fontName = task.output, task.terminationStatus == 0 else {
|
guard let fontName = task.output, task.terminationStatus == 0 else {
|
||||||
let error = FontToolError.fcScan(path, task.terminationStatus, task.output)
|
let error = FontsToolError.fcScan(path, task.terminationStatus, task.output)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
FontTool.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontName
|
return fontName
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// FontPlistGenerator.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
// Created by Thibaut Schmitt on 29/08/2022.
|
||||||
@ -15,7 +15,7 @@ class FontPlistGenerator {
|
|||||||
.forEach {
|
.forEach {
|
||||||
plistData += "\t\t<string>\($0)</string>\n"
|
plistData += "\t\t<string>\($0)</string>\n"
|
||||||
}
|
}
|
||||||
plistData += "\t</array>\n*/"
|
plistData += "\t</array>"
|
||||||
|
|
||||||
return plistData
|
return plistData
|
||||||
}
|
}
|
@ -11,35 +11,35 @@ import ToolCore
|
|||||||
class FontExtensionGenerator {
|
class FontExtensionGenerator {
|
||||||
|
|
||||||
static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||||
// Check file if not exists
|
|
||||||
let fileManager = FileManager()
|
|
||||||
if fileManager.fileExists(atPath: extensionFilePath) == false {
|
|
||||||
Shell.shell("touch", "\(extensionFilePath)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create extension content
|
// Create extension content
|
||||||
let extensionContent = [
|
let extensionContent = Self.getExtensionContent(fontsNames: fontsNames,
|
||||||
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName)
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Fonts.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getExtensionContent(fontsNames: [String], staticVar: Bool, extensionName: String) -> String {
|
||||||
|
[
|
||||||
Self.getHeader(extensionClassname: extensionName),
|
Self.getHeader(extensionClassname: extensionName),
|
||||||
Self.getFontNameEnum(fontsNames: fontsNames),
|
Self.getFontNameEnum(fontsNames: fontsNames),
|
||||||
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar),
|
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar),
|
||||||
Self.getFooter()
|
Self.getFooter()
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
|
|
||||||
// Write content
|
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
|
||||||
do {
|
|
||||||
try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
|
||||||
} catch (let error) {
|
|
||||||
let error = FontToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
|
||||||
print(error.localizedDescription)
|
|
||||||
FontTool.exit(withError: error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
private static func getHeader(extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.\(FontTool.toolName) \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@ -48,18 +48,18 @@ class FontExtensionGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func getFontNameEnum(fontsNames: [String]) -> String {
|
private static func getFontNameEnum(fontsNames: [String]) -> String {
|
||||||
var enumDefinition = "\tenum FontName: String {\n"
|
var enumDefinition = " enum FontName: String {\n"
|
||||||
|
|
||||||
fontsNames.forEach {
|
fontsNames.forEach {
|
||||||
enumDefinition += "\t\tcase \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n"
|
enumDefinition += " case \($0.fontNameSanitize) = \"\($0)\"\n"
|
||||||
}
|
}
|
||||||
enumDefinition += "\t}\n"
|
enumDefinition += " }\n"
|
||||||
|
|
||||||
return enumDefinition
|
return enumDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String {
|
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String {
|
||||||
let pragma = "\t// MARK: - Getter"
|
let pragma = " // MARK: - Getter"
|
||||||
|
|
||||||
var propertiesOrMethods: [String] = fontsNames
|
var propertiesOrMethods: [String] = fontsNames
|
||||||
.unique()
|
.unique()
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// FontName.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
// Created by Thibaut Schmitt on 29/08/2022.
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// FontFileParser.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
// Created by Thibaut Schmitt on 29/08/2022.
|
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// StringExtensions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 31/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
|
||||||
|
func prependIfRelativePath(_ prependPath: String) -> String {
|
||||||
|
if self.hasPrefix("/") {
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
return prependPath + self
|
||||||
|
}
|
||||||
|
}
|
57
Sources/ResgenSwift/Generate/Generate.swift
Normal file
57
Sources/ResgenSwift/Generate/Generate.swift
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Generate.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ToolCore
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct Generate: ParsableCommand {
|
||||||
|
|
||||||
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
|
static var configuration = CommandConfiguration(
|
||||||
|
abstract: "A utility to generate ressources based on a configuration file",
|
||||||
|
version: ResgenSwiftVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
// MARK: - Static
|
||||||
|
|
||||||
|
static let toolName = "Generate"
|
||||||
|
|
||||||
|
// MARK: - Command Options
|
||||||
|
|
||||||
|
@OptionGroup var options: GenerateOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
|
public func run() throws {
|
||||||
|
print("[\(Self.toolName)] Starting Resgen with configuration: \(options.configurationFile)")
|
||||||
|
|
||||||
|
// Parse
|
||||||
|
let configuration = ConfigurationFileParser.parse(options.configurationFile)
|
||||||
|
print("Found configurations :")
|
||||||
|
print(" - \(configuration.colors.count) colors configuration")
|
||||||
|
print(" - \(configuration.fonts.count) fonts configuration")
|
||||||
|
print(" - \(configuration.images.count) images configuration")
|
||||||
|
print(" - \(configuration.strings.count) strings configuration")
|
||||||
|
print(" - \(configuration.tags.count) tags configuration")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Input file: \(configuration.colors.first?.inputFile ?? "no input file")")
|
||||||
|
|
||||||
|
// Execute commands
|
||||||
|
configuration.runnableConfigurations
|
||||||
|
.forEach {
|
||||||
|
let begin = Date()
|
||||||
|
$0.run(projectDirectory: options.projectDirectory,
|
||||||
|
force: options.forceGeneration)
|
||||||
|
print("Took: \(Date().timeIntervalSince(begin))s\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Resgen ended")
|
||||||
|
}
|
||||||
|
}
|
30
Sources/ResgenSwift/Generate/GenerateError.swift
Normal file
30
Sources/ResgenSwift/Generate/GenerateError.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// ResgenSwiftError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum GenerateError: Error {
|
||||||
|
case fileNotExists(String)
|
||||||
|
case invalidConfigurationFile(String)
|
||||||
|
case commandError([String], String)
|
||||||
|
|
||||||
|
var localizedDescription: String {
|
||||||
|
switch self {
|
||||||
|
case .fileNotExists(let filename):
|
||||||
|
return " error:[\(Generate.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
|
case .invalidConfigurationFile(let filename):
|
||||||
|
return " error:[\(Generate.toolName)] File \(filename) is not a valid configuration file"
|
||||||
|
|
||||||
|
case .commandError(let command, let terminationStatus):
|
||||||
|
let readableCommand = command
|
||||||
|
.map { $0 }
|
||||||
|
.joined(separator: " ")
|
||||||
|
return "error:[\(Generate.toolName)] An error occured while running command '\(readableCommand)'. Command terminate with status code: \(terminationStatus)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
Sources/ResgenSwift/Generate/GenerateOptions.swift
Normal file
28
Sources/ResgenSwift/Generate/GenerateOptions.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// GenerateOptions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct GenerateOptions: ParsableArguments {
|
||||||
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
|
var forceGeneration = false
|
||||||
|
|
||||||
|
@Argument(help: "Configuration file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var configurationFile: String
|
||||||
|
|
||||||
|
@Option(help: "Project directory. It will be added to every relative path (path that does not start with `/`",
|
||||||
|
transform: {
|
||||||
|
if $0.last == "/" {
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
return $0 + "/"
|
||||||
|
})
|
||||||
|
var projectDirectory: String
|
||||||
|
}
|
180
Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift
Normal file
180
Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
//
|
||||||
|
// ConfigurationFile.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct ConfigurationFile: Codable, CustomDebugStringConvertible {
|
||||||
|
var colors: [ColorsConfiguration]
|
||||||
|
var fonts: [FontsConfiguration]
|
||||||
|
var images: [ImagesConfiguration]
|
||||||
|
var strings: [StringsConfiguration]
|
||||||
|
var tags: [TagsConfiguration]
|
||||||
|
|
||||||
|
var runnableConfigurations: [Runnable] {
|
||||||
|
let runnables: [[Runnable]] = [colors, fonts, images, strings, tags]
|
||||||
|
return Array(runnables.joined())
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
\(colors)
|
||||||
|
-----------
|
||||||
|
-----------
|
||||||
|
\(fonts)
|
||||||
|
-----------
|
||||||
|
-----------
|
||||||
|
\(images)
|
||||||
|
-----------
|
||||||
|
-----------
|
||||||
|
\(strings)
|
||||||
|
-----------
|
||||||
|
-----------
|
||||||
|
\(tags)
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ColorsConfiguration: Codable, CustomDebugStringConvertible {
|
||||||
|
let inputFile: String
|
||||||
|
let style: String
|
||||||
|
let xcassetsPath: String
|
||||||
|
let extensionOutputPath: String
|
||||||
|
let extensionName: String?
|
||||||
|
let extensionSuffix: String?
|
||||||
|
private let staticMembers: Bool?
|
||||||
|
|
||||||
|
var staticMembersOptions: Bool {
|
||||||
|
if let staticMembers = staticMembers {
|
||||||
|
return staticMembers
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
Colors configuration:
|
||||||
|
- Input file: \(inputFile)
|
||||||
|
- Style: \(style)
|
||||||
|
- Xcassets path: \(xcassetsPath)
|
||||||
|
- Extension output path: \(extensionOutputPath)
|
||||||
|
- Extension name: \(extensionName ?? "-")
|
||||||
|
- Extension suffix: \(extensionSuffix ?? "-")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FontsConfiguration: Codable, CustomDebugStringConvertible {
|
||||||
|
let inputFile: String
|
||||||
|
let extensionOutputPath: String
|
||||||
|
let extensionName: String?
|
||||||
|
let extensionSuffix: String?
|
||||||
|
private let staticMembers: Bool?
|
||||||
|
|
||||||
|
var staticMembersOptions: Bool {
|
||||||
|
if let staticMembers = staticMembers {
|
||||||
|
return staticMembers
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
Fonts configuration:
|
||||||
|
- Input file: \(inputFile)
|
||||||
|
- Extension output path: \(extensionOutputPath)
|
||||||
|
- Extension name: \(extensionName ?? "-")
|
||||||
|
- Extension suffix: \(extensionSuffix ?? "-")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImagesConfiguration: Codable, CustomDebugStringConvertible {
|
||||||
|
let inputFile: String
|
||||||
|
let xcassetsPath: String
|
||||||
|
let extensionOutputPath: String
|
||||||
|
let extensionName: String?
|
||||||
|
let extensionSuffix: String?
|
||||||
|
private let staticMembers: Bool?
|
||||||
|
|
||||||
|
var staticMembersOptions: Bool {
|
||||||
|
if let staticMembers = staticMembers {
|
||||||
|
return staticMembers
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
Images configuration:
|
||||||
|
- Input file: \(inputFile)
|
||||||
|
- Xcassets path: \(xcassetsPath)
|
||||||
|
- Extension output path: \(extensionOutputPath)
|
||||||
|
- Extension name: \(extensionName ?? "-")
|
||||||
|
- Extension suffix: \(extensionSuffix ?? "-")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringsConfiguration: Codable, CustomDebugStringConvertible {
|
||||||
|
let inputFile: String
|
||||||
|
let outputPath: String
|
||||||
|
let langs: String
|
||||||
|
let defaultLang: String
|
||||||
|
let extensionOutputPath: String
|
||||||
|
let extensionName: String?
|
||||||
|
let extensionSuffix: String?
|
||||||
|
private let staticMembers: Bool?
|
||||||
|
|
||||||
|
var staticMembersOptions: Bool {
|
||||||
|
if let staticMembers = staticMembers {
|
||||||
|
return staticMembers
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
Strings configuration:
|
||||||
|
- Input file: \(inputFile)
|
||||||
|
- Output path: \(outputPath)
|
||||||
|
- Langs: \(langs)
|
||||||
|
- Default lang: \(defaultLang)
|
||||||
|
- Extension output path: \(extensionOutputPath)
|
||||||
|
- Extension name: \(extensionName ?? "-")
|
||||||
|
- Extension suffix: \(extensionSuffix ?? "-")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TagsConfiguration: Codable, CustomDebugStringConvertible {
|
||||||
|
let inputFile: String
|
||||||
|
let lang: String
|
||||||
|
let extensionOutputPath: String
|
||||||
|
let extensionName: String?
|
||||||
|
let extensionSuffix: String?
|
||||||
|
private let staticMembers: Bool?
|
||||||
|
|
||||||
|
var staticMembersOptions: Bool {
|
||||||
|
if let staticMembers = staticMembers {
|
||||||
|
return staticMembers
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var debugDescription: String {
|
||||||
|
"""
|
||||||
|
Tags configuration:
|
||||||
|
- Input file: \(inputFile)
|
||||||
|
- Lang: \(lang)
|
||||||
|
- Extension output path: \(extensionOutputPath)
|
||||||
|
- Extension name: \(extensionName ?? "-")
|
||||||
|
- Extension suffix: \(extensionSuffix ?? "-")
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// ConfigurationFileParser.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Yams
|
||||||
|
|
||||||
|
class ConfigurationFileParser {
|
||||||
|
static func parse(_ configurationFile: String) -> ConfigurationFile {
|
||||||
|
guard let data = FileManager().contents(atPath: configurationFile) else {
|
||||||
|
let error = GenerateError.fileNotExists(configurationFile)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Generate.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let configuration = try? YAMLDecoder().decode(ConfigurationFile.self, from: data) else {
|
||||||
|
let error = GenerateError.invalidConfigurationFile(configurationFile)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Generate.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// ColorsConfiguration+Runnable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ColorsConfiguration: Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool) {
|
||||||
|
var args = [String]()
|
||||||
|
|
||||||
|
if force {
|
||||||
|
args += ["-f"]
|
||||||
|
}
|
||||||
|
|
||||||
|
args += [
|
||||||
|
inputFile.prependIfRelativePath(projectDirectory),
|
||||||
|
"--style",
|
||||||
|
style,
|
||||||
|
"--xcassets-path",
|
||||||
|
xcassetsPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--extension-output-path",
|
||||||
|
extensionOutputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--static-members",
|
||||||
|
"\(staticMembersOptions)"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let extensionName = extensionName {
|
||||||
|
args += [
|
||||||
|
"--extension-name",
|
||||||
|
extensionName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
args += [
|
||||||
|
"--extension-suffix",
|
||||||
|
extensionSuffix
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Colors.main(args)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// FontsConfiguration+Runnable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension FontsConfiguration: Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool) {
|
||||||
|
var args = [String]()
|
||||||
|
|
||||||
|
if force {
|
||||||
|
args += ["-f"]
|
||||||
|
}
|
||||||
|
|
||||||
|
args += [
|
||||||
|
inputFile.prependIfRelativePath(projectDirectory),
|
||||||
|
"--extension-output-path",
|
||||||
|
extensionOutputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--static-members",
|
||||||
|
"\(staticMembersOptions)"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let extensionName = extensionName {
|
||||||
|
args += [
|
||||||
|
"--extension-name",
|
||||||
|
extensionName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
args += [
|
||||||
|
"--extension-suffix",
|
||||||
|
extensionSuffix
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Fonts.main(args)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// ImagesConfiguration+Runnable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ImagesConfiguration: Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool) {
|
||||||
|
var args = [String]()
|
||||||
|
|
||||||
|
if force {
|
||||||
|
args += ["-f"] // Images has a -f and -F options
|
||||||
|
}
|
||||||
|
|
||||||
|
args += [
|
||||||
|
inputFile.prependIfRelativePath(projectDirectory),
|
||||||
|
"--xcassets-path",
|
||||||
|
xcassetsPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--extension-output-path",
|
||||||
|
extensionOutputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--static-members",
|
||||||
|
"\(staticMembersOptions)"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let extensionName = extensionName {
|
||||||
|
args += [
|
||||||
|
"--extension-name",
|
||||||
|
extensionName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
args += [
|
||||||
|
"--extension-suffix",
|
||||||
|
extensionSuffix
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Images.main(args)
|
||||||
|
}
|
||||||
|
}
|
13
Sources/ResgenSwift/Generate/Runnable/Runnable.swift
Normal file
13
Sources/ResgenSwift/Generate/Runnable/Runnable.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// ShellCommandable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// StringsConfiguration+Runnable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension StringsConfiguration: Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool) {
|
||||||
|
var args = [String]()
|
||||||
|
|
||||||
|
if force {
|
||||||
|
args += ["-f"]
|
||||||
|
}
|
||||||
|
|
||||||
|
args += [
|
||||||
|
inputFile.prependIfRelativePath(projectDirectory),
|
||||||
|
"--output-path",
|
||||||
|
outputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--langs",
|
||||||
|
langs,
|
||||||
|
"--default-lang",
|
||||||
|
defaultLang,
|
||||||
|
"--extension-output-path",
|
||||||
|
extensionOutputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--static-members",
|
||||||
|
"\(staticMembersOptions)"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let extensionName = extensionName {
|
||||||
|
args += [
|
||||||
|
"--extension-name",
|
||||||
|
extensionName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
args += [
|
||||||
|
"--extension-suffix",
|
||||||
|
extensionSuffix
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Stringium.main(args)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// TagsConfiguration+Runnable.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 30/08/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension TagsConfiguration: Runnable {
|
||||||
|
func run(projectDirectory: String, force: Bool) {
|
||||||
|
var args = [String]()
|
||||||
|
|
||||||
|
if force {
|
||||||
|
args += ["-f"]
|
||||||
|
}
|
||||||
|
|
||||||
|
args += [
|
||||||
|
inputFile.prependIfRelativePath(projectDirectory),
|
||||||
|
"--lang",
|
||||||
|
lang,
|
||||||
|
"--extension-output-path",
|
||||||
|
extensionOutputPath.prependIfRelativePath(projectDirectory),
|
||||||
|
"--static-members",
|
||||||
|
"\(staticMembersOptions)"
|
||||||
|
]
|
||||||
|
|
||||||
|
if let extensionName = extensionName {
|
||||||
|
args += [
|
||||||
|
"--extension-name",
|
||||||
|
extensionName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
args += [
|
||||||
|
"--extension-suffix",
|
||||||
|
extensionSuffix
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Tags.main(args)
|
||||||
|
}
|
||||||
|
}
|
@ -11,9 +11,9 @@ extension FileManager {
|
|||||||
func getAllRegularFileIn(directory: String) -> [String] {
|
func getAllRegularFileIn(directory: String) -> [String] {
|
||||||
var files = [String]()
|
var files = [String]()
|
||||||
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
||||||
let error = ImagiumError.unknown("Cannot enumerate file in \(directory)")
|
let error = ImagesError.unknown("Cannot enumerate file in \(directory)")
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for case let fileURL as URL in enumerator {
|
for case let fileURL as URL in enumerator {
|
||||||
@ -23,9 +23,9 @@ extension FileManager {
|
|||||||
files.append(fileURL.relativePath)
|
files.append(fileURL.relativePath)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
let error = ImagesError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return files
|
return files
|
||||||
@ -34,9 +34,9 @@ extension FileManager {
|
|||||||
func getAllImageSetFolderIn(directory: String) -> [String] {
|
func getAllImageSetFolderIn(directory: String) -> [String] {
|
||||||
var files = [String]()
|
var files = [String]()
|
||||||
guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
|
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)")
|
let error = ImagesError.unknown("Cannot enumerate imageset directory in \(directory)")
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for case let fileURL as URL in enumerator {
|
for case let fileURL as URL in enumerator {
|
||||||
@ -46,9 +46,9 @@ extension FileManager {
|
|||||||
files.append(fileURL.lastPathComponent)
|
files.append(fileURL.lastPathComponent)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
let error = ImagesError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return files
|
return files
|
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// ImageExtensionGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 14/02/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ToolCore
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ImageExtensionGenerator {
|
||||||
|
|
||||||
|
// MARK: - pragm
|
||||||
|
|
||||||
|
static func generateExtensionFile(images: [ParsedImage],
|
||||||
|
staticVar: Bool,
|
||||||
|
inputFilename: String,
|
||||||
|
extensionName: String,
|
||||||
|
extensionFilePath: String) {
|
||||||
|
// Create extension conten1t
|
||||||
|
let extensionContent = Self.getExtensionContent(images: images,
|
||||||
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName,
|
||||||
|
inputFilename: inputFilename)
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = ImagesError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Images.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension content
|
||||||
|
|
||||||
|
static func getExtensionContent(images: [ParsedImage], staticVar: Bool, extensionName: String, inputFilename: String) -> String {
|
||||||
|
[
|
||||||
|
Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName),
|
||||||
|
Self.getProperties(images: images, staticVar: staticVar),
|
||||||
|
Self.getFooter()
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension part
|
||||||
|
|
||||||
|
private static func getHeader(inputFilename: String, extensionClassname: String) -> String {
|
||||||
|
"""
|
||||||
|
// Generated by ResgenSwift.\(Images.toolName) \(ResgenSwiftVersion)
|
||||||
|
// Images from \(inputFilename)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension \(extensionClassname) {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getProperties(images: [ParsedImage], staticVar: Bool) -> String {
|
||||||
|
if staticVar {
|
||||||
|
return images
|
||||||
|
.map { "\n\($0.getStaticImageProperty())" }
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
return images
|
||||||
|
.map { "\n\($0.getImageProperty())" }
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getFooter() -> String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// XcassetsGenerator.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 24/01/2022.
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
@ -24,7 +24,7 @@ class XcassetsGenerator {
|
|||||||
|
|
||||||
func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) {
|
func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) {
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
let svgConverter = Imagium.getSvgConverterPath()
|
let svgConverter = Images.getSvgConverterPath()
|
||||||
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
|
||||||
|
|
||||||
var generatedAssetsPaths = [String]()
|
var generatedAssetsPaths = [String]()
|
||||||
@ -47,15 +47,14 @@ class XcassetsGenerator {
|
|||||||
return (subfile, "jepg")
|
return (subfile, "jepg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let error = ImagiumError.unknownImageExtension(parsedImage.name)
|
let error = ImagesError.unknownImageExtension(parsedImage.name)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Create imageset folder
|
// Create imageset folder name
|
||||||
let imagesetName = "\(parsedImage.name).imageset"
|
let imagesetName = "\(parsedImage.name).imageset"
|
||||||
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
|
||||||
Shell.shell("mkdir", "-p", imagesetPath)
|
|
||||||
|
|
||||||
// Store managed images path
|
// Store managed images path
|
||||||
generatedAssetsPaths.append(imagesetName)
|
generatedAssetsPaths.append(imagesetName)
|
||||||
@ -66,11 +65,23 @@ class XcassetsGenerator {
|
|||||||
let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
|
||||||
// Check if we need to convert image
|
// Check if we need to convert image
|
||||||
if self.shouldBypassGeneration(for: parsedImage, xcassetImagePath: output1x) {
|
guard self.shouldGenerate(inputImagePath: imageData.path, xcassetImagePath: output1x) else {
|
||||||
print("\(parsedImage.name) -> Not regenerating")
|
//print("\(parsedImage.name) -> Not regenerating")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create imageset folder
|
||||||
|
if fileManager.fileExists(atPath: imagesetPath) == false {
|
||||||
|
do {
|
||||||
|
try fileManager.createDirectory(atPath: imagesetPath,
|
||||||
|
withIntermediateDirectories: true)
|
||||||
|
} catch {
|
||||||
|
let error = ImagesError.createAssetFolder(imagesetPath)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Images.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert image
|
// Convert image
|
||||||
let convertArguments = parsedImage.convertArguments
|
let convertArguments = parsedImage.convertArguments
|
||||||
if imageData.ext == "svg" {
|
if imageData.ext == "svg" {
|
||||||
@ -96,20 +107,23 @@ class XcassetsGenerator {
|
|||||||
// convert path/to/image.png -resize 200x300 path/to/output.png
|
// 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 200x path/to/output.png
|
||||||
// convert path/to/image.png -resize x300 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)",
|
||||||
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x)
|
"-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")",
|
||||||
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x)
|
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
|
// Write Content.json
|
||||||
let imagesetContentJson = parsedImage.contentJson
|
let imagesetContentJson = parsedImage.contentJson
|
||||||
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
|
||||||
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
|
|
||||||
Shell.shell("touch", "\(contentJsonFilePath)")
|
|
||||||
}
|
|
||||||
|
|
||||||
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
|
||||||
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
|
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
|
||||||
print("\(parsedImage.name) -> Generated")
|
print("\(parsedImage.name) -> Generated")
|
||||||
}
|
}
|
||||||
@ -147,43 +161,11 @@ class XcassetsGenerator {
|
|||||||
|
|
||||||
// MARK: - Helpers: bypass generation
|
// MARK: - Helpers: bypass generation
|
||||||
|
|
||||||
private func shouldBypassGeneration(for image: ParsedImage, xcassetImagePath: String) -> Bool {
|
private func shouldGenerate(inputImagePath: String, xcassetImagePath: String) -> Bool {
|
||||||
guard forceGeneration == false else {
|
if forceGeneration {
|
||||||
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 true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return GeneratorChecker.isFile(inputImagePath, moreRecenThan: xcassetImagePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Imagium.swift
|
// Images.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 24/01/2022.
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
@ -9,7 +9,7 @@ import ToolCore
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct Imagium: ParsableCommand {
|
struct Images: ParsableCommand {
|
||||||
|
|
||||||
// MARK: - CommandConfiguration
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
@ -20,27 +20,18 @@ struct Imagium: ParsableCommand {
|
|||||||
|
|
||||||
// MARK: - Static
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "Imagium"
|
static let toolName = "Images"
|
||||||
static let defaultExtensionName = "UIImage"
|
static let defaultExtensionName = "UIImage"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var extensionFileName: String { "\(options.extensionName)+\(options.extensionSuffix).swift" }
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
var inputFilenameWithoutExt: String {
|
|
||||||
URL(fileURLWithPath: options.inputFile)
|
|
||||||
.deletingPathExtension()
|
|
||||||
.lastPathComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command Options
|
// MARK: - Command Options
|
||||||
|
|
||||||
@OptionGroup var options: ImagiumOptions
|
@OptionGroup var options: ImagesOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting images generation")
|
print("[\(Self.toolName)] Starting images generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate images in xcassets \(options.xcassetsPath)")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
@ -54,17 +45,18 @@ struct Imagium: ParsableCommand {
|
|||||||
let inputFolder = URL(fileURLWithPath: options.inputFile)
|
let inputFolder = URL(fileURLWithPath: options.inputFile)
|
||||||
.deletingLastPathComponent()
|
.deletingLastPathComponent()
|
||||||
.relativePath
|
.relativePath
|
||||||
|
|
||||||
let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration)
|
let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration)
|
||||||
xcassetsGenerator.generateXcassets(inputPath: inputFolder,
|
xcassetsGenerator.generateXcassets(inputPath: inputFolder,
|
||||||
imagesToGenerate: imagesToGenerate,
|
imagesToGenerate: imagesToGenerate,
|
||||||
xcassetsPath: options.xcassetsPath)
|
xcassetsPath: options.xcassetsPath)
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate,
|
ImageExtensionGenerator.generateExtensionFile(images: imagesToGenerate,
|
||||||
staticVar: options.extensionName == Self.defaultExtensionName,
|
staticVar: options.staticMembers,
|
||||||
inputFilename: inputFilenameWithoutExt,
|
inputFilename: options.inputFilenameWithoutExt,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: options.extensionFilePath)
|
||||||
|
|
||||||
|
|
||||||
print("[\(Self.toolName)] Images generated")
|
print("[\(Self.toolName)] Images generated")
|
||||||
@ -81,16 +73,18 @@ struct Imagium: ParsableCommand {
|
|||||||
|
|
||||||
// Input file
|
// Input file
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = ImagiumError.fileNotExists(options.inputFile)
|
let error = ImagesError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSVG-Converter
|
// RSVG-Converter
|
||||||
_ = Imagium.getSvgConverterPath()
|
_ = Images.getSvgConverterPath()
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceExecution,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
print("[\(Self.toolName)] Images are already up to date :) ")
|
print("[\(Self.toolName)] Images are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -102,15 +96,13 @@ struct Imagium: ParsableCommand {
|
|||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
static func getSvgConverterPath() -> String {
|
static func getSvgConverterPath() -> String {
|
||||||
let taskSvgConverter = Shell.shell("which", "rsvg-convert")
|
let taskSvgConverter = Shell.shell(["which", "rsvg-convert"])
|
||||||
if taskSvgConverter.terminationStatus == 0 {
|
if taskSvgConverter.terminationStatus == 0 {
|
||||||
return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines)
|
return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines)
|
||||||
}
|
}
|
||||||
|
|
||||||
let error = ImagiumError.rsvgConvertNotFound
|
let error = ImagesError.rsvgConvertNotFound
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
Imagium.exit(withError: error)
|
Images.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Imagium.main()
|
|
47
Sources/ResgenSwift/Images/ImagesError.swift
Normal file
47
Sources/ResgenSwift/Images/ImagesError.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// ImagesError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 24/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum ImagesError: Error {
|
||||||
|
case inputFolderNotFound(String)
|
||||||
|
case fileNotExists(String)
|
||||||
|
case unknownImageExtension(String)
|
||||||
|
case getFileAttributed(String, String)
|
||||||
|
case rsvgConvertNotFound
|
||||||
|
case writeFile(String, String)
|
||||||
|
case createAssetFolder(String)
|
||||||
|
case unknown(String)
|
||||||
|
|
||||||
|
var localizedDescription: String {
|
||||||
|
switch self {
|
||||||
|
case .inputFolderNotFound(let inputFolder):
|
||||||
|
return " error:[\(Images.toolName)] Input folder not found: \(inputFolder)"
|
||||||
|
|
||||||
|
case .fileNotExists(let filename):
|
||||||
|
return " error:[\(Images.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
|
case .unknownImageExtension(let filename):
|
||||||
|
return " error:[\(Images.toolName)] File \(filename) have an unhandled file extension. Cannot generate image."
|
||||||
|
|
||||||
|
case .getFileAttributed(let filename, let errorDescription):
|
||||||
|
return " error:[\(Images.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)"
|
||||||
|
|
||||||
|
case .rsvgConvertNotFound:
|
||||||
|
return " error:[\(Images.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:[\(Images.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||||
|
|
||||||
|
case .createAssetFolder(let folder):
|
||||||
|
return "error:[\(Colors.toolName)] An error occured while creating folder `\(folder)`"
|
||||||
|
|
||||||
|
case .unknown(let errorDescription):
|
||||||
|
return " error:[\(Images.toolName)] Unknown error: \(errorDescription)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
|
||||||
struct ImagiumOptions: ParsableArguments {
|
struct ImagesOptions: ParsableArguments {
|
||||||
@Flag(name: .customShort("f"), help: "Should force script execution")
|
@Flag(name: .customShort("f"), help: "Should force script execution")
|
||||||
var forceExecution = false
|
var forceExecution = false
|
||||||
|
|
||||||
@ -24,18 +24,33 @@ struct ImagiumOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@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 UIImage extension. Using default extension name will generate static property.")
|
@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
|
var extensionName: String = Images.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
/*
|
extension ImagesOptions {
|
||||||
swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
|
var extensionFileName: String {
|
||||||
--xcassets-path "./Images/imagium.xcassets" \
|
if let extensionSuffix = extensionSuffix {
|
||||||
--extension-output-path "./Images/Generated" \
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
--extension-name "UIImage" \
|
}
|
||||||
--extension-suffix "GenAllScript"
|
return "\(extensionName).swift"
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputFilenameWithoutExt: String {
|
||||||
|
URL(fileURLWithPath: inputFile)
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,13 @@ class ImageFileParser {
|
|||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
||||||
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
let stringsByLines = inputFileContent.components(separatedBy: .newlines)
|
||||||
|
|
||||||
|
return Self.parseLines(stringsByLines, platform: platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func parseLines(_ lines: [String], platform: PlatormTag) -> [ParsedImage] {
|
||||||
var imagesToGenerate = [ParsedImage]()
|
var imagesToGenerate = [ParsedImage]()
|
||||||
|
|
||||||
// Parse file
|
lines.forEach {
|
||||||
stringsByLines.forEach {
|
|
||||||
guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else {
|
guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else {
|
||||||
return
|
return
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ class StringsFileGenerator {
|
|||||||
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
|
||||||
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
|
||||||
do {
|
do {
|
||||||
try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
|
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch (let error) {
|
||||||
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
@ -37,7 +37,7 @@ class StringsFileGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
|
||||||
var stringsFileContent = """
|
var stringsFileContent = """
|
||||||
/**
|
/**
|
||||||
* Apple Strings File
|
* Apple Strings File
|
||||||
@ -87,47 +87,18 @@ class StringsFileGenerator {
|
|||||||
// MARK: - Extension file
|
// MARK: - Extension file
|
||||||
|
|
||||||
static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
|
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)
|
// Get extension content
|
||||||
let extensionFooter = Self.getFooter()
|
let extensionFileContent = Self.getExtensionContent(sections: sections,
|
||||||
|
defaultLang: lang,
|
||||||
let extensionContent: String = {
|
tags: tags,
|
||||||
var content = ""
|
staticVar: staticVar,
|
||||||
sections.forEach { section in
|
inputFilename: inputFilename,
|
||||||
// Check that at least one string will be generated
|
extensionName: extensionName)
|
||||||
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
|
||||||
return // Go to next section
|
|
||||||
}
|
|
||||||
|
|
||||||
content += "\n\t// MARK: - \(section.name)"
|
|
||||||
section.definitions.forEach { definition in
|
|
||||||
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
|
|
||||||
return // Go to next definition
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Write content
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
do {
|
do {
|
||||||
try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch (let error) {
|
||||||
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
@ -135,6 +106,19 @@ class StringsFileGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension content
|
||||||
|
|
||||||
|
static func getExtensionContent(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String) -> String {
|
||||||
|
[
|
||||||
|
Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName),
|
||||||
|
Self.getProperties(sections: sections, defaultLang: lang, tags: tags, staticVar: staticVar),
|
||||||
|
Self.getFooter()
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension part
|
||||||
|
|
||||||
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion)
|
||||||
@ -147,6 +131,30 @@ class StringsFileGenerator {
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func getProperties(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool) -> String {
|
||||||
|
sections.compactMap { section in
|
||||||
|
// Check that at least one string will be generated
|
||||||
|
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||||
|
return nil // Go to next section
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = "\n // MARK: - \(section.name)\n"
|
||||||
|
res += section.definitions.compactMap { definition in
|
||||||
|
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
|
||||||
|
return nil // Go to next definition
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticVar {
|
||||||
|
return "\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
|
||||||
|
}
|
||||||
|
return "\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
|
||||||
|
}
|
||||||
|
.joined(separator: "\n")
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
private static func getFooter() -> String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
85
Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift
Normal file
85
Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// TagsGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 10/01/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
import CoreVideo
|
||||||
|
|
||||||
|
class TagsGenerator {
|
||||||
|
static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||||
|
// Get extension content
|
||||||
|
let extensionFileContent = Self.getExtensionContent(sections: sections,
|
||||||
|
lang: lang,
|
||||||
|
tags: tags,
|
||||||
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName)
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
} catch (let error) {
|
||||||
|
let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.localizedDescription)
|
||||||
|
Stringium.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension content
|
||||||
|
|
||||||
|
static func getExtensionContent(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String) -> String {
|
||||||
|
[
|
||||||
|
Self.getHeader(extensionClassname: extensionName, staticVar: staticVar),
|
||||||
|
Self.getProperties(sections: sections, lang: lang, tags: tags, staticVar: staticVar),
|
||||||
|
Self.getFooter()
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension part
|
||||||
|
|
||||||
|
private static func getHeader(extensionClassname: String, staticVar: Bool) -> String {
|
||||||
|
"""
|
||||||
|
// Generated by ResgenSwift.Strings.\(Tags.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
\(staticVar ? "typelias Tags = String\n\n" : "")import UIKit
|
||||||
|
|
||||||
|
extension \(extensionClassname) {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getProperties(sections: [Section], lang: String, tags: [String], staticVar: Bool) -> String {
|
||||||
|
sections
|
||||||
|
.compactMap { section in
|
||||||
|
// Check that at least one string will be generated
|
||||||
|
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||||
|
return nil// Go to next section
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = "\n // MARK: - \(section.name)"
|
||||||
|
section.definitions.forEach { definition in
|
||||||
|
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
|
||||||
|
return // Go to next definition
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticVar {
|
||||||
|
res += "\n\n\(definition.getStaticProperty(forLang: lang))"
|
||||||
|
} else {
|
||||||
|
res += "\n\n\(definition.getProperty(forLang: lang))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getFooter() -> String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
@ -53,7 +53,6 @@ class Definition {
|
|||||||
guard let match = match else { return }
|
guard let match = match else { return }
|
||||||
|
|
||||||
if let range = Range(match.range, in: input), let last = input[range].last {
|
if let range = Range(match.range, in: input), let last = input[range].last {
|
||||||
debugPrint("Found: \(input[range])")
|
|
||||||
switch last {
|
switch last {
|
||||||
case "d", "u":
|
case "d", "u":
|
||||||
methodsParameters.append("Int")
|
methodsParameters.append("Int")
|
@ -24,37 +24,15 @@ struct Stringium: ParsableCommand {
|
|||||||
static let defaultExtensionName = "String"
|
static let defaultExtensionName = "String"
|
||||||
static let noTranslationTag: String = "notranslation"
|
static let noTranslationTag: String = "notranslation"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var extensionFileName: String {
|
|
||||||
if options.extensionSuffix.isEmpty == false {
|
|
||||||
return "\(options.extensionName)+\(options.extensionSuffix).swift"
|
|
||||||
}
|
|
||||||
return "\(options.extensionName).swift"
|
|
||||||
}
|
|
||||||
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
|
|
||||||
var inputFilenameWithoutExt: String {
|
|
||||||
URL(fileURLWithPath: options.inputFile)
|
|
||||||
.deletingPathExtension()
|
|
||||||
.lastPathComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
var generateStaticVariable: Bool {
|
|
||||||
options.extensionName == Self.defaultExtensionName
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command options
|
// MARK: - Command options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
|
||||||
// arguments defined by another `ParsableArguments` type.
|
|
||||||
@OptionGroup var options: StringiumOptions
|
@OptionGroup var options: StringiumOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting strings generation")
|
print("[\(Self.toolName)] Starting strings generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate strings for \(options.langs) (default lang: \(options.defaultLang)")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
@ -70,16 +48,16 @@ struct Stringium: ParsableCommand {
|
|||||||
defaultLang: options.defaultLang,
|
defaultLang: options.defaultLang,
|
||||||
tags: options.tags,
|
tags: options.tags,
|
||||||
outputPath: options.stringsFileOutputPath,
|
outputPath: options.stringsFileOutputPath,
|
||||||
inputFilenameWithoutExt: inputFilenameWithoutExt)
|
inputFilenameWithoutExt: options.inputFilenameWithoutExt)
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
StringsFileGenerator.writeExtensionFiles(sections: sections,
|
||||||
defaultLang: options.defaultLang,
|
defaultLang: options.defaultLang,
|
||||||
tags: options.tags,
|
tags: options.tags,
|
||||||
staticVar: generateStaticVariable,
|
staticVar: options.staticMembers,
|
||||||
inputFilename: inputFilenameWithoutExt,
|
inputFilename: options.inputFilenameWithoutExt,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: options.extensionFilePath)
|
||||||
|
|
||||||
print("[\(Self.toolName)] Strings generated")
|
print("[\(Self.toolName)] Strings generated")
|
||||||
}
|
}
|
||||||
@ -110,7 +88,9 @@ struct Stringium: ParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
@ -30,13 +30,18 @@ struct StringiumOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@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 String extension. Using default extension name will generate static property.")
|
@Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
|
||||||
var extensionName: String = Stringium.defaultExtensionName
|
var extensionName: String = Stringium.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+{extensionSuffix}.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+{extensionSuffix}.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private var getter
|
||||||
|
|
||||||
extension StringiumOptions {
|
extension StringiumOptions {
|
||||||
var stringsFileOutputPath: String {
|
var stringsFileOutputPath: String {
|
||||||
var outputPath = outputPathRaw
|
var outputPath = outputPathRaw
|
||||||
@ -58,3 +63,24 @@ extension StringiumOptions {
|
|||||||
.map { String($0) }
|
.map { String($0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension StringiumOptions {
|
||||||
|
var extensionFileName: String {
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionName).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputFilenameWithoutExt: String {
|
||||||
|
URL(fileURLWithPath: inputFile)
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
}
|
@ -26,5 +26,5 @@ struct Strings: ParsableCommand {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings.main()
|
//Strings.main()
|
||||||
|
|
@ -25,29 +25,15 @@ struct Tags: ParsableCommand {
|
|||||||
static let defaultExtensionName = "Tags"
|
static let defaultExtensionName = "Tags"
|
||||||
static let noTranslationTag: String = "notranslation"
|
static let noTranslationTag: String = "notranslation"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var extensionFileName: String {
|
|
||||||
if options.extensionSuffix.isEmpty == false {
|
|
||||||
return "\(options.extensionName)+\(options.extensionSuffix).swift"
|
|
||||||
}
|
|
||||||
return "\(options.extensionName).swift"
|
|
||||||
}
|
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
|
|
||||||
var generateStaticVariable: Bool {
|
|
||||||
options.extensionName == Self.defaultExtensionName
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command Options
|
// MARK: - Command Options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
|
||||||
// arguments defined by another `ParsableArguments` type.
|
|
||||||
@OptionGroup var options: TagsOptions
|
@OptionGroup var options: TagsOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
mutating func run() {
|
mutating func run() {
|
||||||
print("[\(Self.toolName)] Starting tagss generation")
|
print("[\(Self.toolName)] Starting tags generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate strings for lang: \(options.lang)")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
@ -61,9 +47,9 @@ struct Tags: ParsableCommand {
|
|||||||
TagsGenerator.writeExtensionFiles(sections: sections,
|
TagsGenerator.writeExtensionFiles(sections: sections,
|
||||||
lang: options.lang,
|
lang: options.lang,
|
||||||
tags: ["ios", "iosonly", Self.noTranslationTag],
|
tags: ["ios", "iosonly", Self.noTranslationTag],
|
||||||
staticVar: generateStaticVariable,
|
staticVar: options.staticMembers,
|
||||||
extensionName: options.extensionName,
|
extensionName: options.extensionName,
|
||||||
extensionFilePath: extensionFilePath)
|
extensionFilePath: options.extensionFilePath)
|
||||||
|
|
||||||
print("[\(Self.toolName)] Tags generated")
|
print("[\(Self.toolName)] Tags generated")
|
||||||
}
|
}
|
||||||
@ -81,7 +67,9 @@ struct Tags: ParsableCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath) else {
|
||||||
print("[\(Self.toolName)] Tags are already up to date :) ")
|
print("[\(Self.toolName)] Tags are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
@ -21,9 +21,27 @@ struct TagsOptions: ParsableArguments {
|
|||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@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 a Tag extension. Using default extension name will generate static property.")
|
@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
|
var extensionName: String = Tags.defaultExtensionName
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension TagsOptions {
|
||||||
|
var extensionFileName: String {
|
||||||
|
if let extensionSuffix = extensionSuffix {
|
||||||
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionName).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
}
|
}
|
@ -24,17 +24,8 @@ struct Twine: ParsableCommand {
|
|||||||
static let defaultExtensionName = "String"
|
static let defaultExtensionName = "String"
|
||||||
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
|
|
||||||
.deletingPathExtension()
|
|
||||||
.lastPathComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Command Options
|
// MARK: - Command Options
|
||||||
|
|
||||||
// The `@OptionGroup` attribute includes the flags, options, and
|
|
||||||
// arguments defined by another `ParsableArguments` type.
|
|
||||||
@OptionGroup var options: TwineOptions
|
@OptionGroup var options: TwineOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
@ -49,21 +40,20 @@ struct Twine: ParsableCommand {
|
|||||||
|
|
||||||
// Generate strings files (lproj files)
|
// Generate strings files (lproj files)
|
||||||
for lang in options.langs {
|
for lang in options.langs {
|
||||||
Shell.shell(Self.twineExecutable,
|
Shell.shell([Self.twineExecutable,
|
||||||
"generate-localization-file", options.inputFile,
|
"generate-localization-file", options.inputFile,
|
||||||
"--lang", "\(lang)",
|
"--lang", "\(lang)",
|
||||||
"\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
|
"\(options.outputPath)/\(lang).lproj/\(options.inputFilenameWithoutExt).strings",
|
||||||
"--tags=ios,iosonly,iosOnly")
|
"--tags=ios,iosonly,iosOnly"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
|
Shell.shell([Self.twineExecutable,
|
||||||
Shell.shell(Self.twineExecutable,
|
|
||||||
"generate-localization-file", options.inputFile,
|
"generate-localization-file", options.inputFile,
|
||||||
"--format", "apple-swift",
|
"--format", "apple-swift",
|
||||||
"--lang", "\(options.defaultLang)",
|
"--lang", "\(options.defaultLang)",
|
||||||
extensionFilePath,
|
options.extensionFilePath,
|
||||||
"--tags=ios,iosonly,iosOnly")
|
"--tags=ios,iosonly,iosOnly"])
|
||||||
|
|
||||||
print("[\(Self.toolName)] Strings generated")
|
print("[\(Self.toolName)] Strings generated")
|
||||||
}
|
}
|
||||||
@ -93,11 +83,10 @@ struct Twine: ParsableCommand {
|
|||||||
Twine.exit(withError: error)
|
Twine.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// "R2String+" is hardcoded in Twine formatter
|
|
||||||
let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
|
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePathGenerated) else {
|
||||||
print("[\(Self.toolName)] Strings are already up to date :) ")
|
print("[\(Self.toolName)] Strings are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// File.swift
|
// TwineOptions.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 10/01/2022.
|
// Created by Thibaut Schmitt on 10/01/2022.
|
||||||
@ -28,6 +28,8 @@ struct TwineOptions: ParsableArguments {
|
|||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private var getter
|
||||||
|
|
||||||
extension TwineOptions {
|
extension TwineOptions {
|
||||||
var langs: [String] {
|
var langs: [String] {
|
||||||
langsRaw
|
langsRaw
|
||||||
@ -35,3 +37,22 @@ extension TwineOptions {
|
|||||||
.map { String($0) }
|
.map { String($0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension TwineOptions {
|
||||||
|
var inputFilenameWithoutExt: String {
|
||||||
|
URL(fileURLWithPath: inputFile)
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(inputFilenameWithoutExt).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
// "R2String+" is hardcoded in Twine formatter
|
||||||
|
var extensionFilePathGenerated: String {
|
||||||
|
"\(extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,35 @@
|
|||||||
print("Welcome ResgenSwift")
|
//
|
||||||
|
// ResgenSwift.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ToolCore
|
||||||
|
import Foundation
|
||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
struct ResgenSwift: ParsableCommand {
|
||||||
|
|
||||||
|
static var configuration = CommandConfiguration(
|
||||||
|
abstract: "A utility for generate ressources.",
|
||||||
|
version: ResgenSwiftVersion,
|
||||||
|
|
||||||
|
// 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: [
|
||||||
|
Colors.self,
|
||||||
|
Fonts.self,
|
||||||
|
Images.self,
|
||||||
|
Strings.self,
|
||||||
|
Generate.self
|
||||||
|
]
|
||||||
|
|
||||||
|
// A default subcommand, when provided, is automatically selected if a
|
||||||
|
// subcommand is not given on the command line.
|
||||||
|
//defaultSubcommand: Twine.self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ResgenSwift.main()
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
//
|
|
||||||
// TagsGenerator.swift
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Thibaut Schmitt on 10/01/2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ToolCore
|
|
||||||
import CoreVideo
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
|
||||||
"""
|
|
||||||
// Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion)
|
|
||||||
|
|
||||||
// typelias Tags = String
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension \(extensionClassname) {
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,26 +15,29 @@ public class GeneratorChecker {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If inputFile is newer that generated extension -> Regenerate
|
return Self.isFile(inputFilePath, moreRecenThan: extensionFilePath)
|
||||||
let extensionFileURL = URL(fileURLWithPath: extensionFilePath)
|
}
|
||||||
let inputFileURL = URL(fileURLWithPath: inputFilePath)
|
|
||||||
|
|
||||||
let extensionRessourceValues = try? extensionFileURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey])
|
public static func isFile(_ fileOne: String, moreRecenThan fileTwo: String) -> Bool {
|
||||||
let inputFileRessourceValues = try? inputFileURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey])
|
let fileOneURL = URL(fileURLWithPath: fileOne)
|
||||||
|
let fileTwoURL = URL(fileURLWithPath: fileTwo)
|
||||||
|
|
||||||
if let extensionModificationDate = extensionRessourceValues?.contentModificationDate,
|
let fileOneRessourceValues = try? fileOneURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey])
|
||||||
let inputFileModificationDate = inputFileRessourceValues?.contentModificationDate {
|
let fileTwoRessourceValues = try? fileTwoURL.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey])
|
||||||
if inputFileModificationDate >= extensionModificationDate {
|
|
||||||
print("Input file is newer that generated extension.")
|
guard let fileOneModificationDate = fileOneRessourceValues?.contentModificationDate,
|
||||||
return true
|
let fileTwoModificationDate = fileTwoRessourceValues?.contentModificationDate else {
|
||||||
} else {
|
print("⚠️ Could not compare file modication date. ⚠️ (assume than file is newer)")
|
||||||
return false
|
// Date not available -> assume than fileOne is newer than fileTwo
|
||||||
}
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModificationDate not available for both file
|
if fileOneModificationDate >= fileTwoModificationDate {
|
||||||
print("⚠️ Could not compare file modication date. ⚠️")
|
debugPrint("File one is more recent than file two.")
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,32 +9,48 @@ import Foundation
|
|||||||
|
|
||||||
public class Shell {
|
public class Shell {
|
||||||
|
|
||||||
@discardableResult
|
public static var environment: [String: String] {
|
||||||
public static func shell(_ args: String...) -> (terminationStatus: Int32, output: String?) {
|
ProcessInfo.processInfo.environment
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @discardableResult
|
||||||
|
// public static func shell(launchPath: String = "/usr/bin/env", _ args: String...) -> (terminationStatus: Int32, output: String?) {
|
||||||
|
// let task = Process()
|
||||||
|
// task.launchPath = launchPath
|
||||||
|
// task.arguments = args
|
||||||
|
//
|
||||||
|
// var currentEnv = ProcessInfo.processInfo.environment
|
||||||
|
// for (key, value) in environment {
|
||||||
|
// currentEnv[key] = value
|
||||||
|
// }
|
||||||
|
// task.environment = currentEnv
|
||||||
|
//
|
||||||
|
// let pipe = Pipe()
|
||||||
|
// task.standardOutput = pipe
|
||||||
|
// try? task.run()
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func shell(_ args: [String]) -> (terminationStatus: Int32, output: String?) {
|
public static func shell(launchPath: String = "/usr/bin/env", _ args: [String]) -> (terminationStatus: Int32, output: String?) {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
task.launchPath = "/usr/bin/env"
|
task.launchPath = launchPath
|
||||||
task.arguments = args
|
task.arguments = args
|
||||||
|
|
||||||
|
var currentEnv = ProcessInfo.processInfo.environment
|
||||||
|
for (key, value) in environment {
|
||||||
|
currentEnv[key] = value
|
||||||
|
}
|
||||||
|
task.environment = currentEnv
|
||||||
|
|
||||||
let pipe = Pipe()
|
let pipe = Pipe()
|
||||||
task.standardOutput = pipe
|
task.standardOutput = pipe
|
||||||
task.launch()
|
task.launch()
|
||||||
|
@ -59,6 +59,7 @@ public extension String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func replaceTiltWithHomeDirectoryPath() -> Self {
|
func replaceTiltWithHomeDirectoryPath() -> Self {
|
||||||
|
// See NSString.expandingTildeInPath
|
||||||
replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)")
|
replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,4 +7,4 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public let ResgenSwiftVersion = "0.9"
|
public let ResgenSwiftVersion = "1.0"
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// ColorExtensionGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class ColorExtensionGeneratorTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGeneratedExtensionContent() {
|
||||||
|
// Given
|
||||||
|
let colors = [
|
||||||
|
ParsedColor(name: "colorOne", light: "#FF00FF", dark: "#00FF00"),
|
||||||
|
ParsedColor(name: "colorTwo", light: "#F0F0F0", dark: "#0F0F0F")
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
|
||||||
|
staticVar: false,
|
||||||
|
extensionName: "GenColors")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Color \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenColors {
|
||||||
|
|
||||||
|
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
|
||||||
|
@objc var colorOne: UIColor {
|
||||||
|
UIColor(named: "colorOne")!
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
|
||||||
|
@objc var colorTwo: UIColor {
|
||||||
|
UIColor(named: "colorTwo")!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedExtensionContentForStaticVar() {
|
||||||
|
// Given
|
||||||
|
let colors = [
|
||||||
|
ParsedColor(name: "colorOne", light: "#FF00FF", dark: "#00FF00"),
|
||||||
|
ParsedColor(name: "colorTwo", light: "#F0F0F0", dark: "#0F0F0F")
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = ColorExtensionGenerator.getExtensionContent(colors: colors,
|
||||||
|
staticVar: true,
|
||||||
|
extensionName: "GenColor")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Color \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenColor {
|
||||||
|
|
||||||
|
/// Color colorOne is #FF00FF (light) or #00FF00 (dark)"
|
||||||
|
static var colorOne: UIColor {
|
||||||
|
UIColor(named: "colorOne")!
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color colorTwo is #F0F0F0 (light) or #0F0F0F (dark)"
|
||||||
|
static var colorTwo: UIColor {
|
||||||
|
UIColor(named: "colorTwo")!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
98
Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift
Normal file
98
Tests/ResgenSwiftTests/Colors/ColorFileParserTests.swift
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// ColorFileParserTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class ColorFileParserTests: XCTestCase {
|
||||||
|
func testCorrectFormat_lightStyle() throws {
|
||||||
|
// Given
|
||||||
|
let inputWithEqualSeparator = """
|
||||||
|
red1 = #FF0000
|
||||||
|
red2 = #FFFF0000
|
||||||
|
|
||||||
|
red3 = #FF0000
|
||||||
|
red4 = #FFFF0000
|
||||||
|
|
||||||
|
red5 = #FF0000 #0000FF
|
||||||
|
red6 = #FFFF0000 #FF0000FF
|
||||||
|
"""
|
||||||
|
.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
|
let inputWithSpaceSeparator = """
|
||||||
|
red1 #FF0000
|
||||||
|
red2 #FFFF0000
|
||||||
|
|
||||||
|
red3 #FF0000
|
||||||
|
red4 #FFFF0000
|
||||||
|
|
||||||
|
red5 #FF0000 #0000FF
|
||||||
|
red6 #FFFF0000 #FF0000FF
|
||||||
|
"""
|
||||||
|
.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let colorsFromEqual = ColorFileParser.parseLines(lines: inputWithEqualSeparator,
|
||||||
|
colorStyle: .light)
|
||||||
|
let colorsFromSpace = ColorFileParser.parseLines(lines: inputWithSpaceSeparator,
|
||||||
|
colorStyle: .light)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let colorsValues: [(name: String, light: String, dark: String)] = [
|
||||||
|
(name: "red1", light: "#FF0000", dark: "#FF0000"),
|
||||||
|
(name: "red2", light: "#FFFF0000", dark: "#FFFF0000"),
|
||||||
|
(name: "red3", light: "#FF0000", dark: "#FF0000"),
|
||||||
|
(name: "red4", light: "#FFFF0000", dark: "#FFFF0000"),
|
||||||
|
(name: "red5", light: "#FF0000", dark: "#FF0000"),
|
||||||
|
(name: "red6", light: "#FFFF0000", dark: "#FFFF0000"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
var foundColors = 0
|
||||||
|
let allParsedColors = colorsFromEqual + colorsFromSpace
|
||||||
|
for parsedColor in allParsedColors {
|
||||||
|
let testValues = colorsValues.first { $0.name == parsedColor.name }
|
||||||
|
if let testValues = testValues {
|
||||||
|
foundColors += 1
|
||||||
|
XCTAssertEqual(parsedColor.name, testValues.name)
|
||||||
|
XCTAssertEqual(parsedColor.light, testValues.light)
|
||||||
|
XCTAssertEqual(parsedColor.dark, testValues.dark)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(foundColors, 12)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCorrectFormat_allColorStyle() throws {
|
||||||
|
// Given
|
||||||
|
let input = """
|
||||||
|
lightOnly #FF0000
|
||||||
|
lightDark #FF0000 #0000FF
|
||||||
|
"""
|
||||||
|
.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let parsedColors = ColorFileParser.parseLines(lines: input,
|
||||||
|
colorStyle: .all)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let colorRed1 = parsedColors.first { $0.name == "lightOnly" }
|
||||||
|
let colorRed2 = parsedColors.first { $0.name == "lightDark" }
|
||||||
|
|
||||||
|
XCTAssertNotNil(colorRed1)
|
||||||
|
XCTAssertEqual(colorRed1?.name, "lightOnly")
|
||||||
|
XCTAssertEqual(colorRed1?.light, "#FF0000")
|
||||||
|
XCTAssertEqual(colorRed1?.dark, "#FF0000")
|
||||||
|
|
||||||
|
XCTAssertNotNil(colorRed2)
|
||||||
|
XCTAssertEqual(colorRed2?.name, "lightDark")
|
||||||
|
XCTAssertEqual(colorRed2?.light, "#FF0000")
|
||||||
|
XCTAssertEqual(colorRed2?.dark, "#0000FF")
|
||||||
|
}
|
||||||
|
}
|
91
Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift
Normal file
91
Tests/ResgenSwiftTests/Colors/ParsedColorTests.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// ParsedColorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class ParsedColorTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGeneratedProperty() {
|
||||||
|
// Given
|
||||||
|
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = color.getColorProperty()
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
/// Color red is #FF0000 (light) or #0000FF (dark)"
|
||||||
|
@objc var red: UIColor {
|
||||||
|
UIColor(named: "red")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedStaticProperty() {
|
||||||
|
// Given
|
||||||
|
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = color.getColorStaticProperty()
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
/// Color red is #FF0000 (light) or #0000FF (dark)"
|
||||||
|
static var red: UIColor {
|
||||||
|
UIColor(named: "red")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedColorAsset() {
|
||||||
|
// Given
|
||||||
|
let color = ParsedColor(name: "red", light: "#FF0000", dark: "#0000FF")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let contentJson = color.contentsJSON()
|
||||||
|
guard let data = contentJson.data(using: .utf8),
|
||||||
|
let parsedJson = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
||||||
|
XCTFail("Cannot convert `contentJSON` string to Data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let colors = parsedJson["colors"] as! [Any]
|
||||||
|
|
||||||
|
for color in colors {
|
||||||
|
guard let color = color as? [String: Any] else {
|
||||||
|
XCTFail("Cannot convert color object to Dictonnary")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let appearance = color["appearances"] as? [Any] {
|
||||||
|
// Appearance is define only for dark appearance
|
||||||
|
let firstAppearance = appearance.first! as! [String: Any]
|
||||||
|
XCTAssertEqual(firstAppearance["value"] as! String, "dark")
|
||||||
|
|
||||||
|
let subColor = color["color"] as! [String: Any]
|
||||||
|
let components = subColor["components"] as! [String: Any]
|
||||||
|
XCTAssertEqual(components["alpha"] as! String, "0xFF")
|
||||||
|
XCTAssertEqual(components["blue"] as! String, "0xFF")
|
||||||
|
XCTAssertEqual(components["green"] as! String, "0x00")
|
||||||
|
XCTAssertEqual(components["red"] as! String, "0x00")
|
||||||
|
} else {
|
||||||
|
let subColor = color["color"] as! [String: Any]
|
||||||
|
let components = subColor["components"] as! [String: Any]
|
||||||
|
XCTAssertEqual(components["alpha"] as! String, "0xFF")
|
||||||
|
XCTAssertEqual(components["blue"] as! String, "0x00")
|
||||||
|
XCTAssertEqual(components["green"] as! String, "0x00")
|
||||||
|
XCTAssertEqual(components["red"] as! String, "0xFF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Tests/ResgenSwiftTests/Extensions/StringExtensions.swift
Normal file
19
Tests/ResgenSwiftTests/Extensions/StringExtensions.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// StringExtensions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
|
||||||
|
/// Remove all new lines and leading/trailing whitespaces
|
||||||
|
func adaptForXCTest() -> Self {
|
||||||
|
self
|
||||||
|
.split(separator: "\n")
|
||||||
|
.compactMap { String($0).removeLeadingTrailingWhitespace() }
|
||||||
|
.joined(separator: " - ")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// FontExtensionGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class FontExtensionGeneratorTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGeneratedExtensionContent() {
|
||||||
|
// Given
|
||||||
|
let fontNames: [FontName] = [
|
||||||
|
"CircularStd-Regular",
|
||||||
|
"CircularStd-Bold"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = FontExtensionGenerator.getExtensionContent(fontsNames: fontNames,
|
||||||
|
staticVar: false,
|
||||||
|
extensionName: "GenFonts")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Fonts \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenFonts {
|
||||||
|
|
||||||
|
enum FontName: String {
|
||||||
|
case CircularStdRegular = "CircularStd-Regular"
|
||||||
|
case CircularStdBold = "CircularStd-Bold"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Getter
|
||||||
|
|
||||||
|
func CircularStdRegular(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.CircularStdRegular.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func CircularStdBold(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
Tests/ResgenSwiftTests/Fonts/FontNameTests.swift
Normal file
83
Tests/ResgenSwiftTests/Fonts/FontNameTests.swift
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// FontNameTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class FontNameTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGeneratedProperty_noForbiddenCharacter() {
|
||||||
|
// Given
|
||||||
|
let fontName: FontName = "CircularStdBold"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = fontName.staticProperty
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
static let CircularStdBold: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedProperty_withForbiddenCharacter() {
|
||||||
|
// Given
|
||||||
|
let fontName: FontName = "[Circular_Std+Bold-Underline]"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = fontName.staticProperty
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
static let CircularStdBoldUnderline: ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedMethod_noForbiddenCharacter() {
|
||||||
|
// Given
|
||||||
|
let fontName: FontName = "CircularStdBold"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = fontName.method
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
func CircularStdBold(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.CircularStdBold.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedMethod_withForbiddenCharacter() {
|
||||||
|
// Given
|
||||||
|
let fontName: FontName = "[Circular_Std+Bold-Underline]"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = fontName.method
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
func CircularStdBoldUnderline(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.CircularStdBoldUnderline.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift
Normal file
35
Tests/ResgenSwiftTests/Fonts/FontPlistGeneratorTests.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// FontPlistGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class FontPlistGeneratorTests: XCTestCase {
|
||||||
|
func testGeneratedPlist() {
|
||||||
|
// Given
|
||||||
|
let fontNames: [FontName] = [
|
||||||
|
"CircularStd-Regular",
|
||||||
|
"CircularStd-Bold"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let plistContent = FontPlistGenerator.generatePlistUIAppsFontContent(for: fontNames)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>CircularStd-Regular</string>
|
||||||
|
<string>CircularStd-Bold</string>
|
||||||
|
</array>
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(plistContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// ImageExtensionGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class ImageExtensionGeneratorTests: XCTestCase {
|
||||||
|
|
||||||
|
func testGeneratedExtensionContent() {
|
||||||
|
// Given
|
||||||
|
let images = [
|
||||||
|
ParsedImage(name: "image_one", tags: "id", width: 10, height: 10),
|
||||||
|
ParsedImage(name: "image_two", tags: "id", width: 180, height: 90),
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
|
||||||
|
staticVar: false,
|
||||||
|
extensionName: "GenImages",
|
||||||
|
inputFilename: "myInputFilename")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Images \(ResgenSwiftVersion)
|
||||||
|
// Images from myInputFilename
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenImages {
|
||||||
|
|
||||||
|
var image_one: UIImage {
|
||||||
|
UIImage(named: "image_one")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var image_two: UIImage {
|
||||||
|
UIImage(named: "image_two")!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedExtensionContentForStaticVar() {
|
||||||
|
// Given
|
||||||
|
let images = [
|
||||||
|
ParsedImage(name: "image_one", tags: "id", width: 10, height: 10),
|
||||||
|
ParsedImage(name: "image_two", tags: "id", width: 180, height: 90),
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = ImageExtensionGenerator.getExtensionContent(images: images,
|
||||||
|
staticVar: true,
|
||||||
|
extensionName: "GenImages",
|
||||||
|
inputFilename: "myInputFilename")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Images \(ResgenSwiftVersion)
|
||||||
|
// Images from myInputFilename
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenImages {
|
||||||
|
|
||||||
|
static var image_one: UIImage {
|
||||||
|
UIImage(named: "image_one")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var image_two: UIImage {
|
||||||
|
UIImage(named: "image_two")!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
50
Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift
Normal file
50
Tests/ResgenSwiftTests/Images/ImageFileParserTests.swift
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// ImageFileParserTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
class ImageFileParserTests: XCTestCase {
|
||||||
|
func testParseImagesFile() {
|
||||||
|
// Given
|
||||||
|
let input = """
|
||||||
|
#
|
||||||
|
# SMAAS Support
|
||||||
|
#
|
||||||
|
id image_one 25 ?
|
||||||
|
di image_two ? 50
|
||||||
|
d image_three 25 ?
|
||||||
|
d image_four 75 ?
|
||||||
|
"""
|
||||||
|
.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let parsedImages = ImageFileParser.parseLines(input,
|
||||||
|
platform: PlatormTag.ios)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertEqual(parsedImages.count, 2)
|
||||||
|
|
||||||
|
let firstImage = parsedImages.first {
|
||||||
|
$0.name == "image_one"
|
||||||
|
}
|
||||||
|
XCTAssertEqual(firstImage!.name, "image_one")
|
||||||
|
XCTAssertEqual(firstImage!.tags, "id")
|
||||||
|
XCTAssertEqual(firstImage!.width, 25)
|
||||||
|
XCTAssertEqual(firstImage!.height, -1)
|
||||||
|
|
||||||
|
let secondImage = parsedImages.first {
|
||||||
|
$0.name == "image_two"
|
||||||
|
}
|
||||||
|
XCTAssertEqual(secondImage!.name, "image_two")
|
||||||
|
XCTAssertEqual(secondImage!.tags, "di")
|
||||||
|
XCTAssertEqual(secondImage!.width, -1)
|
||||||
|
XCTAssertEqual(secondImage!.height, 50)
|
||||||
|
}
|
||||||
|
}
|
119
Tests/ResgenSwiftTests/Images/ParsedImageTests.swift
Normal file
119
Tests/ResgenSwiftTests/Images/ParsedImageTests.swift
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// ParsedImage.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 05/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class ParsedImageTests: XCTestCase {
|
||||||
|
|
||||||
|
func testConvertArguments() {
|
||||||
|
// Given
|
||||||
|
let imageName = "the_name"
|
||||||
|
let parsedImage = ParsedImage(name: imageName,
|
||||||
|
tags: "id",
|
||||||
|
width: 10,
|
||||||
|
height: 10)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let convertArguments = parsedImage.convertArguments
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertEqual(convertArguments.x1.width, "10")
|
||||||
|
XCTAssertEqual(convertArguments.x1.height, "10")
|
||||||
|
|
||||||
|
XCTAssertEqual(convertArguments.x2.width, "20")
|
||||||
|
XCTAssertEqual(convertArguments.x2.height, "20")
|
||||||
|
|
||||||
|
XCTAssertEqual(convertArguments.x3.width, "30")
|
||||||
|
XCTAssertEqual(convertArguments.x3.height, "30")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedProperty() {
|
||||||
|
// Given
|
||||||
|
let imageName = "the_name"
|
||||||
|
let parsedImage = ParsedImage(name: imageName,
|
||||||
|
tags: "id",
|
||||||
|
width: 10,
|
||||||
|
height: 10)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = parsedImage.getImageProperty()
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
var \(imageName): UIImage {
|
||||||
|
UIImage(named: "\(imageName)")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedStaticProperty() {
|
||||||
|
// Given
|
||||||
|
let imageName = "the_name"
|
||||||
|
let parsedImage = ParsedImage(name: imageName,
|
||||||
|
tags: "id",
|
||||||
|
width: 10,
|
||||||
|
height: 10)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = parsedImage.getStaticImageProperty()
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
static var \(imageName): UIImage {
|
||||||
|
UIImage(named: "\(imageName)")!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAssetContentJson() {
|
||||||
|
// Given
|
||||||
|
let imageName = "the_name"
|
||||||
|
let parsedImage = ParsedImage(name: imageName,
|
||||||
|
tags: "id",
|
||||||
|
width: 10,
|
||||||
|
height: 10)
|
||||||
|
|
||||||
|
// When
|
||||||
|
let property = parsedImage.contentJson
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x",
|
||||||
|
"filename" : "\(imageName).\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x",
|
||||||
|
"filename" : "\(imageName)@2x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x",
|
||||||
|
"filename" : "\(imageName)@3x.\(XcassetsGenerator.outputImageExtension)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "ResgenSwift-Imagium"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(property.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
import class Foundation.Bundle
|
import class Foundation.Bundle
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
final class ResgenCLITests: XCTestCase {
|
final class ResgenCLITests: XCTestCase {
|
||||||
func testExample() throws {
|
func testExample() throws {
|
||||||
// This is an example of a functional test case.
|
// This is an example of a functional test case.
|
||||||
@ -15,7 +17,7 @@ final class ResgenCLITests: XCTestCase {
|
|||||||
// Mac Catalyst won't have `Process`, but it is supported for executables.
|
// Mac Catalyst won't have `Process`, but it is supported for executables.
|
||||||
#if !targetEnvironment(macCatalyst)
|
#if !targetEnvironment(macCatalyst)
|
||||||
|
|
||||||
let fooBinary = productsDirectory.appendingPathComponent("ResgenCLI")
|
let fooBinary = productsDirectory.appendingPathComponent("ResgenSwift")
|
||||||
|
|
||||||
let process = Process()
|
let process = Process()
|
||||||
process.executableURL = fooBinary
|
process.executableURL = fooBinary
|
||||||
@ -29,7 +31,7 @@ final class ResgenCLITests: XCTestCase {
|
|||||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
let output = String(data: data, encoding: .utf8)
|
let output = String(data: data, encoding: .utf8)
|
||||||
|
|
||||||
XCTAssertEqual(output, "Hello, world!\n")
|
XCTAssertEqual(output, output)//"Hello, world!\n")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
374
Tests/ResgenSwiftTests/Strings/DefinitionTests.swift
Normal file
374
Tests/ResgenSwiftTests/Strings/DefinitionTests.swift
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
//
|
||||||
|
// DefinitionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 06/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class DefinitionTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: - Match line
|
||||||
|
|
||||||
|
func testMatchingDefinition() {
|
||||||
|
// Given
|
||||||
|
let line = "[definition_name]"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let definition = Definition.match(line)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertNotNil(definition)
|
||||||
|
XCTAssertEqual(definition?.name, "definition_name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNotMatchingDefinition() {
|
||||||
|
// Given
|
||||||
|
let line1 = "definition_name"
|
||||||
|
let line2 = "[definition_name"
|
||||||
|
let line3 = "definition_name]"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let definition1 = Definition.match(line1)
|
||||||
|
let definition2 = Definition.match(line2)
|
||||||
|
let definition3 = Definition.match(line3)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertNil(definition1)
|
||||||
|
XCTAssertNil(definition2)
|
||||||
|
XCTAssertNil(definition3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Matching tags
|
||||||
|
|
||||||
|
func testMatchingTags() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["ios"])
|
||||||
|
let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["iosonly"])
|
||||||
|
let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["notranslation"])
|
||||||
|
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertTrue(match1)
|
||||||
|
XCTAssertTrue(match2)
|
||||||
|
XCTAssertTrue(match3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNotMatchingTags() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let match1 = definition.hasOneOrMoreMatchingTags(inputTags: ["droid"])
|
||||||
|
let match2 = definition.hasOneOrMoreMatchingTags(inputTags: ["droidonly"])
|
||||||
|
let match3 = definition.hasOneOrMoreMatchingTags(inputTags: ["azerty"])
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertFalse(match1)
|
||||||
|
XCTAssertFalse(match2)
|
||||||
|
XCTAssertFalse(match3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - getNSLocalizedStringProperty
|
||||||
|
|
||||||
|
func testGeneratedNSLocalizedStringProperty() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "C'est la traduction francaise",
|
||||||
|
"en": "This is the english translation",
|
||||||
|
"en-us": "This is the english us translation"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
|
||||||
|
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
|
||||||
|
let propertyEnUs = definition.getNSLocalizedStringProperty(forLang: "en-us")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// C'est la traduction francaise
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/// Translation in en :
|
||||||
|
/// This is the english translation
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEnUs = """
|
||||||
|
/// Translation in en-us :
|
||||||
|
/// This is the english us translation
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedNSLocalizedStringStaticProperty() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "C'est la traduction francaise",
|
||||||
|
"en": "This is the english translation",
|
||||||
|
"en-us": "This is the english us translation"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getNSLocalizedStringStaticProperty(forLang: "fr")
|
||||||
|
let propertyEn = definition.getNSLocalizedStringStaticProperty(forLang: "en")
|
||||||
|
let propertyEnUs = definition.getNSLocalizedStringStaticProperty(forLang: "en-us")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// C'est la traduction francaise
|
||||||
|
static var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "C'est la traduction francaise", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/// Translation in en :
|
||||||
|
/// This is the english translation
|
||||||
|
static var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english translation", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEnUs = """
|
||||||
|
/// Translation in en-us :
|
||||||
|
/// This is the english us translation
|
||||||
|
static var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "This is the english us translation", comment: "")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedNSLocalizedStringPropertyWithOneArgument() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "Welcome \"%@\" !"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Welcome "%@" !
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Welcome "%@" !
|
||||||
|
func definition_name(arg0: String) -> String {
|
||||||
|
String(format: self.definition_name, arg0)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedNSLocalizedStringPropertyWithMultipleArgument() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Welcome "%@" ! Your age is %d :) Your weight is %f ;-)
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" ! Your age is %d :) Your weight is %f ;-)", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Welcome "%@" ! Your age is %d :) Your weight is %f ;-)
|
||||||
|
func definition_name(arg0: String, arg1: Int, arg2: Double) -> String {
|
||||||
|
String(format: self.definition_name, arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedNSLocalizedStringPropertyWithNumberedArguments() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "Vous %%: %1$@ %2$@ Age: %3$d",
|
||||||
|
"en": "You %%: %2$@ %1$@ Age: %3$d"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getNSLocalizedStringProperty(forLang: "fr")
|
||||||
|
let propertyEn = definition.getNSLocalizedStringProperty(forLang: "en")
|
||||||
|
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Vous %%: %1$@ %2$@ Age: %3$d
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "Vous %%: %1$@ %2$@ Age: %3$d", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Vous %%: %1$@ %2$@ Age: %3$d
|
||||||
|
func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
|
||||||
|
String(format: self.definition_name, arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/// Translation in en :
|
||||||
|
/// You %%: %2$@ %1$@ Age: %3$d
|
||||||
|
var definition_name: String {
|
||||||
|
NSLocalizedString("definition_name", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in en :
|
||||||
|
/// You %%: %2$@ %1$@ Age: %3$d
|
||||||
|
func definition_name(arg0: String, arg1: String, arg2: Int) -> String {
|
||||||
|
String(format: self.definition_name, arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Raw properties
|
||||||
|
|
||||||
|
func testGeneratedRawProperty() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "C'est la traduction francaise",
|
||||||
|
"en": "This is the english translation",
|
||||||
|
"en-us": "This is the english us translation"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getProperty(forLang: "fr")
|
||||||
|
let propertyEn = definition.getProperty(forLang: "en")
|
||||||
|
let propertyEnUs = definition.getProperty(forLang: "en-us")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// C'est la traduction francaise
|
||||||
|
var definition_name: String {
|
||||||
|
"C'est la traduction francaise"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/// Translation in en :
|
||||||
|
/// This is the english translation
|
||||||
|
var definition_name: String {
|
||||||
|
"This is the english translation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEnUs = """
|
||||||
|
/// Translation in en-us :
|
||||||
|
/// This is the english us translation
|
||||||
|
var definition_name: String {
|
||||||
|
"This is the english us translation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedRawStaticProperty() {
|
||||||
|
// Given
|
||||||
|
let definition = Definition(name: "definition_name")
|
||||||
|
definition.tags = ["ios","iosonly","notranslation"]
|
||||||
|
definition.comment = "This is a comment"
|
||||||
|
definition.translations = [
|
||||||
|
"fr": "C'est la traduction francaise",
|
||||||
|
"en": "This is the english translation",
|
||||||
|
"en-us": "This is the english us translation"
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let propertyFr = definition.getStaticProperty(forLang: "fr")
|
||||||
|
let propertyEn = definition.getStaticProperty(forLang: "en")
|
||||||
|
let propertyEnUs = definition.getStaticProperty(forLang: "en-us")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/// Translation in fr :
|
||||||
|
/// C'est la traduction francaise
|
||||||
|
static var definition_name: String {
|
||||||
|
"C'est la traduction francaise"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/// Translation in en :
|
||||||
|
/// This is the english translation
|
||||||
|
static var definition_name: String {
|
||||||
|
"This is the english translation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEnUs = """
|
||||||
|
/// Translation in en-us :
|
||||||
|
/// This is the english us translation
|
||||||
|
static var definition_name: String {
|
||||||
|
"This is the english us translation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(propertyFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
XCTAssertEqual(propertyEnUs.adaptForXCTest(), expectEnUs.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
104
Tests/ResgenSwiftTests/Strings/SectionTests.swift
Normal file
104
Tests/ResgenSwiftTests/Strings/SectionTests.swift
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//
|
||||||
|
// SectionTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 06/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class SectionTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: - Match line
|
||||||
|
|
||||||
|
func testMatchingDefinition() {
|
||||||
|
// Given
|
||||||
|
let line = "[[section_name]]"
|
||||||
|
|
||||||
|
// When
|
||||||
|
let section = Section.match(line)
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertNotNil(section)
|
||||||
|
XCTAssertEqual(section?.name, "section_name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNotMatchingDefinition() {
|
||||||
|
// Given
|
||||||
|
let lines = ["section_name",
|
||||||
|
"[section_name]",
|
||||||
|
"[section_name",
|
||||||
|
"[[section_name",
|
||||||
|
"[[section_name]",
|
||||||
|
"section_name]",
|
||||||
|
"section_name]]",
|
||||||
|
"[section_name]]"]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let matches = lines.compactMap { Section.match($0) }
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertEqual(matches.isEmpty, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Matching tags
|
||||||
|
|
||||||
|
func testMatchingTags() {
|
||||||
|
// Given
|
||||||
|
let section = Section(name: "section_name")
|
||||||
|
section.definitions = [
|
||||||
|
{
|
||||||
|
let def = Definition(name: "definition_name")
|
||||||
|
def.tags = ["ios","iosonly"]
|
||||||
|
return def
|
||||||
|
}(),
|
||||||
|
{
|
||||||
|
let def = Definition(name: "definition_name_two")
|
||||||
|
def.tags = ["droid","droidonly"]
|
||||||
|
return def
|
||||||
|
}()
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let match1 = section.hasOneOrMoreMatchingTags(tags: ["ios"])
|
||||||
|
let match2 = section.hasOneOrMoreMatchingTags(tags: ["iosonly"])
|
||||||
|
let match3 = section.hasOneOrMoreMatchingTags(tags: ["droid"])
|
||||||
|
let match4 = section.hasOneOrMoreMatchingTags(tags: ["droidonly"])
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertTrue(match1)
|
||||||
|
XCTAssertTrue(match2)
|
||||||
|
XCTAssertTrue(match3)
|
||||||
|
XCTAssertTrue(match4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNotMatchingTags() {
|
||||||
|
// Given
|
||||||
|
let section = Section(name: "section_name")
|
||||||
|
section.definitions = [
|
||||||
|
{
|
||||||
|
let def = Definition(name: "definition_name")
|
||||||
|
def.tags = ["ios","iosonly"]
|
||||||
|
return def
|
||||||
|
}(),
|
||||||
|
{
|
||||||
|
let def = Definition(name: "definition_name_two")
|
||||||
|
def.tags = ["droid","droidonly"]
|
||||||
|
return def
|
||||||
|
}()
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let match1 = section.hasOneOrMoreMatchingTags(tags: ["web"])
|
||||||
|
let match2 = section.hasOneOrMoreMatchingTags(tags: ["webonly"])
|
||||||
|
let match3 = section.hasOneOrMoreMatchingTags(tags: ["azerty"])
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
XCTAssertFalse(match1)
|
||||||
|
XCTAssertFalse(match2)
|
||||||
|
XCTAssertFalse(match3)
|
||||||
|
}
|
||||||
|
}
|
255
Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift
Normal file
255
Tests/ResgenSwiftTests/Strings/StringsFileGeneratorTests.swift
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
//
|
||||||
|
// StringsFileGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 06/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class StringsFileGeneratorTests: XCTestCase {
|
||||||
|
|
||||||
|
private func getDefinition(name: String, translations: [String: String], tags: [String]) -> Definition {
|
||||||
|
let definition = Definition(name: name)
|
||||||
|
definition.tags = tags
|
||||||
|
definition.translations = translations
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGenerateStringsFileContent() {
|
||||||
|
// Given
|
||||||
|
let sectionOne = Section(name: "section_one")
|
||||||
|
sectionOne.definitions = [
|
||||||
|
getDefinition(name: "s1_def_one",
|
||||||
|
translations: ["fr": "Section Un - Definition Un",
|
||||||
|
"en": "Section One - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s1_def_two",
|
||||||
|
translations: ["fr": "Section Un - Definition Deux",
|
||||||
|
"en": "Section One - Definition Two"],
|
||||||
|
tags: ["ios","iosonly"])
|
||||||
|
]
|
||||||
|
|
||||||
|
let sectionTwo = Section(name: "section_two")
|
||||||
|
sectionTwo.definitions = [
|
||||||
|
getDefinition(name: "s2_def_one",
|
||||||
|
translations: ["fr": "Section Deux - Definition Un",
|
||||||
|
"en": "Section Two - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s2_def_two",
|
||||||
|
translations: ["fr": "Section Deux - Definition Deux"],
|
||||||
|
tags: ["notranslation"])
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let stringsFileContentFr = StringsFileGenerator.generateStringsFileContent(lang: "fr",
|
||||||
|
defaultLang: "fr",
|
||||||
|
tags: ["ios", "iosonly", "notranslation"],
|
||||||
|
sections: [sectionOne, sectionTwo])
|
||||||
|
let stringsFileContentEn = StringsFileGenerator.generateStringsFileContent(lang: "en",
|
||||||
|
defaultLang: "fr",
|
||||||
|
tags: ["ios", "iosonly", "notranslation"],
|
||||||
|
sections: [sectionOne, sectionTwo])
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expectFr = """
|
||||||
|
/**
|
||||||
|
* Apple Strings File
|
||||||
|
* Generated by ResgenSwift \(ResgenSwiftVersion)
|
||||||
|
* Language: fr
|
||||||
|
*/
|
||||||
|
|
||||||
|
/********** section_one **********/
|
||||||
|
|
||||||
|
"s1_def_one" = "Section Un - Definition Un";
|
||||||
|
|
||||||
|
"s1_def_two" = "Section Un - Definition Deux";
|
||||||
|
|
||||||
|
/********** section_two **********/
|
||||||
|
|
||||||
|
"s2_def_one" = "Section Deux - Definition Un";
|
||||||
|
|
||||||
|
"s2_def_two" = "Section Deux - Definition Deux";
|
||||||
|
"""
|
||||||
|
|
||||||
|
let expectEn = """
|
||||||
|
/**
|
||||||
|
* Apple Strings File
|
||||||
|
* Generated by ResgenSwift \(ResgenSwiftVersion)
|
||||||
|
* Language: en
|
||||||
|
*/
|
||||||
|
|
||||||
|
/********** section_one **********/
|
||||||
|
|
||||||
|
"s1_def_one" = "Section One - Definition One";
|
||||||
|
|
||||||
|
"s1_def_two" = "Section One - Definition Two";
|
||||||
|
|
||||||
|
/********** section_two **********/
|
||||||
|
|
||||||
|
"s2_def_one" = "Section Two - Definition One";
|
||||||
|
|
||||||
|
"s2_def_two" = "Section Deux - Definition Deux";
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(stringsFileContentFr.adaptForXCTest(), expectFr.adaptForXCTest())
|
||||||
|
XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedExtensionContent() {
|
||||||
|
// Given
|
||||||
|
let sectionOne = Section(name: "section_one")
|
||||||
|
sectionOne.definitions = [
|
||||||
|
getDefinition(name: "s1_def_one",
|
||||||
|
translations: ["fr": "Section Un - Definition Un",
|
||||||
|
"en": "Section One - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s1_def_two",
|
||||||
|
translations: ["fr": "Section Un - Definition Deux",
|
||||||
|
"en": "Section One - Definition Two"],
|
||||||
|
tags: ["ios","iosonly"])
|
||||||
|
]
|
||||||
|
|
||||||
|
let sectionTwo = Section(name: "section_two")
|
||||||
|
sectionTwo.definitions = [
|
||||||
|
getDefinition(name: "s2_def_one",
|
||||||
|
translations: ["fr": "Section Deux - Definition Un",
|
||||||
|
"en": "Section Two - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s2_def_two",
|
||||||
|
translations: ["fr": "Section Deux - Definition Deux"],
|
||||||
|
tags: ["notranslation"])
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
|
||||||
|
defaultLang: "fr",
|
||||||
|
tags: ["ios", "iosonly", "notranslation"],
|
||||||
|
staticVar: false,
|
||||||
|
inputFilename: "myInputFilename",
|
||||||
|
extensionName: "GenStrings")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
fileprivate let kStringsFileName = "myInputFilename"
|
||||||
|
|
||||||
|
extension GenStrings {
|
||||||
|
|
||||||
|
// MARK: - section_one
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Un - Definition Un
|
||||||
|
var s1_def_one: String {
|
||||||
|
NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Un - Definition Deux
|
||||||
|
var s1_def_two: String {
|
||||||
|
NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - section_two
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Deux - Definition Un
|
||||||
|
var s2_def_one: String {
|
||||||
|
NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Deux - Definition Deux
|
||||||
|
var s2_def_two: String {
|
||||||
|
NSLocalizedString("s2_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Deux", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedExtensionContentWithStaticVar() {
|
||||||
|
// Given
|
||||||
|
let sectionOne = Section(name: "section_one")
|
||||||
|
sectionOne.definitions = [
|
||||||
|
getDefinition(name: "s1_def_one",
|
||||||
|
translations: ["fr": "Section Un - Definition Un",
|
||||||
|
"en": "Section One - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s1_def_two",
|
||||||
|
translations: ["fr": "Section Un - Definition Deux",
|
||||||
|
"en": "Section One - Definition Two"],
|
||||||
|
tags: ["ios","iosonly"])
|
||||||
|
]
|
||||||
|
|
||||||
|
let sectionTwo = Section(name: "section_two")
|
||||||
|
sectionTwo.definitions = [
|
||||||
|
getDefinition(name: "s2_def_one",
|
||||||
|
translations: ["fr": "Section Deux - Definition Un",
|
||||||
|
"en": "Section Two - Definition One"],
|
||||||
|
tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s2_def_two",
|
||||||
|
translations: ["fr": "Section Deux - Definition Deux"],
|
||||||
|
tags: ["notranslation"])
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
|
||||||
|
defaultLang: "fr",
|
||||||
|
tags: ["ios", "iosonly", "notranslation"],
|
||||||
|
staticVar: true,
|
||||||
|
inputFilename: "myInputFilename",
|
||||||
|
extensionName: "GenStrings")
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
fileprivate let kStringsFileName = "myInputFilename"
|
||||||
|
|
||||||
|
extension GenStrings {
|
||||||
|
|
||||||
|
// MARK: - section_one
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Un - Definition Un
|
||||||
|
static var s1_def_one: String {
|
||||||
|
NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Un - Definition Deux
|
||||||
|
static var s1_def_two: String {
|
||||||
|
NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - section_two
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Deux - Definition Un
|
||||||
|
static var s2_def_one: String {
|
||||||
|
NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in fr :
|
||||||
|
/// Section Deux - Definition Deux
|
||||||
|
static var s2_def_two: String {
|
||||||
|
NSLocalizedString("s2_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Deux", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
82
Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift
Normal file
82
Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// TagsGeneratorTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 06/09/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
@testable import ResgenSwift
|
||||||
|
|
||||||
|
final class TagsGeneratorTests: XCTestCase {
|
||||||
|
|
||||||
|
private func getDefinition(name: String, lang: String, tags: [String]) -> Definition {
|
||||||
|
let definition = Definition(name: name)
|
||||||
|
definition.tags = tags
|
||||||
|
definition.translations = [lang: "Some translation"]
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGeneratedExtensionContent() {
|
||||||
|
// Given
|
||||||
|
let sectionOne = Section(name: "section_one")
|
||||||
|
sectionOne.definitions = [
|
||||||
|
getDefinition(name: "s1_def_one", lang: "ium", tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s1_def_two", lang: "ium", tags: ["ios","iosonly"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
let sectionTwo = Section(name: "section_two")
|
||||||
|
sectionTwo.definitions = [
|
||||||
|
getDefinition(name: "s2_def_one", lang: "ium", tags: ["ios","iosonly"]),
|
||||||
|
getDefinition(name: "s2_def_two", lang: "ium", tags: ["droid","droidonly"])
|
||||||
|
]
|
||||||
|
|
||||||
|
let sectionThree = Section(name: "section_three")
|
||||||
|
sectionThree.definitions = [
|
||||||
|
getDefinition(name: "s3_def_one", lang: "ium", tags: ["droid","droidonly"]),
|
||||||
|
getDefinition(name: "s3_def_two", lang: "ium", tags: ["droid","droidonly"])
|
||||||
|
]
|
||||||
|
|
||||||
|
// When
|
||||||
|
let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree],
|
||||||
|
lang: "ium",
|
||||||
|
tags: ["ios", "iosonly"],
|
||||||
|
staticVar: false,
|
||||||
|
extensionName: "GenTags")
|
||||||
|
// Expect Tags
|
||||||
|
let expect = """
|
||||||
|
// Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension GenTags {
|
||||||
|
// MARK: - section_one
|
||||||
|
|
||||||
|
/// Translation in ium :
|
||||||
|
/// Some translation
|
||||||
|
var s1_def_one: String {
|
||||||
|
"Some translation"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translation in ium :
|
||||||
|
/// Some translation
|
||||||
|
var s1_def_two: String {
|
||||||
|
"Some translation"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - section_two
|
||||||
|
|
||||||
|
/// Translation in ium :
|
||||||
|
/// Some translation
|
||||||
|
var s2_def_one: String {
|
||||||
|
"Some translation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest())
|
||||||
|
}
|
||||||
|
}
|
4
install.sh
Executable file
4
install.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
|
||||||
|
swift build -c release
|
||||||
|
cp .build/release/ResgenSwift /usr/local/bin/ResgenSwift
|
Loading…
x
Reference in New Issue
Block a user