Refactor ModuleType to be a single source of truth (#800)

Rather than defining a new enum, tie ModuleType to ModuleHandler names
from PassepartoutKit.

Also a way to reuse ModuleType.localizedDescription on both Module and
ModuleBuilder implementations.
This commit is contained in:
Davide 2024-11-02 15:23:36 +01:00 committed by GitHub
parent f286ed8ff9
commit 454efb8e50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 68 additions and 78 deletions

View File

@ -49,6 +49,6 @@ private extension ProfileEditor {
assertionFailure("\(type(of: module)) does not provide a default view") assertionFailure("\(type(of: module)) does not provide a default view")
return nil return nil
} }
return (provider, module.typeDescription) return (provider, module.moduleType.localizedDescription)
} }
} }

View File

@ -114,7 +114,7 @@ private extension OpenVPNView {
isImporting = true isImporting = true
} }
.alert( .alert(
module.typeDescription, module.moduleType.localizedDescription,
isPresented: $requiresPassphrase, isPresented: $requiresPassphrase,
presenting: importURL, presenting: importURL,
actions: { url in actions: { url in
@ -194,7 +194,7 @@ private extension OpenVPNView {
pp_log(.app, .error, "Unable to import OpenVPN configuration: \(error)") pp_log(.app, .error, "Unable to import OpenVPN configuration: \(error)")
errorHandler.handle( errorHandler.handle(
(error as? StandardOpenVPNParserError)?.asPassepartoutError ?? error, (error as? StandardOpenVPNParserError)?.asPassepartoutError ?? error,
title: module.typeDescription title: module.moduleType.localizedDescription
) )
} }
} }

View File

@ -40,7 +40,7 @@ struct ModuleViewModifier<T>: ViewModifier where T: ModuleBuilder & Equatable {
if withName { if withName {
NameSection( NameSection(
name: editor.binding(forNameOf: draft.id), name: editor.binding(forNameOf: draft.id),
placeholder: draft.typeDescription placeholder: draft.moduleType.localizedDescription
) )
} }
content content

View File

@ -117,6 +117,9 @@ private extension ProfileCoordinator {
case .onDemand: case .onDemand:
break break
default:
fatalError("Unhandled module type: \(moduleType)")
} }
guard paywallReason == nil else { guard paywallReason == nil else {
return return

View File

@ -71,9 +71,6 @@ public final class ProfileEditor: ObservableObject {
extension ProfileEditor { extension ProfileEditor {
public var moduleTypes: [ModuleType] { public var moduleTypes: [ModuleType] {
editableProfile.modules editableProfile.modules
.compactMap {
$0 as? ModuleTypeProviding
}
.map(\.moduleType) .map(\.moduleType)
} }

View File

@ -1,8 +1,8 @@
// //
// ModuleTypeProviding.swift // Module+ModuleType.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/10/24. // Created by Davide De Rosa on 11/2/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved. // Copyright (c) 2024 Davide De Rosa. All rights reserved.
// //
// https://github.com/passepartoutvpn // https://github.com/passepartoutvpn
@ -26,6 +26,14 @@
import Foundation import Foundation
import PassepartoutKit import PassepartoutKit
protocol ModuleTypeProviding { extension Module {
var moduleType: ModuleType { get } public var moduleType: ModuleType {
ModuleType(moduleHandler)
}
}
extension ModuleBuilder {
public var moduleType: ModuleType {
ModuleType(BuiltType.moduleHandler)
}
} }

View File

@ -1,8 +1,8 @@
// //
// DescriptiveModule+L10n.swift // ModuleType+Known.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/10/24. // Created by Davide De Rosa on 11/2/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved. // Copyright (c) 2024 Davide De Rosa. All rights reserved.
// //
// https://github.com/passepartoutvpn // https://github.com/passepartoutvpn
@ -26,38 +26,25 @@
import Foundation import Foundation
import PassepartoutKit import PassepartoutKit
extension OpenVPNModule.Builder: ModuleTypeProviding { extension ModuleType: CaseIterable {
var moduleType: ModuleType { public static let allCases: [ModuleType] = [
.openVPN .openVPN,
} .wireGuard,
} .dns,
.httpProxy,
extension WireGuardModule.Builder: ModuleTypeProviding { .ip,
var moduleType: ModuleType {
.wireGuard
}
}
extension DNSModule.Builder: ModuleTypeProviding {
var moduleType: ModuleType {
.dns
}
}
extension HTTPProxyModule.Builder: ModuleTypeProviding {
var moduleType: ModuleType {
.httpProxy
}
}
extension IPModule.Builder: ModuleTypeProviding {
var moduleType: ModuleType {
.ip
}
}
extension OnDemandModule.Builder: ModuleTypeProviding {
var moduleType: ModuleType {
.onDemand .onDemand
} ]
public static let openVPN = ModuleType(OpenVPNModule.self)
public static let wireGuard = ModuleType(WireGuardModule.self)
public static let dns = ModuleType(DNSModule.self)
public static let httpProxy = ModuleType(HTTPProxyModule.self)
public static let ip = ModuleType(IPModule.self)
public static let onDemand = ModuleType(OnDemandModule.self)
} }

View File

@ -46,6 +46,9 @@ extension ModuleType {
case .onDemand: case .onDemand:
return OnDemandModule.Builder() return OnDemandModule.Builder()
default:
fatalError("Unknown module type: \(rawValue)")
} }
} }
} }

View File

@ -27,18 +27,20 @@ import Foundation
import PassepartoutKit import PassepartoutKit
import PassepartoutWireGuardGo import PassepartoutWireGuardGo
public enum ModuleType: String, CaseIterable { public struct ModuleType: RawRepresentable, Hashable {
case openVPN public let rawValue: String
case wireGuard public init?(rawValue: String) {
self.rawValue = rawValue
}
case dns init(_ moduleType: Module.Type) {
self.init(moduleType.moduleHandler)
}
case httpProxy init(_ moduleHandler: ModuleHandler) {
rawValue = moduleHandler.id.name
case ip }
case onDemand
} }
extension ModuleType: Identifiable { extension ModuleType: Identifiable {
@ -46,3 +48,9 @@ extension ModuleType: Identifiable {
rawValue rawValue
} }
} }
extension ModuleType: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.rawValue == rhs.rawValue
}
}

View File

@ -36,7 +36,7 @@ extension AppError: LocalizedError {
return V.emptyProfileName return V.emptyProfileName
case .malformedModule(let module, let error): case .malformedModule(let module, let error):
return V.malformedModule(module.typeDescription, error.localizedDescription) return V.malformedModule(module.moduleType.localizedDescription, error.localizedDescription)
case .permissionDenied: case .permissionDenied:
return V.default return V.default

View File

@ -30,15 +30,6 @@ extension ModuleBuilder {
@MainActor @MainActor
public func description(inEditor editor: ProfileEditor) -> String { public func description(inEditor editor: ProfileEditor) -> String {
editor.profile.displayName(forModuleWithId: id) ?? typeDescription editor.profile.displayName(forModuleWithId: id) ?? moduleType.localizedDescription
}
}
extension ModuleBuilder {
public var typeDescription: String {
guard let providing = self as? ModuleTypeProviding else {
return String(describing: self)
}
return providing.moduleType.localizedDescription
} }
} }

View File

@ -25,6 +25,7 @@
import CommonUtils import CommonUtils
import Foundation import Foundation
import PassepartoutKit
extension ModuleType: LocalizableEntity { extension ModuleType: LocalizableEntity {
public var localizedDescription: String { public var localizedDescription: String {
@ -46,6 +47,10 @@ extension ModuleType: LocalizableEntity {
case .onDemand: case .onDemand:
return Strings.Global.onDemand return Strings.Global.onDemand
default:
assertionFailure("Missing localization for ModuleType: \(rawValue)")
return rawValue
} }
} }
} }

View File

@ -46,7 +46,6 @@ public final class UILibrary: UILibraryConfiguring {
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key) logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
) )
assertMissingImplementations()
uiConfiguring?.configure(with: context) uiConfiguring?.configure(with: context)
Task { Task {
@ -58,14 +57,3 @@ public final class UILibrary: UILibraryConfiguring {
} }
} }
} }
private extension UILibrary {
func assertMissingImplementations() {
ModuleType.allCases.forEach { moduleType in
let builder = moduleType.newModule()
guard builder is ModuleTypeProviding else {
fatalError("\(moduleType): is not ModuleTypeProviding")
}
}
}
}