From ca763cd5d0d6226c03eb02f8c1f93de0c70f8f6a Mon Sep 17 00:00:00 2001 From: Loris Perret Date: Fri, 8 Dec 2023 11:29:48 +0100 Subject: [PATCH] fix: Rebase tags --- .../Tags/Generated/Tags+GenAllScript.swift | 125 ++---------------- SampleFiles/Tags/sampleTags.txt | 10 +- SampleFiles/genAllRessources.sh | 11 +- SampleFiles/resgenConfiguration.yml | 16 ++- Sources/ResgenSwift/Generate/Generate.swift | 1 + .../ResgenSwift/Generate/GenerateError.swift | 11 ++ .../Generate/Model/ConfigurationFile.swift | 55 +++++++- .../Runnable/TagsConfiguration+Runnable.swift | 4 +- .../Strings/Generator/TagsGenerator.swift | 86 ++++++++++++ Sources/ResgenSwift/Strings/Tag/Tags.swift | 79 +++++++++++ .../ResgenSwift/Strings/Tag/TagsOptions.swift | 47 +++++++ Sources/ResgenSwift/main.swift | 2 + .../Strings/TagsGeneratorTests.swift | 82 ++++++++++++ 13 files changed, 396 insertions(+), 133 deletions(-) create mode 100644 Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift create mode 100644 Sources/ResgenSwift/Strings/Tag/Tags.swift create mode 100644 Sources/ResgenSwift/Strings/Tag/TagsOptions.swift create mode 100644 Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift diff --git a/SampleFiles/Tags/Generated/Tags+GenAllScript.swift b/SampleFiles/Tags/Generated/Tags+GenAllScript.swift index e906ace..45fc4ea 100644 --- a/SampleFiles/Tags/Generated/Tags+GenAllScript.swift +++ b/SampleFiles/Tags/Generated/Tags+GenAllScript.swift @@ -1,125 +1,20 @@ -// Generated by ResgenSwift.Tags 1.2 +// Generated by ResgenSwift.Strings.Tags 1.2 import UIKit -import MatomoTracker -import Firebase -// MARK: - Protocol - -protocol AnalyticsManagerProtocol { - func logScreen(name: String, path: String) - func logEvent(name: String) -} - -// MARK: - Matomo - -class MatomoAnalyticsManager: AnalyticsManagerProtocol { - - // MARK: - Properties - - private var tracker: MatomoTracker - - // MARK: - Init - - init(siteId: String, url: String) { - debugPrint("[Matomo service] Server URL: \(url)") - debugPrint("[Matomo service] Site ID: \(siteId)") - tracker = MatomoTracker(siteId: siteId, baseURL: URL(string: url)!) - - #if DEBUG - tracker.dispatchInterval = 5 - #endif - - #if DEBUG - tracker.logger = DefaultLogger(minLevel: .verbose) - #endif - - debugPrint("[Matomo service] Configured with content base: \(tracker.contentBase?.absoluteString ?? "-")") - debugPrint("[Matomo service] Opt out: \(tracker.isOptedOut)") - } - - // MARK: - Methods - - func logScreen(name: String, path: String) { - guard !tracker.isOptedOut else { return } - guard let trackerUrl = tracker.contentBase?.absoluteString else { return } - let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS") - tracker.track( - view: [name], - url: urlString - ) - } - - func logEvent(name: String) { - guard !tracker.isOptedOut else { return } - tracker.track( - eventWithCategory: "category", - action: "action", - name: name, - number: nil, - url: nil - ) - } -} - -// MARK: - Firebase - -class FirebaseAnalyticsManager: AnalyticsManagerProtocol { - func logScreen(name: String, path: String) { - Analytics.logEvent(AnalyticsEventScreenView, parameters: [AnalyticsParameterScreenName: name]) - } - - func logEvent(name: String) { - var parameters = [ - AnalyticsParameterValue: name - ] - - Analytics.logEvent(AnalyticsEventSelectContent, parameters: parameters) - } -} - -// MARK: - Manager - -class AnalyticsManager { - static var shared = AnalyticsManager() - - // MARK: - Properties - - var managers: [AnalyticsManagerProtocol] = [] - - private var isEnabled: Bool = true - - // MARK: - Methods - - func setAnalyticsEnabled(_ enable: Bool) { isEnabled = enable } - - func configure(sideId: String, url: String) { - managers.append(MatomoAnalyticsManager(siteId: sideId, url: url)) - - managers.append(FirebaseAnalyticsManager()) - } - - private func logScreen(name: String, path: String) { - guard isEnabled else { return } - managers.forEach { manager in - manager.logScreen(name: name, path: path) - } - } - - private func logEvent(name: String) { - guard isEnabled else { return } - managers.forEach { manager in - manager.logEvent(name: name) - } - } +extension Tags { // MARK: - ScreenTag - func logScreenEcranUn() { - logScreen(name: "Ecran un", path: "ecran_un/") + /// Translation in ium : + /// Ecran un + var screen_one: String { + "Ecran un" } - func logEventEcranDeux() { - logEvent(name: "Ecran deux") + /// Translation in ium : + /// Ecran deux + var screen_two: String { + "Ecran deux" } } diff --git a/SampleFiles/Tags/sampleTags.txt b/SampleFiles/Tags/sampleTags.txt index 5ace993..98f67d4 100644 --- a/SampleFiles/Tags/sampleTags.txt +++ b/SampleFiles/Tags/sampleTags.txt @@ -1,13 +1,7 @@ [[ScreenTag]] [screen_one] - path = ecran_un/ - name = Ecran un - type = screen + ium = Ecran un tags = droid,ios - comments = [screen_two] - path = ecran_deux/ - name = Ecran deux - type = event + ium = Ecran deux tags = droid,ios - comments = diff --git a/SampleFiles/genAllRessources.sh b/SampleFiles/genAllRessources.sh index ddab6b1..bd96019 100755 --- a/SampleFiles/genAllRessources.sh +++ b/SampleFiles/genAllRessources.sh @@ -45,10 +45,19 @@ echo "\n-------------------------\n" ## Tags swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \ - --target "matomo firebase" \ + --lang "ium" \ --extension-output-path "./Tags/Generated" \ --extension-name "Tags" \ --extension-suffix "GenAllScript" +# +#echo "\n-------------------------\n" + +## Analytics +#swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \ +# --target "matomo firebase" \ +# --extension-output-path "./Tags/Generated" \ +# --extension-name "Analytics" \ +# --extension-suffix "GenAllScript" echo "\n-------------------------\n" # diff --git a/SampleFiles/resgenConfiguration.yml b/SampleFiles/resgenConfiguration.yml index fbc512a..512fc67 100644 --- a/SampleFiles/resgenConfiguration.yml +++ b/SampleFiles/resgenConfiguration.yml @@ -70,14 +70,26 @@ colors: # Tags # tags: -- +- inputFile: ./Tags/sampleTags.txt - target: "matomo firebase" + lang: ium extensionOutputPath: ./Tags/Generated extensionName: Tags extensionSuffix: GenAllScript +# +# Analytics +# +analytics: +- + inputFile: ./Tags/sampleTags.yml + target: "matomo firebase" + extensionOutputPath: ./Tags/Generated + extensionName: Analytics + extensionSuffix: GenAllScript + + # # Fonts # diff --git a/Sources/ResgenSwift/Generate/Generate.swift b/Sources/ResgenSwift/Generate/Generate.swift index 4ac6685..a051046 100644 --- a/Sources/ResgenSwift/Generate/Generate.swift +++ b/Sources/ResgenSwift/Generate/Generate.swift @@ -34,6 +34,7 @@ struct Generate: ParsableCommand { // Parse let configuration = ConfigurationFileParser.parse(options.configurationFile) print("Found configurations :") + print(" - \(configuration.analytics.count) analytics configuration(s)") print(" - \(configuration.colors.count) colors configuration(s)") print(" - \(configuration.fonts.count) fonts configuration(s)") print(" - \(configuration.images.count) images configuration(s)") diff --git a/Sources/ResgenSwift/Generate/GenerateError.swift b/Sources/ResgenSwift/Generate/GenerateError.swift index 2ebc045..e6bab8c 100644 --- a/Sources/ResgenSwift/Generate/GenerateError.swift +++ b/Sources/ResgenSwift/Generate/GenerateError.swift @@ -13,6 +13,11 @@ enum GenerateError: Error { case commandError([String], String) case writeFile(String, String) + // Analytics + + case missingElement(String) + case invalidParameter(String) + var description: String { switch self { case .fileNotExists(let filename): @@ -29,6 +34,12 @@ enum GenerateError: Error { case .writeFile(let filename, let info): return "error: [\(Generate.toolName)] An error occured while writing file in \(filename): \(info)" + + case .missingElement(let element): + return "error: [\(Generate.toolName)] Missing \(element) for Matomo" + + case .invalidParameter(let reason): + return "error: [\(Generate.toolName)] Invalid parameter \(reason)" } } } diff --git a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift index 47ddb79..a631426 100644 --- a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift +++ b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift @@ -9,6 +9,7 @@ import Foundation struct ConfigurationFile: Codable, CustomDebugStringConvertible { var architecture: ConfigurationArchitecture? + var analytics: [AnalyticsConfiguration] var colors: [ColorsConfiguration] var fonts: [FontsConfiguration] var images: [ImagesConfiguration] @@ -16,12 +17,15 @@ struct ConfigurationFile: Codable, CustomDebugStringConvertible { var tags: [TagsConfiguration] var runnableConfigurations: [Runnable] { - let runnables: [[Runnable]] = [colors, fonts, images, strings, tags] + let runnables: [[Runnable]] = [analytics, colors, fonts, images, strings, tags] return Array(runnables.joined()) } var debugDescription: String { """ + \(analytics) + ----------- + ----------- \(colors) ----------- ----------- @@ -76,6 +80,47 @@ struct ConfigurationArchitecture: Codable { } } +struct AnalyticsConfiguration: Codable, CustomDebugStringConvertible { + let inputFile: String + let target: String + let extensionOutputPath: String + let extensionName: String? + let extensionSuffix: String? + private let staticMembers: Bool? + + var staticMembersOptions: Bool { + if let staticMembers = staticMembers { + return staticMembers + } + return false + } + + internal init(inputFile: String, + target: String, + extensionOutputPath: String, + extensionName: String?, + extensionSuffix: String?, + staticMembers: Bool?) { + self.inputFile = inputFile + self.target = target + self.extensionOutputPath = extensionOutputPath + self.extensionName = extensionName + self.extensionSuffix = extensionSuffix + self.staticMembers = staticMembers + } + + var debugDescription: String { + """ + Analytics configuration: + - Input file: \(inputFile) + - Target: \(target) + - Extension output path: \(extensionOutputPath) + - Extension name: \(extensionName ?? "-") + - Extension suffix: \(extensionSuffix ?? "-") + """ + } +} + struct ColorsConfiguration: Codable, CustomDebugStringConvertible { let inputFile: String let style: String @@ -266,7 +311,7 @@ struct StringsConfiguration: Codable, CustomDebugStringConvertible { struct TagsConfiguration: Codable, CustomDebugStringConvertible { let inputFile: String - let target: String + let lang: String let extensionOutputPath: String let extensionName: String? let extensionSuffix: String? @@ -280,13 +325,13 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible { } internal init(inputFile: String, - target: String, + lang: String, extensionOutputPath: String, extensionName: String?, extensionSuffix: String?, staticMembers: Bool?) { self.inputFile = inputFile - self.target = target + self.lang = lang self.extensionOutputPath = extensionOutputPath self.extensionName = extensionName self.extensionSuffix = extensionSuffix @@ -297,7 +342,7 @@ struct TagsConfiguration: Codable, CustomDebugStringConvertible { """ Tags configuration: - Input file: \(inputFile) - - Target: \(target) + - Lang: \(lang) - Extension output path: \(extensionOutputPath) - Extension name: \(extensionName ?? "-") - Extension suffix: \(extensionSuffix ?? "-") diff --git a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+Runnable.swift index 198f5ea..04c6648 100644 --- a/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+Runnable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/TagsConfiguration+Runnable.swift @@ -17,8 +17,8 @@ extension TagsConfiguration: Runnable { args += [ inputFile.prependIfRelativePath(projectDirectory), - "--target", - target, + "--lang", + lang, "--extension-output-path", extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", diff --git a/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift new file mode 100644 index 0000000..ee09d73 --- /dev/null +++ b/Sources/ResgenSwift/Strings/Generator/TagsGenerator.swift @@ -0,0 +1,86 @@ +// +// TagsGenerator.swift +// +// +// Created by Thibaut Schmitt on 10/01/2022. +// + +import Foundation +import ToolCore +import CoreVideo + +class TagsGenerator { + static func writeExtensionFiles(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { + // Get extension content + let extensionFileContent = Self.getExtensionContent(sections: sections, + lang: lang, + tags: tags, + staticVar: staticVar, + extensionName: extensionName) + + // Write content + let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) + do { + try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8) + } catch (let error) { + let error = StringiumError.writeFile(extensionFilePath, error.localizedDescription) + print(error.description) + Stringium.exit(withError: error) + } + } + + // MARK: - Extension content + + static func getExtensionContent(sections: [Section], lang: String, tags: [String], staticVar: Bool, extensionName: String) -> String { + [ + Self.getHeader(extensionClassname: extensionName, staticVar: staticVar), + Self.getProperties(sections: sections, lang: lang, tags: tags, staticVar: staticVar), + Self.getFooter() + ] + .joined(separator: "\n") + } + + // MARK: - Extension part + + private static func getHeader(extensionClassname: String, staticVar: Bool) -> String { + """ + // Generated by ResgenSwift.Strings.\(Tags.toolName) \(ResgenSwiftVersion) + + \(staticVar ? "typelias Tags = String\n\n" : "")import UIKit + + extension \(extensionClassname) { + """ + } + + private static func getProperties(sections: [Section], lang: String, tags: [String], staticVar: Bool) -> String { + sections + .compactMap { section in + // Check that at least one string will be generated + guard section.hasOneOrMoreMatchingTags(tags: tags) else { + return nil// Go to next section + } + + var res = "\n // MARK: - \(section.name)" + section.definitions.forEach { definition in + guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { + return // Go to next definition + } + + if staticVar { + res += "\n\n\(definition.getStaticProperty(forLang: lang))" + } else { + res += "\n\n\(definition.getProperty(forLang: lang))" + } + } + return res + } + .joined(separator: "\n") + } + + private static func getFooter() -> String { + """ + } + + """ + } +} diff --git a/Sources/ResgenSwift/Strings/Tag/Tags.swift b/Sources/ResgenSwift/Strings/Tag/Tags.swift new file mode 100644 index 0000000..7fedafa --- /dev/null +++ b/Sources/ResgenSwift/Strings/Tag/Tags.swift @@ -0,0 +1,79 @@ +// +// Tag.swift +// +// +// Created by Thibaut Schmitt on 10/01/2022. +// + +import ToolCore +import Foundation +import ArgumentParser + +struct Tags: ParsableCommand { + + // MARK: - Command Configuration + + static var configuration = CommandConfiguration( + abstract: "Generate tags extension file.", + version: ResgenSwiftVersion + ) + + + // MARK: - Static + + static let toolName = "Tags" + static let defaultExtensionName = "Tags" + static let noTranslationTag: String = "notranslation" + + // MARK: - Command Options + + @OptionGroup var options: TagsOptions + + // MARK: - Run + + mutating func run() { + print("[\(Self.toolName)] Starting tags generation") + print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate strings for lang: \(options.lang)") + + // 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.staticMembers, + extensionName: options.extensionName, + extensionFilePath: options.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.description) + Stringium.exit(withError: error) + } + + // Check if needed to regenerate + guard GeneratorChecker.shouldGenerate(force: options.forceGeneration, + inputFilePath: options.inputFile, + extensionFilePath: options.extensionFilePath) else { + print("[\(Self.toolName)] Tags are already up to date :) ") + return false + } + + return true + } +} diff --git a/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift new file mode 100644 index 0000000..e5372b9 --- /dev/null +++ b/Sources/ResgenSwift/Strings/Tag/TagsOptions.swift @@ -0,0 +1,47 @@ +// +// TagOptions.swift +// +// +// Created by Thibaut Schmitt on 10/01/2022. +// + +import Foundation +import ArgumentParser + +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: "Tell if it will generate static properties or not") + var staticMembers: Bool = false + + @Option(help: "Extension name. If not specified, it will generate a Tag extension.") + var extensionName: String = Tags.defaultExtensionName + + @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Tag{extensionSuffix}.swift") + var extensionSuffix: String? +} + +// MARK: - Computed var + +extension TagsOptions { + var extensionFileName: String { + if let extensionSuffix = extensionSuffix { + return "\(extensionName)+\(extensionSuffix).swift" + } + return "\(extensionName).swift" + } + + var extensionFilePath: String { + "\(extensionOutputPath)/\(extensionFileName)" + } +} diff --git a/Sources/ResgenSwift/main.swift b/Sources/ResgenSwift/main.swift index 996161e..01bd9c2 100644 --- a/Sources/ResgenSwift/main.swift +++ b/Sources/ResgenSwift/main.swift @@ -19,10 +19,12 @@ struct ResgenSwift: ParsableCommand { // With language support for type-level introspection, this could be // provided by automatically finding nested `ParsableCommand` types. subcommands: [ + Analytics.self, Colors.self, Fonts.self, Images.self, Strings.self, + Tags.self, Generate.self ] diff --git a/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift b/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift new file mode 100644 index 0000000..561c70c --- /dev/null +++ b/Tests/ResgenSwiftTests/Strings/TagsGeneratorTests.swift @@ -0,0 +1,82 @@ +// +// TagsGeneratorTests.swift +// +// +// Created by Thibaut Schmitt on 06/09/2022. +// + +import Foundation +import XCTest +import ToolCore + +@testable import ResgenSwift + +final class TagsGeneratorTests: XCTestCase { + + private func getDefinition(name: String, lang: String, tags: [String]) -> Definition { + let definition = Definition(name: name) + definition.tags = tags + definition.translations = [lang: "Some translation"] + return definition + } + + func testGeneratedExtensionContent() { + // Given + let sectionOne = Section(name: "section_one") + sectionOne.definitions = [ + getDefinition(name: "s1_def_one", lang: "ium", tags: ["ios","iosonly"]), + getDefinition(name: "s1_def_two", lang: "ium", tags: ["ios","iosonly"]), + ] + + let sectionTwo = Section(name: "section_two") + sectionTwo.definitions = [ + getDefinition(name: "s2_def_one", lang: "ium", tags: ["ios","iosonly"]), + getDefinition(name: "s2_def_two", lang: "ium", tags: ["droid","droidonly"]) + ] + + let sectionThree = Section(name: "section_three") + sectionThree.definitions = [ + getDefinition(name: "s3_def_one", lang: "ium", tags: ["droid","droidonly"]), + getDefinition(name: "s3_def_two", lang: "ium", tags: ["droid","droidonly"]) + ] + + // When + let extensionContent = TagsGenerator.getExtensionContent(sections: [sectionOne, sectionTwo, sectionThree], + lang: "ium", + tags: ["ios", "iosonly"], + staticVar: false, + extensionName: "GenTags") + // Expect Tags + let expect = """ + // Generated by ResgenSwift.Strings.Tags \(ResgenSwiftVersion) + + import UIKit + + extension GenTags { + // MARK: - section_one + + /// Translation in ium : + /// Some translation + var s1_def_one: String { + "Some translation" + } + + /// Translation in ium : + /// Some translation + var s1_def_two: String { + "Some translation" + } + + // MARK: - section_two + + /// Translation in ium : + /// Some translation + var s2_def_one: String { + "Some translation" + } + } + """ + + XCTAssertEqual(extensionContent.adaptForXCTest(), expect.adaptForXCTest()) + } +}