Retours sur la structure du code

This commit is contained in:
Thibaut Schmitt 2023-12-08 17:37:21 +01:00 committed by Loris Perret
parent 5427ff6786
commit dfe31b5c80
13 changed files with 177 additions and 143 deletions

View File

@ -32,6 +32,10 @@ struct Analytics: ParsableCommand {
mutating func run() { mutating func run() {
print("[\(Self.toolName)] Starting analytics generation") print("[\(Self.toolName)] Starting analytics generation")
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)") print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)")
// Check requirements
guard checkRequirements() else { return }
print("[\(Self.toolName)] Will generate analytics") print("[\(Self.toolName)] Will generate analytics")
// Check requirements // Check requirements
@ -75,18 +79,11 @@ struct Analytics: ParsableCommand {
} }
} }
extension Analytics { // MARK: - Requirements
enum TargetType: CaseIterable {
case matomo private func checkRequirements() -> Bool {
case firebase // Check les requirements et gestion des erreurs
// Il faut que toutes les erreurs générées par Analytics soit des AnalyticsError
var value: String { true
switch self {
case .matomo:
"matomo"
case .firebase:
"firebase"
}
}
} }
} }

View File

@ -10,13 +10,13 @@ import ToolCore
import CoreVideo import CoreVideo
class AnalyticsGenerator { class AnalyticsGenerator {
static var targets: [Analytics.TargetType] = [] static var targets: [TrackerType] = []
static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) { static func writeExtensionFiles(sections: [AnalyticsCategory], target: String, tags: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
// Get target type from enum // Get target type from enum
let targetsString: [String] = target.components(separatedBy: " ") let targetsString: [String] = target.components(separatedBy: " ")
Analytics.TargetType.allCases.forEach { enumTarget in TrackerType.allCases.forEach { enumTarget in
if targetsString.contains(enumTarget.value) { if targetsString.contains(enumTarget.value) {
targets.append(enumTarget) targets.append(enumTarget)
} }
@ -58,7 +58,7 @@ class AnalyticsGenerator {
\(Self.getImport()) \(Self.getImport())
\(Self.getAnalytics()) \(Self.getAnalyticsProtocol())
// MARK: - Manager // MARK: - Manager
class AnalyticsManager { class AnalyticsManager {
@ -91,10 +91,10 @@ class AnalyticsGenerator {
private static func getImport() -> String { private static func getImport() -> String {
var result: [String] = [] var result: [String] = []
if targets.contains(Analytics.TargetType.matomo) { if targets.contains(TrackerType.matomo) {
result.append("import MatomoTracker") result.append("import MatomoTracker")
} }
if targets.contains(Analytics.TargetType.firebase) { if targets.contains(TrackerType.firebase) {
result.append("import FirebaseAnalytics") result.append("import FirebaseAnalytics")
} }
@ -136,13 +136,13 @@ class AnalyticsGenerator {
var content: [String] = [] var content: [String] = []
let footer = " }" let footer = " }"
if targets.contains(Analytics.TargetType.matomo) { if targets.contains(TrackerType.matomo) {
header = "func configure(siteId: String, url: String) {" header = "func configure(siteId: String, url: String) {"
} else if targets.contains(Analytics.TargetType.firebase) { } else if targets.contains(TrackerType.firebase) {
header = "func configure() {" header = "func configure() {"
} }
if targets.contains(Analytics.TargetType.matomo) { if targets.contains(TrackerType.matomo) {
content.append(""" content.append("""
managers.append( managers.append(
MatomoAnalyticsManager( MatomoAnalyticsManager(
@ -152,7 +152,7 @@ class AnalyticsGenerator {
) )
""") """)
} }
if targets.contains(Analytics.TargetType.firebase) { if targets.contains(TrackerType.firebase) {
content.append(" managers.append(FirebaseAnalyticsManager())") content.append(" managers.append(FirebaseAnalyticsManager())")
} }
@ -164,7 +164,7 @@ class AnalyticsGenerator {
.joined(separator: "\n") .joined(separator: "\n")
} }
private static func getAnalytics() -> String { private static func getAnalyticsProtocol() -> String {
let proto = """ let proto = """
// MARK: - Protocol // MARK: - Protocol
@ -182,12 +182,12 @@ class AnalyticsGenerator {
var result: [String] = [proto] var result: [String] = [proto]
if targets.contains(Analytics.TargetType.matomo) { if targets.contains(TrackerType.matomo) {
result.append(MatomoGenerator.service.content) result.append(MatomoGenerator.service)
} }
if targets.contains(Analytics.TargetType.firebase) { if targets.contains(TrackerType.firebase) {
result.append(FirebaseGenerator.service.content) result.append(FirebaseGenerator.service)
} }
return result.joined(separator: "\n") return result.joined(separator: "\n")

View File

@ -8,19 +8,20 @@
import Foundation import Foundation
enum FirebaseGenerator { enum FirebaseGenerator {
case service
static var service: String {
var content: String {
[ [
FirebaseGenerator.service.header, FirebaseGenerator.header,
FirebaseGenerator.service.logScreen, FirebaseGenerator.logScreen,
FirebaseGenerator.service.logEvent, FirebaseGenerator.logEvent,
FirebaseGenerator.service.footer FirebaseGenerator.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
private var header: String { // MARK: - Private vars
private static var header: String {
""" """
// MARK: - Firebase // MARK: - Firebase
@ -28,7 +29,7 @@ enum FirebaseGenerator {
""" """
} }
private var logScreen: String { private static var logScreen: String {
""" """
func logScreen(name: String, path: String) { func logScreen(name: String, path: String) {
var parameters = [ var parameters = [
@ -44,7 +45,7 @@ enum FirebaseGenerator {
""" """
} }
private var logEvent: String { private static var logEvent: String {
""" """
func logEvent( func logEvent(
name: String, name: String,
@ -71,7 +72,7 @@ enum FirebaseGenerator {
""" """
} }
private var footer: String { private static var footer: String {
""" """
} }

View File

@ -8,20 +8,21 @@
import Foundation import Foundation
enum MatomoGenerator { enum MatomoGenerator {
case service
static var service: String {
var content: String {
[ [
MatomoGenerator.service.header, MatomoGenerator.header,
MatomoGenerator.service.setup, MatomoGenerator.setup,
MatomoGenerator.service.logScreen, MatomoGenerator.logScreen,
MatomoGenerator.service.logEvent, MatomoGenerator.logEvent,
MatomoGenerator.service.footer MatomoGenerator.footer
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
private var header: String { // MARK: - Private vars
private static var header: String {
""" """
// MARK: - Matomo // MARK: - Matomo
@ -34,7 +35,7 @@ enum MatomoGenerator {
""" """
} }
private var setup: String { private static var setup: String {
""" """
// MARK: - Init // MARK: - Init
@ -63,7 +64,7 @@ enum MatomoGenerator {
""" """
} }
private var logScreen: String { private static var logScreen: String {
""" """
func logScreen(name: String, path: String) { func logScreen(name: String, path: String) {
guard !tracker.isOptedOut else { return } guard !tracker.isOptedOut else { return }
@ -79,7 +80,7 @@ enum MatomoGenerator {
""" """
} }
private var logEvent: String { private static var logEvent: String {
""" """
func logEvent( func logEvent(
name: String, name: String,
@ -100,7 +101,7 @@ enum MatomoGenerator {
""" """
} }
private var footer: String { private static var footer: String {
""" """
} }

View File

@ -11,10 +11,14 @@ class AnalyticsCategory {
let id: String // OnBoarding let id: String // OnBoarding
var definitions = [AnalyticsDefinition]() var definitions = [AnalyticsDefinition]()
// MARK: - Init
init(id: String) { init(id: String) {
self.id = id self.id = id
} }
// MARK: - Methods
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool { func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
let allTags = definitions.flatMap { $0.tags } let allTags = definitions.flatMap { $0.tags }
let allTagsSet = Set(allTags) let allTagsSet = Set(allTags)

View File

@ -6,6 +6,7 @@
// //
import Foundation import Foundation
import ToolCore
class AnalyticsDefinition { class AnalyticsDefinition {
let id: String let id: String
@ -18,12 +19,16 @@ class AnalyticsDefinition {
var parameters: [AnalyticsParameter] = [] var parameters: [AnalyticsParameter] = []
var type: TagType var type: TagType
// MARK: - Init
init(id: String, name: String, type: TagType) { init(id: String, name: String, type: TagType) {
self.id = id self.id = id
self.name = name self.name = name
self.type = type self.type = type
} }
// MARK: - Methods
func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool { func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool {
if Set(inputTags).isDisjoint(with: tags) { if Set(inputTags).isDisjoint(with: tags) {
return false return false
@ -31,8 +36,8 @@ class AnalyticsDefinition {
return true return true
} }
// MARK: - Methods // MARK: - Private Methods
private func getFuncName() -> String { private func getFuncName() -> String {
var pascalCaseTitle: String = "" var pascalCaseTitle: String = ""
id.components(separatedBy: "_").forEach { word in id.components(separatedBy: "_").forEach { word in
@ -96,7 +101,7 @@ class AnalyticsDefinition {
supplementaryParams.forEach { param in supplementaryParams.forEach { param in
params.append("\"\(param.name)\": \(param.name)") params.append("\"\(param.name)\": \(param.name)")
} }
if params.count > 1 { if params.count > 1 {
result = """ result = """
[ [
@ -150,27 +155,3 @@ class AnalyticsDefinition {
""" """
} }
} }
extension AnalyticsDefinition {
enum TagType {
case screen
case event
}
}
extension String {
func replacingFirstOccurrence(of: String, with: String) -> Self {
if let range = self.range(of: of) {
let tmp = self.replacingOccurrences(
of: of,
with: with,
options: .literal,
range: range
)
return tmp
}
return self
}
}

View File

@ -17,9 +17,7 @@ struct AnalyticsCategoryDTO: Codable {
var events: [AnalyticsDefinitionEventDTO]? var events: [AnalyticsDefinitionEventDTO]?
} }
protocol AnalyticsDefinitionDTO: Codable {} struct AnalyticsDefinitionScreenDTO: Codable {
struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO {
var id: String var id: String
var name: String var name: String
var tags: String var tags: String
@ -29,7 +27,7 @@ struct AnalyticsDefinitionScreenDTO: AnalyticsDefinitionDTO {
var path: String? var path: String?
} }
struct AnalyticsDefinitionEventDTO: AnalyticsDefinitionDTO { struct AnalyticsDefinitionEventDTO: Codable {
var id: String var id: String
var name: String var name: String
var tags: String var tags: String

View File

@ -12,6 +12,8 @@ class AnalyticsParameter {
var type: String var type: String
var replaceIn: [String] = [] var replaceIn: [String] = []
// MARK: - Init
init(name: String, type: String) { init(name: String, type: String) {
self.name = name self.name = name
self.type = type self.type = type

View File

@ -0,0 +1,16 @@
//
// TagType.swift
//
//
// Created by Thibaut Schmitt on 08/12/2023.
//
import Foundation
extension AnalyticsDefinition {
enum TagType {
case screen
case event
}
}

View File

@ -0,0 +1,22 @@
//
// TargetType.swift
//
//
// Created by Thibaut Schmitt on 08/12/2023.
//
import Foundation
enum TrackerType: CaseIterable {
case matomo
case firebase
var value: String {
switch self {
case .matomo:
"matomo"
case .firebase:
"firebase"
}
}
}

View File

@ -25,11 +25,9 @@ class AnalyticsFileParser {
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
} }
private static func getParameters(fromData data: [AnalyticsParameterDTO]) -> [AnalyticsParameter] { private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
var parameters: [AnalyticsParameter] = [] parameters.map { dtoParameter in
data.forEach { value in
// Type // Type
let type = value.type.uppercasedFirst() let type = value.type.uppercasedFirst()
@ -43,17 +41,18 @@ class AnalyticsFileParser {
let error = AnalyticsError.invalidParameter("type of \(value.name)") let error = AnalyticsError.invalidParameter("type of \(value.name)")
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
let parameter: AnalyticsParameter = AnalyticsParameter(name: value.name, type: type) let parameter = AnalyticsParameter(
name: dtoParameter.name,
if let replaceIn = value.replaceIn { type: type
)
if let replaceIn = dtoParameter.replaceIn {
parameter.replaceIn = replaceIn.components(separatedBy: ",") parameter.replaceIn = replaceIn.components(separatedBy: ",")
} }
parameters.append(parameter) return parameter
} }
return parameters
} }
private static func getTagDefinition( private static func getTagDefinition(
@ -64,24 +63,24 @@ class AnalyticsFileParser {
comments: String?, comments: String?,
parameters: [AnalyticsParameterDTO]? parameters: [AnalyticsParameterDTO]?
) -> AnalyticsDefinition { ) -> AnalyticsDefinition {
let definition: AnalyticsDefinition = AnalyticsDefinition(id: id, name: name, type: type) let definition = AnalyticsDefinition(id: id, name: name, type: type)
definition.tags = tags.components(separatedBy: ",") definition.tags = tags
.components(separatedBy: ",")
.map { $0.removeLeadingTrailingWhitespace() }
if let comments = comments { if let comments = comments {
definition.comments = comments definition.comments = comments
} }
if let parameters = parameters { if let parameters = parameters {
definition.parameters = Self.getParameters(fromData: parameters) definition.parameters = Self.getParameters(from: parameters)
} }
return definition return definition
} }
private static func getTagDefinitionScreen(fromData screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] { private static func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
var definitions: [AnalyticsDefinition] = [] screens.map { screen in
for screen in screens {
let definition: AnalyticsDefinition = Self.getTagDefinition( let definition: AnalyticsDefinition = Self.getTagDefinition(
id: screen.id, id: screen.id,
name: screen.name, name: screen.name,
@ -90,10 +89,10 @@ class AnalyticsFileParser {
comments: screen.comments, comments: screen.comments,
parameters: screen.parameters parameters: screen.parameters
) )
if target.contains(Analytics.TargetType.matomo.value) { if target.contains(Analytics.TargetType.matomo.value) {
// Path // Path
guard let path = screen.path else { guard let path = screen.path else {
let error = AnalyticsError.missingElement("screen path") let error = AnalyticsError.missingElement("screen path")
Analytics.exit(withError: error) Analytics.exit(withError: error)
@ -101,17 +100,13 @@ class AnalyticsFileParser {
definition.path = path definition.path = path
} }
definitions.append(definition) return definition
} }
return definitions
} }
private static func getTagDefinitionEvent(fromData events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] { private static func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
var definitions: [AnalyticsDefinition] = [] events.map { event in
for event in events {
let definition: AnalyticsDefinition = Self.getTagDefinition( let definition: AnalyticsDefinition = Self.getTagDefinition(
id: event.id, id: event.id,
name: event.name, name: event.name,
@ -120,54 +115,57 @@ class AnalyticsFileParser {
comments: event.comments, comments: event.comments,
parameters: event.parameters parameters: event.parameters
) )
if target.contains(Analytics.TargetType.matomo.value) { if target.contains(Analytics.TargetType.matomo.value) {
// Category // Category
guard let category = event.category else { guard let category = event.category else {
let error = AnalyticsError.missingElement("event category") let error = AnalyticsError.missingElement("event category")
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
definition.category = category definition.category = category
// Action // Action
guard let action = event.action else { guard let action = event.action else {
let error = AnalyticsError.missingElement("event action") let error = AnalyticsError.missingElement("event action")
Analytics.exit(withError: error) Analytics.exit(withError: error)
} }
definition.action = action definition.action = action
} }
definitions.append(definition) return definition
} }
return definitions
} }
static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] { static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] {
self.inputFile = inputFile self.inputFile = inputFile
self.target = target self.target = target
let tagFile: AnalyticsFile = Self.parseYaml() let tagFile = Self.parseYaml()
var sections: [AnalyticsCategory] = []
tagFile.categories.forEach { categorie in return tagFile
let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id) .categories
.map { categorie in
if let screens = categorie.screens { let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id)
section.definitions.append(contentsOf: Self.getTagDefinitionScreen(fromData: screens))
if let screens = categorie.screens {
section
.definitions
.append(
contentsOf: Self.getTagDefinitionScreen(from: screens)
)
}
if let events = categorie.events {
section
.definitions
.append(
contentsOf: Self.getTagDefinitionEvent(from: events)
)
}
return section
} }
if let events = categorie.events {
section.definitions.append(contentsOf: Self.getTagDefinitionEvent(fromData: events))
}
sections.append(section)
}
return sections
} }
} }

View File

@ -63,7 +63,7 @@ public extension String {
replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)") replacingOccurrences(of: "~", with: "\(FileManager.default.homeDirectoryForCurrentUser.relativePath)")
} }
func colorComponent() -> (alpha: String, red: String, green: String, blue: String) { func colorComponent() -> (alpha: String, red: String, green: String, blue: String) {
var alpha: String = "FF" var alpha: String = "FF"
var red: String var red: String
var green: String var green: String
@ -89,4 +89,19 @@ public extension String {
func uppercasedFirst() -> String { func uppercasedFirst() -> String {
prefix(1).uppercased() + dropFirst() prefix(1).uppercased() + dropFirst()
} }
func replacingFirstOccurrence(of: String, with: String) -> Self {
if let range = self.range(of: of) {
let tmp = self.replacingOccurrences(
of: of,
with: with,
options: .literal,
range: range
)
return tmp
}
return self
}
} }

View File

@ -1,5 +1,5 @@
// //
// File.swift // DiffString.swift
// //
// //
// Created by Loris Perret on 06/12/2023. // Created by Loris Perret on 06/12/2023.
@ -7,7 +7,6 @@
import Foundation import Foundation
/// Find first differing character between two strings /// Find first differing character between two strings
/// ///
/// :param: s1 First String /// :param: s1 First String