diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme
new file mode 100644
index 0000000..aa41879
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/Imagium.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme
index 63f224c..7d57f6c 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/ResgenSwift-Package.xcscheme
@@ -118,6 +118,34 @@
ReferencedContainer = "container:">
+
+
+
+
+
+
+
+
@@ -178,9 +206,9 @@
runnableDebuggingMode = "0">
diff --git a/Package.swift b/Package.swift
index fcfa735..b5095cf 100644
--- a/Package.swift
+++ b/Package.swift
@@ -15,17 +15,17 @@ let package = Package(
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "ResgenSwift",
- dependencies: ["FontToolCore", "ColorToolCore", "Strings"]
+ dependencies: ["FontTool", "ColorTool", "Strings", "Imagium"]
),
.target(
- name: "FontToolCore",
+ name: "FontTool",
dependencies: [
"CLIToolCore",
.product(name: "ArgumentParser", package: "swift-argument-parser")
]
),
.target(
- name: "ColorToolCore",
+ name: "ColorTool",
dependencies: [
"CLIToolCore",
.product(name: "ArgumentParser", package: "swift-argument-parser")
@@ -33,6 +33,14 @@ let package = Package(
),
.target(
name: "Strings",
+ dependencies: [
+ "CLIToolCore",
+ .product(name: "ArgumentParser", package: "swift-argument-parser")
+ ],
+ sources: ["."] // Force include all subdirectories
+ ),
+ .target(
+ name: "Imagium",
dependencies: [
"CLIToolCore",
.product(name: "ArgumentParser", package: "swift-argument-parser")
diff --git a/SampleFiles/Colors/Generated/UIColor+ColorGenAllScript.swift b/SampleFiles/Colors/Generated/UIColor+ColorGenAllScript.swift
index 5b8cce8..23713af 100644
--- a/SampleFiles/Colors/Generated/UIColor+ColorGenAllScript.swift
+++ b/SampleFiles/Colors/Generated/UIColor+ColorGenAllScript.swift
@@ -1,4 +1,4 @@
-// Generated from ColorToolCore at 2022-01-10 10:57:08 +0000
+// Generated from ColorToolCore at 2022-02-14 09:30:19 +0000
import UIKit
diff --git a/SampleFiles/Fonts/Generated/UIFontoptions.+FontGenAllScript.swift b/SampleFiles/Fonts/Generated/UIFontoptions.+FontGenAllScript.swift
new file mode 100644
index 0000000..398823b
--- /dev/null
+++ b/SampleFiles/Fonts/Generated/UIFontoptions.+FontGenAllScript.swift
@@ -0,0 +1,62 @@
+// Generated from FontToolCore
+
+import UIKit
+
+extension UIFont {
+
+ enum FontName: String {
+ case LatoItalic = "Lato-Italic"
+ case LatoLightItalic = "Lato-LightItalic"
+ case LatoHairline = "Lato-Hairline"
+ case LatoBold = "Lato-Bold"
+ case LatoBlack = "Lato-Black"
+ case LatoRegular = "Lato-Regular"
+ case LatoBlackItalic = "Lato-BlackItalic"
+ case LatoBoldItalic = "Lato-BoldItalic"
+ case LatoLight = "Lato-Light"
+ case LatoHairlineItalic = "Lato-HairlineItalic"
+ }
+
+ // MARK: - Getter
+
+ static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoItalic.rawValue, size: size)!
+ }
+
+ static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
+ }
+
+ static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoHairline.rawValue, size: size)!
+ }
+
+ static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoBold.rawValue, size: size)!
+ }
+
+ static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoBlack.rawValue, size: size)!
+ }
+
+ static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoRegular.rawValue, size: size)!
+ }
+
+ static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
+ }
+
+ static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
+ }
+
+ static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoLight.rawValue, size: size)!
+ }
+
+ static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
+ UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
+ }
+
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/Generated/UIImage+ImageGenAllScript.swift b/SampleFiles/Images/Generated/UIImage+ImageGenAllScript.swift
new file mode 100644
index 0000000..ca3752e
--- /dev/null
+++ b/SampleFiles/Images/Generated/UIImage+ImageGenAllScript.swift
@@ -0,0 +1,32 @@
+// Generated from Imagium at 2022-02-14 09:30:23 +0000
+// Images from sampleImages
+
+import UIKit
+
+extension UIImage {
+
+ static var article_notification_pull_detail: UIImage {
+ UIImage(named: "article_notification_pull_detail")!
+ }
+
+ static var article_notification_pull: UIImage {
+ UIImage(named: "article_notification_pull")!
+ }
+
+ static var new_article: UIImage {
+ UIImage(named: "new_article")!
+ }
+
+ static var welcome_background: UIImage {
+ UIImage(named: "welcome_background")!
+ }
+
+ static var article_trash: UIImage {
+ UIImage(named: "article_trash")!
+ }
+
+ static var ic_close_article: UIImage {
+ UIImage(named: "ic_close_article")!
+ }
+
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/InputImages/Articles/New/new_article.svg b/SampleFiles/Images/InputImages/Articles/New/new_article.svg
new file mode 100644
index 0000000..7bc8f21
--- /dev/null
+++ b/SampleFiles/Images/InputImages/Articles/New/new_article.svg
@@ -0,0 +1,54 @@
+
diff --git a/SampleFiles/Images/InputImages/Articles/article_notification_pull.svg b/SampleFiles/Images/InputImages/Articles/article_notification_pull.svg
new file mode 100644
index 0000000..051989a
--- /dev/null
+++ b/SampleFiles/Images/InputImages/Articles/article_notification_pull.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/SampleFiles/Images/InputImages/Articles/article_notification_pull_detail.svg b/SampleFiles/Images/InputImages/Articles/article_notification_pull_detail.svg
new file mode 100644
index 0000000..15ae41f
--- /dev/null
+++ b/SampleFiles/Images/InputImages/Articles/article_notification_pull_detail.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/SampleFiles/Images/InputImages/Articles/article_trash.svg b/SampleFiles/Images/InputImages/Articles/article_trash.svg
new file mode 100644
index 0000000..8a65860
--- /dev/null
+++ b/SampleFiles/Images/InputImages/Articles/article_trash.svg
@@ -0,0 +1,4 @@
+
diff --git a/SampleFiles/Images/InputImages/ic_close_article.svg b/SampleFiles/Images/InputImages/ic_close_article.svg
new file mode 100644
index 0000000..9e247a8
--- /dev/null
+++ b/SampleFiles/Images/InputImages/ic_close_article.svg
@@ -0,0 +1,4 @@
+
diff --git a/SampleFiles/Images/InputImages/welcome_background.png b/SampleFiles/Images/InputImages/welcome_background.png
new file mode 100644
index 0000000..a604692
Binary files /dev/null and b/SampleFiles/Images/InputImages/welcome_background.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/Contents.json
new file mode 100644
index 0000000..a83e01f
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "article_notification_pull.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "article_notification_pull@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "article_notification_pull@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull.png
new file mode 100644
index 0000000..59607c1
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@2x.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@2x.png
new file mode 100644
index 0000000..ac7e5af
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@3x.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@3x.png
new file mode 100644
index 0000000..5546ca1
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull.imageset/article_notification_pull@3x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/Contents.json
new file mode 100644
index 0000000..caa7fed
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "article_notification_pull_detail.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "article_notification_pull_detail@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "article_notification_pull_detail@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail.png
new file mode 100644
index 0000000..fcb75e1
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@2x.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@2x.png
new file mode 100644
index 0000000..3966828
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@3x.png b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@3x.png
new file mode 100644
index 0000000..b16455b
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_notification_pull_detail.imageset/article_notification_pull_detail@3x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_trash.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/Contents.json
new file mode 100644
index 0000000..1ec6fce
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "article_trash.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "article_trash@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "article_trash@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash.png b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash.png
new file mode 100644
index 0000000..3b1d655
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@2x.png b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@2x.png
new file mode 100644
index 0000000..9232208
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@3x.png b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@3x.png
new file mode 100644
index 0000000..940edca
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash@3x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/Contents.json
new file mode 100644
index 0000000..c3c805a
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "ic_close_article.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "ic_close_article@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "ic_close_article@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article.png b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article.png
new file mode 100644
index 0000000..3f1c26f
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@2x.png b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@2x.png
new file mode 100644
index 0000000..bf30527
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@3x.png b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@3x.png
new file mode 100644
index 0000000..fbf5cd2
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article@3x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/new_article.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/new_article.imageset/Contents.json
new file mode 100644
index 0000000..ef1f43e
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/new_article.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "new_article.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "new_article@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "new_article@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article.png b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article.png
new file mode 100644
index 0000000..c525aa4
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@2x.png b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@2x.png
new file mode 100644
index 0000000..3ca8bd0
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@3x.png b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@3x.png
new file mode 100644
index 0000000..bb92267
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article@3x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/Contents.json b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/Contents.json
new file mode 100644
index 0000000..03a3ce7
--- /dev/null
+++ b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "welcome_background.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "welcome_background@2x.png"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "welcome_background@3x.png"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+}
\ No newline at end of file
diff --git a/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background.png b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background.png
new file mode 100644
index 0000000..88a5366
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@2x.png b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@2x.png
new file mode 100644
index 0000000..9df1caa
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@2x.png differ
diff --git a/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@3x.png b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@3x.png
new file mode 100644
index 0000000..ff0ba0e
Binary files /dev/null and b/SampleFiles/Images/imagium.xcassets/welcome_background.imageset/welcome_background@3x.png differ
diff --git a/SampleFiles/Images/sampleImages.txt b/SampleFiles/Images/sampleImages.txt
new file mode 100644
index 0000000..0bb9c1e
--- /dev/null
+++ b/SampleFiles/Images/sampleImages.txt
@@ -0,0 +1,6 @@
+i article_notification_pull_detail 150 ?
+i article_notification_pull 150 ?
+i new_article 150 ?
+i welcome_background 320 ?
+id article_trash 35 ?
+di ic_close_article 11 11
diff --git a/SampleFiles/Strings/Generated/String+StringGenAllScript.swift b/SampleFiles/Strings/Generated/String+StringGenAllScript.swift
index a2b90cf..69b5753 100644
--- a/SampleFiles/Strings/Generated/String+StringGenAllScript.swift
+++ b/SampleFiles/Strings/Generated/String+StringGenAllScript.swift
@@ -1,4 +1,4 @@
-// Generated from Strings-Stringium at 2022-01-10 10:57:09 +0000
+// Generated from Strings-Stringium at 2022-02-14 09:30:20 +0000
import UIKit
diff --git a/SampleFiles/Tags/Generated/Tags+TagGenAllScript.swift b/SampleFiles/Tags/Generated/Tags+TagGenAllScript.swift
index e0b2cda..0881d01 100644
--- a/SampleFiles/Tags/Generated/Tags+TagGenAllScript.swift
+++ b/SampleFiles/Tags/Generated/Tags+TagGenAllScript.swift
@@ -1,9 +1,9 @@
-// Generated from Strings-Tags at 2022-01-10 10:57:09 +0000
-
-import UIKit
+// Generated from Strings-Tags at 2022-02-14 09:30:20 +0000
// typelias Tags = String
+import UIKit
+
extension Tags {
// MARK: - ScreenTag
@@ -20,4 +20,4 @@ extension Tags {
"Ecran deux"
}
-}
+}
\ No newline at end of file
diff --git a/SampleFiles/genAllRessources.sh b/SampleFiles/genAllRessources.sh
index e1f5b72..2c94a16 100755
--- a/SampleFiles/genAllRessources.sh
+++ b/SampleFiles/genAllRessources.sh
@@ -3,19 +3,23 @@
FORCE_FLAG="$1"
# Font
-swift run -c release FontToolCore $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
+swift run -c release FontTool $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
--extension-output-path "./Fonts/Generated" \
--extension-name "UIFont" \
--extension-suffix "GenAllScript"
+echo "\n-------------------------\n"
+
# Color
-swift run -c release ColorToolCore $FORCE_FLAG "./Colors/sampleColors1.txt" \
+swift run -c release ColorTool $FORCE_FLAG "./Colors/sampleColors1.txt" \
--style all \
--xcassets-path "./Colors/colors.xcassets" \
--extension-output-path "./Colors/Generated/" \
--extension-name "UIColor" \
--extension-suffix "GenAllScript"
+echo "\n-------------------------\n"
+
# Twine
swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
--output-path "./Twine/Generated" \
@@ -23,6 +27,8 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
--default-lang "en" \
--extension-output-path "./Twine/Generated"
+echo "\n-------------------------\n"
+
# Strings
swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt" \
--output-path "./Strings/Generated" \
@@ -31,12 +37,21 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt"
--extension-output-path "./Strings/Generated" \
--extension-name "String" \
--extension-suffix "GenAllScript"
-
+
+echo "\n-------------------------\n"
+
# Tags
swift run -c release Strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \
- --lang "ium" \
+ --lang "ium" \
--extension-output-path "./Tags/Generated" \
--extension-name "Tags" \
--extension-suffix "GenAllScript"
+echo "\n-------------------------\n"
+
# Images
+swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
+ --xcassets-path "./Images/imagium.xcassets" \
+ --extension-output-path "./Images/Generated" \
+ --extension-name "UIImage" \
+ --extension-suffix "GenAllScript"
diff --git a/Sources/CLIToolCore/Shell.swift b/Sources/CLIToolCore/Shell.swift
index 4cb573d..0f8c28b 100644
--- a/Sources/CLIToolCore/Shell.swift
+++ b/Sources/CLIToolCore/Shell.swift
@@ -28,4 +28,24 @@ public class Shell {
return (terminationStatus: task.terminationStatus, output: output)
}
+
+ @discardableResult
+ public static func shell(_ args: [String]) -> (terminationStatus: Int32, output: String?) {
+ let task = Process()
+ task.launchPath = "/usr/bin/env"
+ task.arguments = args
+
+ let pipe = Pipe()
+ task.standardOutput = pipe
+ task.launch()
+ task.waitUntilExit()
+
+ let data = pipe.fileHandleForReading.readDataToEndOfFile()
+
+ guard let output: String = String(data: data, encoding: .utf8) else {
+ return (terminationStatus: task.terminationStatus, output: nil)
+ }
+
+ return (terminationStatus: task.terminationStatus, output: output)
+ }
}
diff --git a/Sources/ColorToolCore/ColorExtensionGenerator.swift b/Sources/ColorTool/ColorExtensionGenerator.swift
similarity index 100%
rename from Sources/ColorToolCore/ColorExtensionGenerator.swift
rename to Sources/ColorTool/ColorExtensionGenerator.swift
diff --git a/Sources/ColorToolCore/ColorToolError.swift b/Sources/ColorTool/ColorToolError.swift
similarity index 100%
rename from Sources/ColorToolCore/ColorToolError.swift
rename to Sources/ColorTool/ColorToolError.swift
diff --git a/Sources/ColorTool/ColorToolOptions.swift b/Sources/ColorTool/ColorToolOptions.swift
new file mode 100644
index 0000000..0cda982
--- /dev/null
+++ b/Sources/ColorTool/ColorToolOptions.swift
@@ -0,0 +1,32 @@
+//
+// ColorToolOptions.swift
+//
+//
+// Created by Thibaut Schmitt on 17/01/2022.
+//
+
+import Foundation
+import ArgumentParser
+
+struct ColorToolOptions: ParsableArguments {
+ @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
+ var forceGeneration = false
+
+ @Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var inputFile: String
+
+ @Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
+ var style: String
+
+ @Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var xcassetsPath: String
+
+ @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var extensionOutputPath: String
+
+ @Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.")
+ var extensionName: String = ColorTool.defaultExtensionName
+
+ @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
+ var extensionSuffix: String = ""
+}
diff --git a/Sources/ColorToolCore/ColorXcassetHelper.swift b/Sources/ColorTool/ColorXcassetHelper.swift
similarity index 100%
rename from Sources/ColorToolCore/ColorXcassetHelper.swift
rename to Sources/ColorTool/ColorXcassetHelper.swift
diff --git a/Sources/ColorToolCore/GenColor.swift b/Sources/ColorTool/GenColor.swift
similarity index 100%
rename from Sources/ColorToolCore/GenColor.swift
rename to Sources/ColorTool/GenColor.swift
diff --git a/Sources/ColorToolCore/main.swift b/Sources/ColorTool/main.swift
similarity index 72%
rename from Sources/ColorToolCore/main.swift
rename to Sources/ColorTool/main.swift
index b20301e..c5b06b2 100644
--- a/Sources/ColorToolCore/main.swift
+++ b/Sources/ColorTool/main.swift
@@ -18,70 +18,51 @@ struct ColorTool: ParsableCommand {
static let defaultExtensionName = "UIColor"
static let assetsColorsFolderName = "Colors"
- @Flag(name: .customShort("f"), help: "Should force generation")
- var forceGeneration = false
+ @OptionGroup var options: ColorToolOptions
- @Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var inputFile: String
-
- @Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
- var style: String
-
- @Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var xcassetsPath: String
-
- @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var extensionOutputPath: String
-
- @Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.")
- var extensionName: String = Self.defaultExtensionName
-
- @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
- var extensionSuffix: String = ""
-
- var colorStyle: ColorStyle { ColorStyle(rawValue: style) ?? .all }
- var extensionFileName: String { "\(extensionName)+Color\(extensionSuffix).swift" }
- var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
+ var colorStyle: ColorStyle { ColorStyle(rawValue: options.style) ?? .all }
+ var extensionFileName: String { "\(options.extensionName)+Color\(options.extensionSuffix).swift" }
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
public func run() throws {
print("[ColorTool] Starting colors generation")
- print("[ColorTool] Will use inputFile \(inputFile) to generate \(colorStyle) colors in xcassets \(xcassetsPath)")
+ print("[ColorTool] Will use inputFile \(options.inputFile) to generate \(colorStyle) colors in xcassets \(options.xcassetsPath)")
print("[ColorTool] Extension will be \(extensionFilePath)")
// Check requirements
let fileManager = FileManager()
- guard fileManager.fileExists(atPath: xcassetsPath) else {
- let error = ColorToolError.fileNotExists(xcassetsPath)
+ guard fileManager.fileExists(atPath: options.xcassetsPath) else {
+ let error = ColorToolError.fileNotExists(options.xcassetsPath)
print(error.localizedDescription)
ColorTool.exit(withError: error)
}
- guard fileManager.fileExists(atPath: inputFile) else {
- let error = ColorToolError.fileNotExists(inputFile)
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = ColorToolError.fileNotExists(options.inputFile)
print(error.localizedDescription)
ColorTool.exit(withError: error)
}
// Check if needed to regenerate
- guard GeneratorChecker.shouldGenerate(force: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
+ guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
print("[ColorTool] Colors are already up to date :) ")
return
}
print("[ColorTool] Will generate colors")
// Delete current colors
- Shell.shell("rm", "-rf", "\(xcassetsPath)/Colors/*")
+ Shell.shell("rm", "-rf", "\(options.xcassetsPath)/Colors/*")
// Get colors
let colorsToGen = getColorsGen()
// Generate all colors in xcassets
- let colorAssetHelper = ColorXcassetHelper(xcassetsPath: xcassetsPath, colors: colorsToGen)
+ let colorAssetHelper = ColorXcassetHelper(xcassetsPath: options.xcassetsPath, colors: colorsToGen)
colorAssetHelper.generateXcassetColors()
// Generate extension
let extensionGenerator = ColorExtensionGenerator(colors: colorsToGen,
- extensionClassname: extensionName,
+ extensionClassname: options.extensionName,
isUIColorExtension: isUIColorExtension())
let extensionHeader = extensionGenerator.getHeader()
let extensionProperties = extensionGenerator.getProperties()
@@ -115,8 +96,8 @@ struct ColorTool: ParsableCommand {
private func getColorsGen() -> [GenColor] {
// Get content of input file
- let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
- let colorsByLines = inputFileContent.components(separatedBy: .newlines)
+ let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8)
+ let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
// Iterate on each line of input file
return colorsByLines.enumerated().compactMap { lineNumber, colorLine in
@@ -155,7 +136,7 @@ struct ColorTool: ParsableCommand {
// MARK: - Helpers
private func isUIColorExtension() -> Bool {
- extensionName == Self.defaultExtensionName
+ options.extensionName == Self.defaultExtensionName
}
}
diff --git a/Sources/FontTool/FontOptions.swift b/Sources/FontTool/FontOptions.swift
new file mode 100644
index 0000000..5a6bf7c
--- /dev/null
+++ b/Sources/FontTool/FontOptions.swift
@@ -0,0 +1,26 @@
+//
+// FontOptions.swift
+//
+//
+// Created by Thibaut Schmitt on 17/01/2022.
+//
+
+import Foundation
+import ArgumentParser
+
+struct FontOptions: ParsableArguments {
+ @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
+ var forceGeneration = false
+
+ @Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var inputFile: String
+
+ @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var extensionOutputPath: String
+
+ @Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
+ var extensionName: String = FontTool.defaultExtensionName
+
+ @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
+ var extensionSuffix: String = ""
+}
diff --git a/Sources/FontToolCore/FontToolContentGenerator.swift b/Sources/FontTool/FontToolContentGenerator.swift
similarity index 100%
rename from Sources/FontToolCore/FontToolContentGenerator.swift
rename to Sources/FontTool/FontToolContentGenerator.swift
diff --git a/Sources/FontToolCore/FontToolError.swift b/Sources/FontTool/FontToolError.swift
similarity index 100%
rename from Sources/FontToolCore/FontToolError.swift
rename to Sources/FontTool/FontToolError.swift
diff --git a/Sources/FontToolCore/FontToolHelper.swift b/Sources/FontTool/FontToolHelper.swift
similarity index 100%
rename from Sources/FontToolCore/FontToolHelper.swift
rename to Sources/FontTool/FontToolHelper.swift
diff --git a/Sources/FontToolCore/main.swift b/Sources/FontTool/main.swift
similarity index 64%
rename from Sources/FontToolCore/main.swift
rename to Sources/FontTool/main.swift
index a2ee033..53db7ca 100644
--- a/Sources/FontToolCore/main.swift
+++ b/Sources/FontTool/main.swift
@@ -10,39 +10,27 @@ import CLIToolCore
import ArgumentParser
struct FontTool: ParsableCommand {
+ static var configuration = CommandConfiguration(abstract: "Generate fonts plist info and extension to access fonts easily.")
static let defaultExtensionName = "UIFont"
- @Flag(name: .customShort("f"), help: "Should force generation")
- var forceGeneration = false
+ @OptionGroup var options: FontOptions
- @Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var inputFile: String
-
- @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var extensionOutputPath: String
-
- @Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
- var extensionName: String = Self.defaultExtensionName
-
- @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
- var extensionSuffix: String = ""
-
- var extensionFileName: String { "\(extensionName)+Font\(extensionSuffix).swift" }
- var extensionFilePath: String { "\(extensionOutputPath)/\(extensionFileName)" }
+ var extensionFileName: String { "\(options.extensionName)options.+Font\(options.extensionSuffix).swift" }
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
public func run() throws {
print("[FontTool] Starting fonts generation")
// Check requirements
let fileManager = FileManager()
- guard fileManager.fileExists(atPath: inputFile) else {
- let error = FontToolError.fileNotExists(inputFile)
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = FontToolError.fileNotExists(options.inputFile)
print(error.localizedDescription)
FontTool.exit(withError: error)
}
// Check if needed to regenerate
- guard GeneratorChecker.shouldGenerate(force: forceGeneration, inputFilePath: inputFile, extensionFilePath: extensionFilePath) else {
+ guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
print("[FontTool] Fonts are already up to date :) ")
return
}
@@ -51,7 +39,7 @@ struct FontTool: ParsableCommand {
// Get fonts to generate
let fontsToGenerate = getFontsToGenerate()
- let inputFolder = URL(fileURLWithPath: inputFile).deletingLastPathComponent().relativePath
+ let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
let fontsFilenames = FontToolHelper
.getFontsFilenames(fromInputFolder: inputFolder)
.filter { fontNameWithPath in
@@ -71,7 +59,7 @@ struct FontTool: ParsableCommand {
// Adding fontsFilenames to header (ex: path/to/font.ttf) to make check of regeneration faster
let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsFilenames)
- let extensionDefinitionOpening = "extension \(extensionName) {\n"
+ let extensionDefinitionOpening = "extension \(options.extensionName) {\n"
let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsNames)
let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsNames, isUIFontExtension: isUIFontExtension())
let extensionDefinitionClosing = "}"
@@ -117,23 +105,13 @@ struct FontTool: ParsableCommand {
// MARK: - Helpers
private func getFontsToGenerate() -> [String] {
- let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
- return inputFileContent.components(separatedBy: .newlines)
+ let inputFileContent = try! String(contentsOfFile: options.inputFile, encoding: .utf8)
+ return inputFileContent.components(separatedBy: CharacterSet.newlines)
}
private func isUIFontExtension() -> Bool {
- extensionName == Self.defaultExtensionName
+ options.extensionName == Self.defaultExtensionName
}
}
FontTool.main()
-
-/*
-
- 1. R2Font extension
- swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated --extension-name R2Font
-
- 1. UIFont defualt extension (will gen static property)
- swift run -c release FontToolCore ./SampleFiles/Fonts/sampleFonts.txt --extension-output-path ./SampleFiles/Fonts/Generated
-
- */
diff --git a/Sources/Imagium/ConvertArgument.swift b/Sources/Imagium/ConvertArgument.swift
new file mode 100644
index 0000000..c66566b
--- /dev/null
+++ b/Sources/Imagium/ConvertArgument.swift
@@ -0,0 +1,13 @@
+//
+// ConvertArgument.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+
+struct ConvertArgument {
+ let width: String?
+ let height: String?
+}
diff --git a/Sources/Imagium/FileManagerExtensions.swift b/Sources/Imagium/FileManagerExtensions.swift
new file mode 100644
index 0000000..0b98d2d
--- /dev/null
+++ b/Sources/Imagium/FileManagerExtensions.swift
@@ -0,0 +1,56 @@
+//
+// FileManagerExtensions.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+
+extension FileManager {
+ func getAllRegularFileIn(directory: String) -> [String] {
+ var files = [String]()
+ guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
+ let error = ImagiumError.unknown("Cannot enumerate file in \(directory)")
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+
+ for case let fileURL as URL in enumerator {
+ do {
+ let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey])
+ if fileAttributes.isRegularFile! {
+ files.append(fileURL.relativePath)
+ }
+ } catch {
+ let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+ }
+ return files
+ }
+
+ func getAllImageSetFolderIn(directory: String) -> [String] {
+ var files = [String]()
+ guard let enumerator = self.enumerator(at: URL(string: directory)!, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
+ let error = ImagiumError.unknown("Cannot enumerate imageset directory in \(directory)")
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+
+ for case let fileURL as URL in enumerator {
+ do {
+ let fileAttributes = try fileURL.resourceValues(forKeys:[.isDirectoryKey])
+ if fileAttributes.isDirectory! && fileURL.lastPathComponent.hasSuffix(".imageset") {
+ files.append(fileURL.lastPathComponent)
+ }
+ } catch {
+ let error = ImagiumError.getFileAttributed(fileURL.relativePath, error.localizedDescription)
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+ }
+ return files
+ }
+}
diff --git a/Sources/Imagium/ImageExtensionGenerator.swift b/Sources/Imagium/ImageExtensionGenerator.swift
new file mode 100644
index 0000000..9c72735
--- /dev/null
+++ b/Sources/Imagium/ImageExtensionGenerator.swift
@@ -0,0 +1,87 @@
+//
+// ImageExtensionGenerator.swift
+//
+//
+// Created by Thibaut Schmitt on 14/02/2022.
+//
+
+import CLIToolCore
+import Foundation
+
+class ImageExtensionGenerator {
+
+ // MARK: - Extension files
+
+ static func writeStringsFiles(images: [ImageToGen], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
+ // Get header/footer
+ let extensionHeader = Self.getHeader(inputFilename: inputFilename, extensionClassname: extensionName)
+ let extensionFooter = Self.getFooter()
+
+ // Create content
+ let extensionContent: String = {
+ var content = ""
+ images.forEach { img in
+ if staticVar {
+ content += "\n\(img.getStaticImageProperty())"
+ } else {
+ content += "\n\(img.getImageProperty())"
+ }
+ content += "\n "
+ }
+ return content
+ }()
+
+ // Create file if not exists
+ let fileManager = FileManager()
+ if fileManager.fileExists(atPath: extensionFilePath) == false {
+ Shell.shell("touch", "\(extensionFilePath)")
+ }
+
+ // Generate extension
+ Self.generateExtensionFile(extensionFilePath: extensionFilePath, extensionHeader, extensionContent, extensionFooter)
+ }
+
+ // MARK: - pragm
+
+ private static func generateExtensionFile(extensionFilePath: String, _ args: String...) {
+ // Create file if not exists
+ let fileManager = FileManager()
+ if fileManager.fileExists(atPath: extensionFilePath) == false {
+ Shell.shell("touch", "\(extensionFilePath)")
+ }
+
+ // Create extension content
+ let extensionContent = args.joined(separator: "\n")
+
+ // Write content
+ let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
+ do {
+ try extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
+ } catch (let error) {
+ let error = ImagiumError.writeFile(extensionFilePath, error.localizedDescription)
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+ }
+
+ private static func getHeader(inputFilename: String, extensionClassname: String) -> String {
+ """
+ // Generated from Imagium at \(Date())
+ // Images from \(inputFilename)
+
+ import UIKit
+
+ extension \(extensionClassname) {
+ """
+ }
+
+ private static func getFooter() -> String {
+ """
+ }
+ """
+ }
+}
+
+//@objc var onboarding_foreground3: UIImage {
+// return UIImage(named: "onboarding_foreground3")!
+// }
diff --git a/Sources/Imagium/ImageFileParser.swift b/Sources/Imagium/ImageFileParser.swift
new file mode 100644
index 0000000..7355963
--- /dev/null
+++ b/Sources/Imagium/ImageFileParser.swift
@@ -0,0 +1,47 @@
+//
+// ImageFileParser.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+
+class ImageFileParser {
+
+ static func parse(_ inputFile: String, platform: PlatormTag) -> [ImageToGen] {
+ let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
+ let stringsByLines = inputFileContent.components(separatedBy: .newlines)
+
+ var imagesToGenerate = [ImageToGen]()
+
+ // Parse file
+ stringsByLines.forEach {
+ guard $0.removeLeadingTrailingWhitespace().isEmpty == false, $0.first != "#" else {
+ return
+ }
+
+ let splittedLine = $0.split(separator: " ")
+
+ let width: Int = {
+ if splittedLine[2] == "?" {
+ return -1
+ }
+ return Int(splittedLine[2])!
+ }()
+ let height: Int = {
+ if splittedLine[3] == "?" {
+ return -1
+ }
+ return Int(splittedLine[3])!
+ }()
+
+ let image = ImageToGen(name: String(splittedLine[1]), tags: String(splittedLine[0]), width: width, height: height)
+ imagesToGenerate.append(image)
+ }
+
+ return imagesToGenerate.filter {
+ $0.tags.contains(platform.rawValue)
+ }
+ }
+}
diff --git a/Sources/Imagium/ImageToGen.swift b/Sources/Imagium/ImageToGen.swift
new file mode 100644
index 0000000..760c821
--- /dev/null
+++ b/Sources/Imagium/ImageToGen.swift
@@ -0,0 +1,90 @@
+//
+// File.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+
+struct ImageToGen {
+ let name: String
+ let tags: String
+ let width: Int
+ let height: Int
+
+ // MARK: - Convert
+
+ var convertArguments: (x1: ConvertArgument, x2: ConvertArgument, x3: ConvertArgument) {
+ var width1x = ""
+ var height1x = ""
+ var width2x = ""
+ var height2x = ""
+ var width3x = ""
+ var height3x = ""
+
+ if width != -1 {
+ width1x = "\(width)"
+ width2x = "\(width * 2)"
+ width3x = "\(width * 3)"
+ }
+
+ if height != -1 {
+ height1x = "\(height)"
+ height2x = "\(height * 2)"
+ height3x = "\(height * 3)"
+ }
+
+ return (x1: ConvertArgument(width: width1x, height: height1x),
+ x2: ConvertArgument(width: width2x, height: height2x),
+ x3: ConvertArgument(width: width3x, height: height3x))
+ }
+
+ // MARK: - Assets
+
+ var contentJson: String {
+ """
+ {
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x",
+ "filename" : "\(name).\(XcassetsGenerator.outputImageExtension)"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x",
+ "filename" : "\(name)@2x.\(XcassetsGenerator.outputImageExtension)"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x",
+ "filename" : "\(name)@3x.\(XcassetsGenerator.outputImageExtension)"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "ResgenSwift-Imagium"
+ }
+ }
+ """
+ }
+
+ // MARK: - Extension property
+
+ func getImageProperty() -> String {
+ """
+ var \(name): UIImage {
+ UIImage(named: "\(name)")!
+ }
+ """
+ }
+
+ func getStaticImageProperty() -> String {
+ """
+ static var \(name): UIImage {
+ UIImage(named: "\(name)")!
+ }
+ """
+ }
+}
diff --git a/Sources/Imagium/ImagiumError.swift b/Sources/Imagium/ImagiumError.swift
new file mode 100644
index 0000000..baa7cc4
--- /dev/null
+++ b/Sources/Imagium/ImagiumError.swift
@@ -0,0 +1,43 @@
+//
+// ImagiumError.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+
+enum ImagiumError: Error {
+ case inputFolderNotFound(String)
+ case fileNotExists(String)
+ case unknownImageExtension(String)
+ case getFileAttributed(String, String)
+ case rsvgConvertNotFound
+ case writeFile(String, String)
+ case unknown(String)
+
+ var localizedDescription: String {
+ switch self {
+ case .inputFolderNotFound(let inputFolder):
+ return " error:[\(Imagium.toolName)] Input folder not found: \(inputFolder)"
+
+ case .fileNotExists(let filename):
+ return " error:[\(Imagium.toolName)] File \(filename) does not exists"
+
+ case .unknownImageExtension(let filename):
+ return " error:[\(Imagium.toolName)] File \(filename) have an unhandled file extension. Cannot generate image."
+
+ case .getFileAttributed(let filename, let errorDescription):
+ return " error:[\(Imagium.toolName)] Getting file attributes of \(filename) failed with error: \(errorDescription)"
+
+ case .rsvgConvertNotFound:
+ return " error:[\(Imagium.toolName)] Can't find rsvg-convert (can be installed with 'brew remove imagemagick && brew install imagemagick --with-librsvg')"
+
+ case .writeFile(let subErrorDescription, let filename):
+ return " error:[\(Imagium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
+
+ case .unknown(let errorDescription):
+ return " error:[\(Imagium.toolName)] Unknown error: \(errorDescription)"
+ }
+ }
+}
diff --git a/Sources/Imagium/ImagiumOptions.swift b/Sources/Imagium/ImagiumOptions.swift
new file mode 100644
index 0000000..6fc88c2
--- /dev/null
+++ b/Sources/Imagium/ImagiumOptions.swift
@@ -0,0 +1,41 @@
+//
+// ImagiumOptions.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+import ArgumentParser
+
+struct ImagiumOptions: ParsableArguments {
+ @Flag(name: .customShort("f"), help: "Should force script execution")
+ var forceExecution = false
+
+ @Flag(name: .customShort("F"), help: "Regenerate all images")
+ var forceExecutionAndGeneration = false
+
+ @Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var inputFile: String
+
+ @Option(help: "Xcassets path where to generate images.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var xcassetsPath: String
+
+ @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var extensionOutputPath: String
+
+ @Option(help: "Extension name. If not specified, it will generate an UIImage extension. Using default extension name will generate static property.")
+ var extensionName: String = Imagium.defaultExtensionName
+
+ @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift")
+ var extensionSuffix: String = ""
+}
+
+
+/*
+ swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
+ --xcassets-path "./Images/imagium.xcassets" \
+ --extension-output-path "./Images/Generated" \
+ --extension-name "UIImage" \
+ --extension-suffix "GenAllScript"
+ */
diff --git a/Sources/Imagium/XcassetsGenerator.swift b/Sources/Imagium/XcassetsGenerator.swift
new file mode 100644
index 0000000..54a5ea0
--- /dev/null
+++ b/Sources/Imagium/XcassetsGenerator.swift
@@ -0,0 +1,189 @@
+//
+// File.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+import CLIToolCore
+
+class XcassetsGenerator {
+
+ static let outputImageExtension = "png"
+
+ let forceGeneration: Bool
+
+ // MARK: - Init
+
+ init(forceGeneration: Bool) {
+ self.forceGeneration = forceGeneration
+ }
+
+ // MARK: - Assets generation
+
+ func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) {
+ let fileManager = FileManager()
+ let svgConverter = Imagium.getSvgConverterPath()
+ let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
+
+ var generatedAssetsPaths = [String]()
+
+ // Generate new assets
+ imagesToGenerate.forEach { imageToGen in
+ // Get image path
+ let imageData: (path: String, ext: String) = {
+ for subfile in allSubFiles {
+ if subfile.hasSuffix("/" + imageToGen.name + ".svg") {
+ return (subfile, "svg")
+ }
+ if subfile.hasSuffix("/" + imageToGen.name + ".png") {
+ return (subfile, "png")
+ }
+ if subfile.hasSuffix("/" + imageToGen.name + ".jpg") {
+ return (subfile, "jpg")
+ }
+ if subfile.hasSuffix("/" + imageToGen.name + ".jepg") {
+ return (subfile, "jepg")
+ }
+ }
+ let error = ImagiumError.unknownImageExtension(imageToGen.name)
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }()
+
+ // Create imageset folder
+ let imagesetName = "\(imageToGen.name).imageset"
+ let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
+ Shell.shell("mkdir", "-p", imagesetPath)
+
+ // Store managed images path
+ generatedAssetsPaths.append(imagesetName)
+
+ // Generate output images path
+ let output1x = "\(imagesetPath)/\(imageToGen.name).\(XcassetsGenerator.outputImageExtension)"
+ let output2x = "\(imagesetPath)/\(imageToGen.name)@2x.\(XcassetsGenerator.outputImageExtension)"
+ let output3x = "\(imagesetPath)/\(imageToGen.name)@3x.\(XcassetsGenerator.outputImageExtension)"
+
+ // Check if we need to convert image
+ if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) {
+ print("\(imageToGen.name) -> Not regenerating")
+ return
+ }
+
+ // Convert image
+ let convertArguments = imageToGen.convertArguments
+ if imageData.ext == "svg" {
+ // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png
+ // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png
+ // /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
+ var command1x = ["\(svgConverter)", "\(imageData.path)"]
+ var command2x = ["\(svgConverter)", "\(imageData.path)"]
+ var command3x = ["\(svgConverter)", "\(imageData.path)"]
+
+ self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
+ self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
+ self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
+
+ command1x.append(contentsOf: ["-o", output1x])
+ command2x.append(contentsOf: ["-o", output2x])
+ command3x.append(contentsOf: ["-o", output3x])
+
+ Shell.shell(command1x)
+ Shell.shell(command2x)
+ Shell.shell(command3x)
+ } else {
+ // convert path/to/image.png -resize 200x300 path/to/output.png
+ // convert path/to/image.png -resize 200x path/to/output.png
+ // convert path/to/image.png -resize x300 path/to/output.png
+ Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x)
+ Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x)
+ Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x)
+ }
+
+ // Write Content.json
+ let imagesetContentJson = imageToGen.contentJson
+ let contentJsonFilePath = "\(imagesetPath)/Contents.json"
+ if fileManager.fileExists(atPath: contentJsonFilePath) == false {
+ Shell.shell("touch", "\(contentJsonFilePath)")
+ }
+
+ let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
+ try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
+
+ print("\(imageToGen.name) -> Generated")
+ }
+
+ // Success info
+ let generatedAssetsCount = generatedAssetsPaths.count
+ print("Images generated: \(generatedAssetsCount)")
+
+ // Delete old assets
+ let allImagesetName = Set(fileManager.getAllImageSetFolderIn(directory: xcassetsPath))
+ let imagesetToRemove = allImagesetName.subtracting(Set(generatedAssetsPaths))
+
+ imagesetToRemove.forEach {
+ print("Will remove: \($0)")
+ }
+
+ imagesetToRemove.forEach { itemToRemove in
+ try! fileManager.removeItem(atPath: "\(xcassetsPath)/\(itemToRemove)")
+ }
+ print("Removed \(imagesetToRemove.count) images")
+ }
+
+ // MARK: - Helpers: SVG command
+
+ private func addConvertArgument(command: inout [String], convertArgument: ConvertArgument) {
+ if let width = convertArgument.width, width.isEmpty == false {
+ command.append("-w")
+ command.append("\(width)")
+ }
+ if let height = convertArgument.height, height.isEmpty == false {
+ command.append("-h")
+ command.append("\(height)")
+ }
+ }
+
+ // MARK: - Helpers: bypass generation
+
+ private func shouldBypassGeneration(for image: ImageToGen, xcassetImagePath: String) -> Bool {
+ guard forceGeneration == false else {
+ return false
+ }
+
+ let fileManager = FileManager()
+
+ // File not exists -> do not bypass
+ guard fileManager.fileExists(atPath: xcassetImagePath) else {
+ return false
+ }
+
+ // Info unavailable -> do not bypass
+ let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath)
+ let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath)
+ guard taskWidth.terminationStatus == 0,
+ taskHeight.terminationStatus == 0 else {
+ return false
+ }
+
+ let currentWidth = Int(taskWidth.output ?? "-1") ?? -1
+ let currentheight = Int(taskHeight.output ?? "-1") ?? -1
+
+ // Info unavailable -> do not bypass
+ guard currentWidth > 0 && currentheight > 0 else {
+ return false
+ }
+
+ // Check width and height
+ if image.width != -1 && currentWidth == image.width {
+ return true
+ }
+ if image.height != -1 && currentheight == image.height {
+ return true
+ }
+
+ return false
+ }
+
+}
diff --git a/Sources/Imagium/main.swift b/Sources/Imagium/main.swift
new file mode 100644
index 0000000..683f40c
--- /dev/null
+++ b/Sources/Imagium/main.swift
@@ -0,0 +1,111 @@
+//
+// Imagium.swift
+//
+//
+// Created by Thibaut Schmitt on 24/01/2022.
+//
+
+import Foundation
+import ArgumentParser
+import CLIToolCore
+
+enum PlatormTag: String {
+ case droid = "d"
+ case ios = "i"
+}
+
+struct Imagium: ParsableCommand {
+
+ static var configuration = CommandConfiguration(
+ abstract: "A utility for generate images.",
+ version: "0.1.0"
+ )
+
+ static let toolName = "Imagium"
+ static let defaultExtensionName = "UIImage"
+
+ var extensionFileName: String { "\(options.extensionName)+Image\(options.extensionSuffix).swift" }
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
+ var inputFilenameWithoutExt: String {
+ URL(fileURLWithPath: options.inputFile)
+ .deletingPathExtension()
+ .lastPathComponent
+ }
+
+ @OptionGroup var options: ImagiumOptions
+
+ mutating func run() {
+ print("[\(Self.toolName)] Starting images generation")
+
+ // Check requirements
+ guard checkRequirements() else { return }
+
+ print("[\(Self.toolName)] Will generate images")
+
+ // Parse input file
+ let imagesToGenerate = ImageFileParser.parse(options.inputFile, platform: PlatormTag.ios)
+
+ // Generate xcassets files
+ let inputFolder = URL(fileURLWithPath: options.inputFile)
+ .deletingLastPathComponent()
+ .relativePath
+ let xcassetsGenerator = XcassetsGenerator(forceGeneration: options.forceExecutionAndGeneration)
+ xcassetsGenerator.generateXcassets(inputPath: inputFolder,
+ imagesToGenerate: imagesToGenerate,
+ xcassetsPath: options.xcassetsPath)
+
+ // Generate extension
+ ImageExtensionGenerator.writeStringsFiles(images: imagesToGenerate,
+ staticVar: options.extensionName == Self.defaultExtensionName,
+ inputFilename: inputFilenameWithoutExt,
+ extensionName: options.extensionName,
+ extensionFilePath: extensionFilePath)
+
+
+ print("[\(Self.toolName)] Images generated")
+ }
+
+ // MARK: - Requirements
+
+ private func checkRequirements() -> Bool {
+ guard options.forceExecutionAndGeneration == false else {
+ return true
+ }
+
+ let fileManager = FileManager()
+
+ // Input file
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = ImagiumError.fileNotExists(options.inputFile)
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+
+ // RSVG-Converter
+ _ = Imagium.getSvgConverterPath()
+
+ // Check if needed to regenerate
+ guard GeneratorChecker.shouldGenerate(force: options.forceExecution, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
+ print("[\(Self.toolName)] Images are already up to date :) ")
+ return false
+ }
+
+ return true
+ }
+
+ // MARK: - Helpers
+
+ @discardableResult
+ static func getSvgConverterPath() -> String {
+ let taskSvgConverter = Shell.shell("which", "rsvg-convert")
+ if taskSvgConverter.terminationStatus == 0 {
+ return taskSvgConverter.output!.removeCharacters(from: CharacterSet.whitespacesAndNewlines)
+ }
+
+ let error = ImagiumError.rsvgConvertNotFound
+ print(error.localizedDescription)
+ Imagium.exit(withError: error)
+ }
+}
+
+Imagium.main()
diff --git a/Sources/ResgenSwift/main.swift b/Sources/ResgenSwift/main.swift
index 1830a03..c2d7115 100644
--- a/Sources/ResgenSwift/main.swift
+++ b/Sources/ResgenSwift/main.swift
@@ -1 +1,13 @@
print("Welcome ResgenSwift")
+
+/*
+ Make resgen as main command and font/color... as subcommands. It could look like:
+ Resgen
+ -> ColorTool
+ -> FontTool
+ -> ImageTool
+ -> Strings
+ -> Twine
+ -> Stringium
+ -> Tag
+ */
diff --git a/Sources/Strings/Generator/StringsFileGenerator.swift b/Sources/Strings/Generator/StringsFileGenerator.swift
index 35ff919..e4d41d4 100644
--- a/Sources/Strings/Generator/StringsFileGenerator.swift
+++ b/Sources/Strings/Generator/StringsFileGenerator.swift
@@ -8,121 +8,120 @@
import Foundation
import CLIToolCore
-extension Strings {
- class StringsFileGenerator {
-
- // MARK: - Strings Files
-
- static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
- var stringsFilesContent = [String: String]()
- for lang in langs {
- stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
- defaultLang: defaultLang,
- tags: tags,
- sections: sections)
- }
-
- // Write strings file content
- langs.forEach { lang in
- guard let fileContent = stringsFilesContent[lang] else { return }
-
- let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
- let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
- do {
- try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
- } catch (let error) {
- let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
- }
+class StringsFileGenerator {
+
+ // MARK: - Strings Files
+
+ static func writeStringsFiles(sections: [Section], langs: [String], defaultLang: String, tags: [String], outputPath: String, inputFilenameWithoutExt: String) {
+ var stringsFilesContent = [String: String]()
+ for lang in langs {
+ stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
+ defaultLang: defaultLang,
+ tags: tags,
+ sections: sections)
}
- private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
- var stringsFileContent = """
+ // Write strings file content
+ langs.forEach { lang in
+ guard let fileContent = stringsFilesContent[lang] else { return }
+
+ let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
+ let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
+ do {
+ try fileContent.write(to: stringsFilePathURL, atomically: true, encoding: .utf8)
+ } catch (let error) {
+ let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
+ }
+ }
+ }
+
+ private static func generateStringsFileContent(lang: String, defaultLang: String, tags inputTags: [String], sections: [Section]) -> String {
+ var stringsFileContent = """
/**
* Apple Strings File
* Generated by ResgenSwift 1.0.0
* Language: \(lang)
*/\n
"""
+
+ sections.forEach { section in
+ // Check that at least one string will be generated
+ guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
+ return // Go to next section
+ }
+ stringsFileContent += "\n/********** \(section.name) **********/\n\n"
+ section.definitions.forEach { definition in
+ let translationOpt: String? = {
+ if definition.tags.contains(Stringium.noTranslationTag) {
+ return definition.translations[defaultLang]
+ }
+ return definition.translations[lang]
+ }()
+
+ if let translation = translationOpt {
+ stringsFileContent += "\"\(definition.name)\" = \"\(translation)\";\n\n"
+ } else {
+ let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
+ }
+ }
+ }
+
+ return stringsFileContent
+ }
+
+ // MARK: - Extension file
+
+ static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
+ let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
+ let extensionFooter = Self.getFooter()
+
+ let extensionContent: String = {
+ var content = ""
sections.forEach { section in
// Check that at least one string will be generated
- guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
+ guard section.hasOneOrMoreMatchingTags(tags: tags) else {
return // Go to next section
}
- stringsFileContent += "\n/********** \(section.name) **********/\n\n"
+ content += "\n\t// MARK: - \(section.name)"
section.definitions.forEach { definition in
- let translationOpt: String? = {
- if definition.tags.contains(Stringium.noTranslationTag) {
- return definition.translations[defaultLang]
- }
- return definition.translations[lang]
- }()
-
- if let translation = translationOpt {
- stringsFileContent += "\"\(definition.name)\" = \"\(translation)\"\n\n"
+ if staticVar {
+ content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
} else {
- let error = StringiumError.langNotDefined(lang, definition.name, definition.reference != nil)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
+ content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
}
}
+ content += "\n"
}
-
- return stringsFileContent
+ return content
+ }()
+
+ // Create file if not exists
+ let fileManager = FileManager()
+ if fileManager.fileExists(atPath: extensionFilePath) == false {
+ Shell.shell("touch", "\(extensionFilePath)")
}
- // MARK: - Extension file
+ // Create extension content
+ let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
- static func writeExtensionFiles(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool, inputFilename: String, extensionName: String, extensionFilePath: String) {
- let extensionHeader = Self.getHeader(stringsFilename: inputFilename, extensionClassname: extensionName)
- let extensionFooter = Self.getFooter()
-
- let extensionContent: String = {
- var content = ""
- sections.forEach { section in
- // Check that at least one string will be generated
- guard section.hasOneOrMoreMatchingTags(tags: tags) else {
- return // Go to next section
- }
-
- content += "\n\t// MARK: - \(section.name)"
- section.definitions.forEach { definition in
- if staticVar {
- content += "\n\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
- } else {
- content += "\n\n\(definition.getNSLocalizedStringProperty(forLang: lang))"
- }
- }
- content += "\n"
- }
- return content
- }()
-
- // Create file if not exists
- let fileManager = FileManager()
- if fileManager.fileExists(atPath: extensionFilePath) == false {
- Shell.shell("touch", "\(extensionFilePath)")
- }
-
- // Create extension content
- let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
-
- // Write content
- let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
- do {
- try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
- } catch (let error) {
- let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
+ // Write content
+ let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
+ do {
+ try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
+ } catch (let error) {
+ let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
-
- private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
+ }
+
+ private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
"""
// Generated from Strings-Stringium at \(Date())
@@ -132,12 +131,11 @@ extension Strings {
extension \(extensionClassname) {
"""
- }
-
- private static func getFooter() -> String {
+ }
+
+ private static func getFooter() -> String {
"""
}
"""
- }
}
}
diff --git a/Sources/Strings/Generator/TagsGenerator.swift b/Sources/Strings/Generator/TagsGenerator.swift
index 925c1d8..bb7e1f1 100644
--- a/Sources/Strings/Generator/TagsGenerator.swift
+++ b/Sources/Strings/Generator/TagsGenerator.swift
@@ -9,54 +9,53 @@ import Foundation
import CLIToolCore
import CoreVideo
-extension Strings {
- class TagsGenerator {
- static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
- let extensionHeader = Self.getHeader(extensionClassname: extensionName)
- let extensionFooter = Self.getFooter()
-
- let extensionContent: String = {
- var content = ""
- sections.forEach { section in
- // Check that at least one string will be generated
- guard section.hasOneOrMoreMatchingTags(tags: tags) else {
- return // Go to next section
- }
-
- content += "\n\t// MARK: - \(section.name)"
- section.definitions.forEach { definition in
- if staticVar {
- content += "\n\n\(definition.getStaticProperty(forLang: lang))"
- } else {
- content += "\n\n\(definition.getProperty(forLang: lang))"
- }
- }
- content += "\n"
+class TagsGenerator {
+ static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
+ let extensionHeader = Self.getHeader(extensionClassname: extensionName)
+ let extensionFooter = Self.getFooter()
+
+ let extensionContent: String = {
+ var content = ""
+ sections.forEach { section in
+ // Check that at least one string will be generated
+ guard section.hasOneOrMoreMatchingTags(tags: tags) else {
+ return // Go to next section
}
- return content
- }()
-
- // Create file if not exists
- let fileManager = FileManager()
- if fileManager.fileExists(atPath: extensionFilePath) == false {
- Shell.shell("touch", "\(extensionFilePath)")
- }
-
- // Create extension content
- let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
-
- // Write content
- let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
- do {
- try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
- } catch (let error) {
- let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
+
+ content += "\n\t// MARK: - \(section.name)"
+ section.definitions.forEach { definition in
+ if staticVar {
+ content += "\n\n\(definition.getStaticProperty(forLang: lang))"
+ } else {
+ content += "\n\n\(definition.getProperty(forLang: lang))"
+ }
+ }
+ content += "\n"
}
+ return content
+ }()
+
+ // Create file if not exists
+ let fileManager = FileManager()
+ if fileManager.fileExists(atPath: extensionFilePath) == false {
+ Shell.shell("touch", "\(extensionFilePath)")
}
- private static func getHeader(extensionClassname: String) -> String {
+ // Create extension content
+ let extensionFileContent = [extensionHeader, extensionContent, extensionFooter].joined(separator: "\n")
+
+ // Write content
+ let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
+ do {
+ try extensionFileContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
+ } catch (let error) {
+ let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
+ }
+ }
+
+ private static func getHeader(extensionClassname: String) -> String {
"""
// Generated from Strings-Tags at \(Date())
@@ -66,12 +65,11 @@ extension Strings {
extension \(extensionClassname) {
"""
- }
-
- private static func getFooter() -> String {
+ }
+
+ private static func getFooter() -> String {
"""
}
"""
- }
}
}
diff --git a/Sources/Strings/Model/Definition.swift b/Sources/Strings/Model/Definition.swift
index b556dcc..e5fd0e4 100644
--- a/Sources/Strings/Model/Definition.swift
+++ b/Sources/Strings/Model/Definition.swift
@@ -7,102 +7,100 @@
import Foundation
-extension Strings {
- class Definition {
- let name: String
- var tags = [String]()
- var comment: String?
- var translations = [String: String]()
- var reference: String?
- var isPlurals = false
-
- var isValid: Bool {
- name.isEmpty == false &&
- translations.isEmpty == false
+class Definition {
+ let name: String
+ var tags = [String]()
+ var comment: String?
+ var translations = [String: String]()
+ var reference: String?
+ var isPlurals = false
+
+ var isValid: Bool {
+ name.isEmpty == false &&
+ translations.isEmpty == false
+ }
+
+ init(name: String) {
+ self.name = name
+ }
+
+ static func match(_ line: String) -> Definition? {
+ guard line.range(of: "\\[(.*?)]$", options: .regularExpression, range: nil, locale: nil) != nil else {
+ return nil
}
- init(name: String) {
- self.name = name
+ let definitionName = line
+ .replacingOccurrences(of: ["[", "]"], with: "")
+ .removeLeadingTrailingWhitespace()
+
+ return Definition(name: definitionName)
+ }
+
+ // MARK: -
+
+ func getNSLocalizedStringProperty(forLang lang: String) -> String {
+ guard let translation = translations[lang] else {
+ let error = StringiumError.langNotDefined(lang, name, reference != nil)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- static func match(_ line: String) -> Definition? {
- guard line.range(of: "\\[(.*?)]", options: .regularExpression, range: nil, locale: nil) != nil else {
- return nil
- }
-
- let definitionName = line
- .replacingOccurrences(of: ["[", "]"], with: "")
- .removeLeadingTrailingWhitespace()
-
- return Definition(name: definitionName)
- }
-
- // MARK: -
-
- func getNSLocalizedStringProperty(forLang lang: String) -> String {
- guard let translation = translations[lang] else {
- let error = StringiumError.langNotDefined(lang, name, reference != nil)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- return """
+ return """
/// Translation in \(lang) :
/// \(translation)
var \(name): String {
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
}
"""
+ }
+
+ func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
+ guard let translation = translations[lang] else {
+ let error = StringiumError.langNotDefined(lang, name, reference != nil)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- func getNSLocalizedStringStaticProperty(forLang lang: String) -> String {
- guard let translation = translations[lang] else {
- let error = StringiumError.langNotDefined(lang, name, reference != nil)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- return """
+ return """
/// Translation in \(lang) :
/// \(translation)
static var \(name): String {
NSLocalizedString("\(name)", tableName: kStringsFileName, bundle: Bundle.main, value: "\(translation)", comment: "")
}
"""
+ }
+
+ // MARK: - Raw strings
+
+ func getProperty(forLang lang: String) -> String {
+ guard let translation = translations[lang] else {
+ let error = StringiumError.langNotDefined(lang, name, reference != nil)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- // MARK: - Raw strings
-
- func getProperty(forLang lang: String) -> String {
- guard let translation = translations[lang] else {
- let error = StringiumError.langNotDefined(lang, name, reference != nil)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- return """
+ return """
/// Translation in \(lang) :
/// \(translation)
var \(name): String {
"\(translation)"
}
"""
+ }
+
+ func getStaticProperty(forLang lang: String) -> String {
+ guard let translation = translations[lang] else {
+ let error = StringiumError.langNotDefined(lang, name, reference != nil)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- func getStaticProperty(forLang lang: String) -> String {
- guard let translation = translations[lang] else {
- let error = StringiumError.langNotDefined(lang, name, reference != nil)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- return """
+ return """
/// Translation in \(lang) :
/// \(translation)
static var \(name): String {
"\(translation)"
}
"""
- }
}
}
diff --git a/Sources/Strings/Model/Section.swift b/Sources/Strings/Model/Section.swift
index 2d598b5..5c8cee1 100644
--- a/Sources/Strings/Model/Section.swift
+++ b/Sources/Strings/Model/Section.swift
@@ -7,35 +7,33 @@
import Foundation
-extension Strings {
- class Section {
- let name: String // OnBoarding
- var definitions = [Definition]()
-
- init(name: String) {
- self.name = name
+class Section {
+ let name: String // OnBoarding
+ var definitions = [Definition]()
+
+ init(name: String) {
+ self.name = name
+ }
+
+ static func match(_ line: String) -> Section? {
+ guard line.range(of: "\\[\\[(.*?)]]$", options: .regularExpression, range: nil, locale: nil) != nil else {
+ return nil
}
- static func match(_ line: String) -> Section? {
- guard line.range(of: "\\[\\[(.*?)]]", options: .regularExpression, range: nil, locale: nil) != nil else {
- return nil
+ let sectionName = line
+ .replacingOccurrences(of: ["[", "]"], with: "")
+ .removeLeadingTrailingWhitespace()
+ return Section(name: sectionName)
+ }
+
+ func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
+ let allTags = definitions.flatMap { $0.tags }
+
+ for tag in tags {
+ if allTags.contains(tag) {
+ return true
}
-
- let sectionName = line
- .replacingOccurrences(of: ["[", "]"], with: "")
- .removeLeadingTrailingWhitespace()
- return Section(name: sectionName)
- }
-
- func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
- let allTags = definitions.flatMap { $0.tags }
-
- for tag in tags {
- if allTags.contains(tag) {
- return true
- }
- }
- return false
}
+ return false
}
}
diff --git a/Sources/Strings/Parser/TwineFileParser.swift b/Sources/Strings/Parser/TwineFileParser.swift
index cd075e1..0c56b66 100644
--- a/Sources/Strings/Parser/TwineFileParser.swift
+++ b/Sources/Strings/Parser/TwineFileParser.swift
@@ -7,88 +7,91 @@
import Foundation
-extension Strings {
-
- class TwineFileParser {
- static func parse(_ inputFile: String) -> [Section] {
- let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
- let stringsByLines = inputFileContent.components(separatedBy: .newlines)
+class TwineFileParser {
+ static func parse(_ inputFile: String) -> [Section] {
+ let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
+ let stringsByLines = inputFileContent.components(separatedBy: .newlines)
+
+ var sections = [Section]()
+
+ // Parse file
+ stringsByLines.forEach {
+ // Section
+ if let section = Section.match($0) {
+ sections.append(section)
+ return
+ }
- var sections = [Section]()
+ // Definition
+ if let definition = Definition.match($0) {
+ sections.last?.definitions.append(definition)
+ return
+ }
- // Parse file
- stringsByLines.forEach {
- // Section
- if let section = Section.match($0) {
- sections.append(section)
- return
- }
+ // Definition content
+ if $0.isEmpty == false {
+ // fr = Test => ["fr ", " Test"]
+ let splitLine = $0
+ .removeLeadingTrailingWhitespace()
+ .split(separator: "=")
- // Definition
- if let definition = Definition.match($0) {
- sections.last?.definitions.append(definition)
- return
- }
+ guard let lastDefinition = sections.last?.definitions.last,
+ let leftElement = splitLine.first else {
+ return
+ }
- // Definition content
- if $0.isEmpty == false {
- // fr = Test => ["fr ", " Test"]
- let splitLine = $0
- .removeLeadingTrailingWhitespace()
- .split(separator: "=")
-
- guard let lastDefinition = sections.last?.definitions.last,
- let leftElement = splitLine.first,
- let rightElement = splitLine.last else {
- return
- }
-
- // "fr " => "fr"
- let leftHand = String(leftElement.dropLast())
- // " Test" => "Test"
- let rightHand = String(rightElement.dropFirst())
-
- // Handle comments, tags and translation
- switch leftHand {
- case "comments":
- lastDefinition.comment = rightHand
-
- case "tags":
- lastDefinition.tags = rightHand
- .split(separator: ",")
- .map { String($0) }
-
- case "ref":
- lastDefinition.reference = rightHand
-
- default:
- lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
- // Is a plurals strings (fr:one = Test)
- // Will be handle later
- //if leftHand.split(separator: ":").count > 1 {
- // lastDefinition.isPlurals = true
- //}
+ let rightElement: String = {
+ if let last = splitLine.last, splitLine.count == 2 {
+ return String(last)
}
+ return ""
+ }()
+
+ // "fr " => "fr"
+ let leftHand = String(leftElement).removeTrailingWhitespace()
+ // " Test" => "Test"
+ let rightHand = String(rightElement).removeLeadingWhitespace()
+
+ // Handle comments, tags and translation
+ switch leftHand {
+ case "comments":
+ lastDefinition.comment = rightHand
+
+ case "tags":
+ lastDefinition.tags = rightHand
+ .split(separator: ",")
+ .map { String($0) }
+
+ case "ref":
+ lastDefinition.reference = rightHand
+
+ default:
+ lastDefinition.translations[leftHand] = rightHand.escapeDoubleQuote()
+ // Is a plurals strings (fr:one = Test)
+ // Will be handle later
+ //if leftHand.split(separator: ":").count > 1 {
+ // lastDefinition.isPlurals = true
+ //}
}
}
-
- // Keep only valid definition
- var invalidDefinitionNames = [String]()
- sections.forEach { section in
- section.definitions = section.definitions
- .filter {
- if $0.isValid == false {
- invalidDefinitionNames.append($0.name)
- return false
- }
- return true
- }
- }
- if invalidDefinitionNames.count > 0 {
- print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
- }
-
- return sections
}
+
+ // Keep only valid definition
+ var invalidDefinitionNames = [String]()
+ sections.forEach { section in
+ section.definitions = section.definitions
+ .filter {
+ if $0.isValid == false {
+ invalidDefinitionNames.append($0.name)
+ return false
+ }
+ return true
+ }
+ }
+ if invalidDefinitionNames.count > 0 {
+ print(" warning:[\(Stringium.toolName)] Found \(invalidDefinitionNames.count) definition (\(invalidDefinitionNames.joined(separator: ", "))")
+ }
+
+ return sections
}
}
diff --git a/Sources/Strings/Stringium/Stringium.swift b/Sources/Strings/Stringium/Stringium.swift
index 3b6c5b9..1746aa2 100644
--- a/Sources/Strings/Stringium/Stringium.swift
+++ b/Sources/Strings/Stringium/Stringium.swift
@@ -9,103 +9,100 @@ import Foundation
import CLIToolCore
import ArgumentParser
-extension Strings {
+struct Stringium: ParsableCommand {
+ static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
- struct Stringium: ParsableCommand {
- static var configuration = CommandConfiguration(abstract: "Generate strings with custom scripts.")
-
- static let toolName = "Stringium"
- static let defaultExtensionName = "String"
- static let noTranslationTag: String = "notranslation"
-
- var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).swift" }
- var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
-
- var langs: [String] {
- options.langsRaw
- .split(separator: " ")
- .map { String($0) }
+ static let toolName = "Stringium"
+ static let defaultExtensionName = "String"
+ static let noTranslationTag: String = "notranslation"
+
+ var extensionFileName: String { "\(options.extensionName)+String\(options.extensionSuffix).swift" }
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
+
+ var langs: [String] {
+ options.langsRaw
+ .split(separator: " ")
+ .map { String($0) }
+ }
+ var inputFilenameWithoutExt: String {
+ URL(fileURLWithPath: options.inputFile)
+ .deletingPathExtension()
+ .lastPathComponent
+ }
+ var stringsFileOutputPath: String {
+ var outputPath = options.outputPathRaw
+ if outputPath.last == "/" {
+ outputPath = String(outputPath.dropLast())
}
- var inputFilenameWithoutExt: String {
- URL(fileURLWithPath: options.inputFile)
- .deletingPathExtension()
- .lastPathComponent
- }
- var stringsFileOutputPath: String {
- var outputPath = options.outputPathRaw
- if outputPath.last == "/" {
- outputPath = String(outputPath.dropLast())
- }
- return outputPath
+ return outputPath
+ }
+
+ // The `@OptionGroup` attribute includes the flags, options, and
+ // arguments defined by another `ParsableArguments` type.
+ @OptionGroup var options: StringiumOptions
+
+ mutating func run() {
+ print("[\(Self.toolName)] Starting strings generation")
+
+ // Check requirements
+ guard checkRequirements() else { return }
+
+ print("[\(Self.toolName)] Will generate strings")
+
+ // Parse input file
+ let sections = TwineFileParser.parse(options.inputFile)
+
+ // Generate strings files
+ StringsFileGenerator.writeStringsFiles(sections: sections,
+ langs: langs,
+ defaultLang: options.defaultLang,
+ tags: ["ios", "iosonly", Self.noTranslationTag],
+ outputPath: stringsFileOutputPath,
+ inputFilenameWithoutExt: inputFilenameWithoutExt)
+
+ // Generate extension
+ StringsFileGenerator.writeExtensionFiles(sections: sections,
+ defaultLang: options.defaultLang,
+ tags: ["ios", "iosonly", Self.noTranslationTag],
+ staticVar: options.extensionName == Self.defaultExtensionName,
+ inputFilename: inputFilenameWithoutExt,
+ extensionName: options.extensionName,
+ extensionFilePath: extensionFilePath)
+
+ print("[\(Self.toolName)] Strings generated")
+ }
+
+ // MARK: - Requirements
+
+ private func checkRequirements() -> Bool {
+ let fileManager = FileManager()
+
+ // Input file
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = StringiumError.fileNotExists(options.inputFile)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- // The `@OptionGroup` attribute includes the flags, options, and
- // arguments defined by another `ParsableArguments` type.
- @OptionGroup var options: StringiumOptions
-
- mutating func run() {
- print("[\(Self.toolName)] Starting strings generation")
-
- // Check requirements
- guard checkRequirements() else { return }
-
- print("[\(Self.toolName)] Will generate strings")
-
- // Parse input file
- let sections = TwineFileParser.parse(options.inputFile)
-
- // Generate strings files
- StringsFileGenerator.writeStringsFiles(sections: sections,
- langs: langs,
- defaultLang: options.defaultLang,
- tags: ["ios", "iosonly", Self.noTranslationTag],
- outputPath: stringsFileOutputPath,
- inputFilenameWithoutExt: inputFilenameWithoutExt)
-
- // Generate extension
- StringsFileGenerator.writeExtensionFiles(sections: sections,
- defaultLang: options.defaultLang,
- tags: ["ios", "iosonly", Self.noTranslationTag],
- staticVar: options.extensionName == Self.defaultExtensionName,
- inputFilename: inputFilenameWithoutExt,
- extensionName: options.extensionName,
- extensionFilePath: extensionFilePath)
-
- print("[\(Self.toolName)] Strings generated")
+ // Langs
+ guard langs.isEmpty == false else {
+ let error = StringiumError.langsListEmpty
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- // MARK: - Requirements
-
- private func checkRequirements() -> Bool {
- let fileManager = FileManager()
-
- // Input file
- guard fileManager.fileExists(atPath: options.inputFile) else {
- let error = StringiumError.fileNotExists(options.inputFile)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- // Langs
- guard langs.isEmpty == false else {
- let error = StringiumError.langsListEmpty
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- guard langs.contains(options.defaultLang) else {
- let error = StringiumError.defaultLangsNotInLangs
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- // Check if needed to regenerate
- guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
- print("[\(Self.toolName)] Strings are already up to date :) ")
- return false
- }
-
- return true
+ guard langs.contains(options.defaultLang) else {
+ let error = StringiumError.defaultLangsNotInLangs
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
+
+ // Check if needed to regenerate
+ guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
+ print("[\(Self.toolName)] Strings are already up to date :) ")
+ return false
+ }
+
+ return true
}
}
diff --git a/Sources/Strings/Stringium/StringiumError.swift b/Sources/Strings/Stringium/StringiumError.swift
index 988ba9d..852d7ec 100644
--- a/Sources/Strings/Stringium/StringiumError.swift
+++ b/Sources/Strings/Stringium/StringiumError.swift
@@ -7,34 +7,32 @@
import Foundation
-extension Strings {
- enum StringiumError: Error {
- case fileNotExists(String)
- case langsListEmpty
- case defaultLangsNotInLangs
- case writeFile(String, String)
- case langNotDefined(String, String, Bool)
-
- var localizedDescription: String {
- switch self {
- case .fileNotExists(let filename):
- return " error:[\(Stringium.toolName)] File \(filename) does not exists "
-
- case .langsListEmpty:
- return " error:[\(Stringium.toolName)] Langs list is empty"
-
- case .defaultLangsNotInLangs:
- return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
-
- case .writeFile(let subErrorDescription, let filename):
- return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
-
- case .langNotDefined(let lang, let definitionName, let isReference):
- if isReference {
- return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
- }
- return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
+enum StringiumError: Error {
+ case fileNotExists(String)
+ case langsListEmpty
+ case defaultLangsNotInLangs
+ case writeFile(String, String)
+ case langNotDefined(String, String, Bool)
+
+ var localizedDescription: String {
+ switch self {
+ case .fileNotExists(let filename):
+ return " error:[\(Stringium.toolName)] File \(filename) does not exists "
+
+ case .langsListEmpty:
+ return " error:[\(Stringium.toolName)] Langs list is empty"
+
+ case .defaultLangsNotInLangs:
+ return " error:[\(Stringium.toolName)] Langs list does not contains the default lang"
+
+ case .writeFile(let subErrorDescription, let filename):
+ return " error:[\(Stringium.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
+
+ case .langNotDefined(let lang, let definitionName, let isReference):
+ if isReference {
+ return " error:[\(Stringium.toolName)] Reference are handled only by TwineTool. Please use it or remove reference from you strings file."
}
+ return " error:[\(Stringium.toolName)] Lang \"\(lang)\" not found for \"\(definitionName)\""
}
- }
+ }
}
diff --git a/Sources/Strings/Stringium/StringiumOptions.swift b/Sources/Strings/Stringium/StringiumOptions.swift
index 9d14ed1..b5b0130 100644
--- a/Sources/Strings/Stringium/StringiumOptions.swift
+++ b/Sources/Strings/Stringium/StringiumOptions.swift
@@ -8,30 +8,28 @@
import Foundation
import ArgumentParser
-extension Strings {
- struct StringiumOptions: ParsableArguments {
- @Flag(name: .customShort("f"), help: "Should force generation")
- var forceGeneration = false
-
- @Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var inputFile: String
-
- @Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var outputPathRaw: String
-
- @Option(name: .customLong("langs"), help: "Langs to generate.")
- var langsRaw: String
-
- @Option(help: "Default langs.")
- var defaultLang: String
-
- @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var extensionOutputPath: String
-
- @Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
- var extensionName: String = Stringium.defaultExtensionName
-
- @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+String{extensionSuffix}.swift")
- var extensionSuffix: String = ""
- }
+struct StringiumOptions: ParsableArguments {
+ @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
+ var forceGeneration = false
+
+ @Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var inputFile: String
+
+ @Option(name: .customLong("output-path"), help: "Path where to strings file.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var outputPathRaw: String
+
+ @Option(name: .customLong("langs"), help: "Langs to generate.")
+ var langsRaw: String
+
+ @Option(help: "Default langs.")
+ var defaultLang: String
+
+ @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var extensionOutputPath: String
+
+ @Option(help: "Extension name. If not specified, it will generate an String extension. Using default extension name will generate static property.")
+ var extensionName: String = Stringium.defaultExtensionName
+
+ @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+String{extensionSuffix}.swift")
+ var extensionSuffix: String = ""
}
diff --git a/Sources/Strings/Tag/Tags.swift b/Sources/Strings/Tag/Tags.swift
index efe3028..5caed2b 100644
--- a/Sources/Strings/Tag/Tags.swift
+++ b/Sources/Strings/Tag/Tags.swift
@@ -9,63 +9,60 @@ import Foundation
import CLIToolCore
import ArgumentParser
-extension Strings {
+struct Tags: ParsableCommand {
+ static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
- struct Tags: ParsableCommand {
- static var configuration = CommandConfiguration(abstract: "Generate tags extension file.")
+ static let toolName = "Tags"
+ static let defaultExtensionName = "Tags"
+ static let noTranslationTag: String = "notranslation"
+
+ var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
+
+ // The `@OptionGroup` attribute includes the flags, options, and
+ // arguments defined by another `ParsableArguments` type.
+ @OptionGroup var options: TagsOptions
+
+ mutating func run() {
+ print("[\(Self.toolName)] Starting tagss generation")
- static let toolName = "Tags"
- static let defaultExtensionName = "Tags"
- static let noTranslationTag: String = "notranslation"
+ // Check requirements
+ guard checkRequirements() else { return }
- var extensionFileName: String { "\(options.extensionName)+Tag\(options.extensionSuffix).swift" }
- var extensionFilePath: String { "\(options.extensionOutputPath)/\(extensionFileName)" }
+ print("[\(Self.toolName)] Will generate tags")
- // The `@OptionGroup` attribute includes the flags, options, and
- // arguments defined by another `ParsableArguments` type.
- @OptionGroup var options: TagsOptions
+ // Parse input file
+ let sections = TwineFileParser.parse(options.inputFile)
- mutating func run() {
- print("[\(Self.toolName)] Starting tagss generation")
-
- // Check requirements
- guard checkRequirements() else { return }
-
- print("[\(Self.toolName)] Will generate tags")
-
- // Parse input file
- let sections = TwineFileParser.parse(options.inputFile)
-
- // Generate extension
- TagsGenerator.writeExtensionFiles(sections: sections,
- lang: options.lang,
- tags: ["ios", "iosonly", Self.noTranslationTag],
- staticVar: options.extensionName == Self.defaultExtensionName,
- extensionName: options.extensionName,
- extensionFilePath: extensionFilePath)
-
- print("[\(Self.toolName)] Tags generated")
+ // Generate extension
+ TagsGenerator.writeExtensionFiles(sections: sections,
+ lang: options.lang,
+ tags: ["ios", "iosonly", Self.noTranslationTag],
+ staticVar: options.extensionName == Self.defaultExtensionName,
+ extensionName: options.extensionName,
+ extensionFilePath: extensionFilePath)
+
+ print("[\(Self.toolName)] Tags generated")
+ }
+
+ // MARK: - Requirements
+
+ private func checkRequirements() -> Bool {
+ let fileManager = FileManager()
+
+ // Input file
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = StringiumError.fileNotExists(options.inputFile)
+ print(error.localizedDescription)
+ Stringium.exit(withError: error)
}
- // MARK: - Requirements
-
- private func checkRequirements() -> Bool {
- let fileManager = FileManager()
-
- // Input file
- guard fileManager.fileExists(atPath: options.inputFile) else {
- let error = StringiumError.fileNotExists(options.inputFile)
- print(error.localizedDescription)
- Stringium.exit(withError: error)
- }
-
- // Check if needed to regenerate
- guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
- print("[\(Self.toolName)] Tags are already up to date :) ")
- return false
- }
-
- return true
+ // Check if needed to regenerate
+ guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePath) else {
+ print("[\(Self.toolName)] Tags are already up to date :) ")
+ return false
}
+
+ return true
}
}
diff --git a/Sources/Strings/Tag/TagsOptions.swift b/Sources/Strings/Tag/TagsOptions.swift
index 527ea57..8cd82cf 100644
--- a/Sources/Strings/Tag/TagsOptions.swift
+++ b/Sources/Strings/Tag/TagsOptions.swift
@@ -8,24 +8,22 @@
import Foundation
import ArgumentParser
-extension Strings {
- struct TagsOptions: ParsableArguments {
- @Flag(name: .customShort("f"), help: "Should force generation")
- var forceGeneration = false
-
- @Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var inputFile: String
-
- @Option(help: "Lang to generate. (\"ium\" by default)")
- var lang: String = "ium"
-
- @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
- var extensionOutputPath: String
-
- @Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
- var extensionName: String = Tags.defaultExtensionName
-
- @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
- var extensionSuffix: String = ""
- }
+struct TagsOptions: ParsableArguments {
+ @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
+ var forceGeneration = false
+
+ @Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var inputFile: String
+
+ @Option(help: "Lang to generate. (\"ium\" by default)")
+ var lang: String = "ium"
+
+ @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
+ var extensionOutputPath: String
+
+ @Option(help: "Extension name. If not specified, it will generate a Tag extension. Using default extension name will generate static property.")
+ var extensionName: String = Tags.defaultExtensionName
+
+ @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift")
+ var extensionSuffix: String = ""
}
diff --git a/Sources/Strings/Twine/Twine.swift b/Sources/Strings/Twine/Twine.swift
index 74f89cc..0b4e9ca 100644
--- a/Sources/Strings/Twine/Twine.swift
+++ b/Sources/Strings/Twine/Twine.swift
@@ -9,89 +9,86 @@ import Foundation
import CLIToolCore
import ArgumentParser
-extension Strings {
+struct Twine: ParsableCommand {
+ static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
- struct Twine: ParsableCommand {
- static var configuration = CommandConfiguration(abstract: "Generate strings with twine.")
+ static let toolName = "Twine"
+ static let defaultExtensionName = "String"
+ static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
+
+ var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } }
+ var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
+ .deletingPathExtension()
+ .lastPathComponent
+ }
+
+ // The `@OptionGroup` attribute includes the flags, options, and
+ // arguments defined by another `ParsableArguments` type.
+ @OptionGroup var options: TwineOptions
+
+ mutating func run() {
+ print("[\(Self.toolName)] Starting strings generation")
- static let toolName = "Twine"
- static let defaultExtensionName = "String"
- static let twineExecutable = "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)/scripts/twine/twine"
+ // Check requirements
+ guard checkRequirements() else { return }
- var langs: [String] { options.langsRaw.split(separator: " ").map { String($0) } }
- var inputFilenameWithoutExt: String { URL(fileURLWithPath: options.inputFile)
- .deletingPathExtension()
- .lastPathComponent
- }
+ print("[\(Self.toolName)] Will generate strings")
- // The `@OptionGroup` attribute includes the flags, options, and
- // arguments defined by another `ParsableArguments` type.
- @OptionGroup var options: TwineOptions
-
- mutating func run() {
- print("[\(Self.toolName)] Starting strings generation")
-
- // Check requirements
- guard checkRequirements() else { return }
-
- print("[\(Self.toolName)] Will generate strings")
-
- // Generate strings files (lproj files)
- for lang in langs {
- Shell.shell(Self.twineExecutable,
- "generate-localization-file", options.inputFile,
- "--lang", "\(lang)",
- "\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
- "--tags=ios,iosonly,iosOnly")
- }
-
- // Generate extension
- var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
+ // Generate strings files (lproj files)
+ for lang in langs {
Shell.shell(Self.twineExecutable,
"generate-localization-file", options.inputFile,
- "--format", "apple-swift",
- "--lang", "\(options.defaultLang)",
- extensionFilePath,
+ "--lang", "\(lang)",
+ "\(options.outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings",
"--tags=ios,iosonly,iosOnly")
-
- print("[\(Self.toolName)] Strings generated")
}
- // MARK: - Requirements
+ // Generate extension
+ var extensionFilePath: String { "\(options.extensionOutputPath)/\(inputFilenameWithoutExt).swift" }
+ Shell.shell(Self.twineExecutable,
+ "generate-localization-file", options.inputFile,
+ "--format", "apple-swift",
+ "--lang", "\(options.defaultLang)",
+ extensionFilePath,
+ "--tags=ios,iosonly,iosOnly")
- private func checkRequirements() -> Bool {
- let fileManager = FileManager()
-
- // Input file
- guard fileManager.fileExists(atPath: options.inputFile) else {
- let error = TwineError.fileNotExists(options.inputFile)
- print(error.localizedDescription)
- Twine.exit(withError: error)
- }
-
- // Langs
- guard langs.isEmpty == false else {
- let error = TwineError.langsListEmpty
- print(error.localizedDescription)
- Twine.exit(withError: error)
- }
-
- guard langs.contains(options.defaultLang) else {
- let error = TwineError.defaultLangsNotInLangs
- print(error.localizedDescription)
- Twine.exit(withError: error)
- }
-
- // "R2String+" is hardcoded in Twine formatter
- let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
-
- // Check if needed to regenerate
- guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
- print("[\(Self.toolName)] Strings are already up to date :) ")
- return false
- }
-
- return true
+ print("[\(Self.toolName)] Strings generated")
+ }
+
+ // MARK: - Requirements
+
+ private func checkRequirements() -> Bool {
+ let fileManager = FileManager()
+
+ // Input file
+ guard fileManager.fileExists(atPath: options.inputFile) else {
+ let error = TwineError.fileNotExists(options.inputFile)
+ print(error.localizedDescription)
+ Twine.exit(withError: error)
}
+
+ // Langs
+ guard langs.isEmpty == false else {
+ let error = TwineError.langsListEmpty
+ print(error.localizedDescription)
+ Twine.exit(withError: error)
+ }
+
+ guard langs.contains(options.defaultLang) else {
+ let error = TwineError.defaultLangsNotInLangs
+ print(error.localizedDescription)
+ Twine.exit(withError: error)
+ }
+
+ // "R2String+" is hardcoded in Twine formatter
+ let extensionFilePathGenerated = "\(options.extensionOutputPath)/R2String+\(inputFilenameWithoutExt).swift"
+
+ // Check if needed to regenerate
+ guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, inputFilePath: options.inputFile, extensionFilePath: extensionFilePathGenerated) else {
+ print("[\(Self.toolName)] Strings are already up to date :) ")
+ return false
+ }
+
+ return true
}
}
diff --git a/Sources/Strings/Twine/TwineError.swift b/Sources/Strings/Twine/TwineError.swift
index 2f0c7af..291f371 100644
--- a/Sources/Strings/Twine/TwineError.swift
+++ b/Sources/Strings/Twine/TwineError.swift
@@ -7,25 +7,21 @@
import Foundation
-extension Strings {
+enum TwineError: Error {
+ case fileNotExists(String)
+ case langsListEmpty
+ case defaultLangsNotInLangs
- enum TwineError: Error {
- case fileNotExists(String)
- case langsListEmpty
- case defaultLangsNotInLangs
-
- var localizedDescription: String {
- switch self {
- case .fileNotExists(let filename):
- return " error:[\(Twine.toolName)] File \(filename) does not exists "
-
- case .langsListEmpty:
- return " error:[\(Twine.toolName)] Langs list is empty"
-
- case .defaultLangsNotInLangs:
- return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
- }
+ var localizedDescription: String {
+ switch self {
+ case .fileNotExists(let filename):
+ return " error:[\(Twine.toolName)] File \(filename) does not exists "
+
+ case .langsListEmpty:
+ return " error:[\(Twine.toolName)] Langs list is empty"
+
+ case .defaultLangsNotInLangs:
+ return " error:[\(Twine.toolName)] Langs list does not contains the default lang"
}
}
-
}
diff --git a/Sources/Strings/Twine/TwineOptions.swift b/Sources/Strings/Twine/TwineOptions.swift
index 167f956..4d1d559 100644
--- a/Sources/Strings/Twine/TwineOptions.swift
+++ b/Sources/Strings/Twine/TwineOptions.swift
@@ -9,7 +9,7 @@ import Foundation
import ArgumentParser
struct TwineOptions: ParsableArguments {
- @Flag(name: .customShort("f"), help: "Should force generation")
+ @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
var forceGeneration = false
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
diff --git a/Sources/Strings/main.swift b/Sources/Strings/main.swift
index 9b70151..d88f21e 100644
--- a/Sources/Strings/main.swift
+++ b/Sources/Strings/main.swift
@@ -14,7 +14,7 @@ struct Strings: ParsableCommand {
abstract: "A utility for generate strings.",
version: "0.1.0",
- // Pass an array to `subcommands` to set up a nested tree of subcommands.
+ // Pass an array to `subcommands` to set up a nested tree of subcommands.
// With language support for type-level introspection, this could be
// provided by automatically finding nested `ParsableCommand` types.
subcommands: [Twine.self, Stringium.self, Tags.self]
@@ -26,3 +26,4 @@ struct Strings: ParsableCommand {
}
Strings.main()
+