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")
return nil
}
return (provider, module.typeDescription)
return (provider, module.moduleType.localizedDescription)
}
}

View File

@ -114,7 +114,7 @@ private extension OpenVPNView {
isImporting = true
}
.alert(
module.typeDescription,
module.moduleType.localizedDescription,
isPresented: $requiresPassphrase,
presenting: importURL,
actions: { url in
@ -194,7 +194,7 @@ private extension OpenVPNView {
pp_log(.app, .error, "Unable to import OpenVPN configuration: \(error)")
errorHandler.handle(
(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 {
NameSection(
name: editor.binding(forNameOf: draft.id),
placeholder: draft.typeDescription
placeholder: draft.moduleType.localizedDescription
)
}
content

View File

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

View File

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

View File

@ -1,8 +1,8 @@
//
// ModuleTypeProviding.swift
// Module+ModuleType.swift
// 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.
//
// https://github.com/passepartoutvpn
@ -26,6 +26,14 @@
import Foundation
import PassepartoutKit
protocol ModuleTypeProviding {
var moduleType: ModuleType { get }
extension Module {
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
//
// 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.
//
// https://github.com/passepartoutvpn
@ -26,38 +26,25 @@
import Foundation
import PassepartoutKit
extension OpenVPNModule.Builder: ModuleTypeProviding {
var moduleType: ModuleType {
.openVPN
}
}
extension WireGuardModule.Builder: ModuleTypeProviding {
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 {
extension ModuleType: CaseIterable {
public static let allCases: [ModuleType] = [
.openVPN,
.wireGuard,
.dns,
.httpProxy,
.ip,
.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:
return OnDemandModule.Builder()
default:
fatalError("Unknown module type: \(rawValue)")
}
}
}

View File

@ -27,18 +27,20 @@ import Foundation
import PassepartoutKit
import PassepartoutWireGuardGo
public enum ModuleType: String, CaseIterable {
case openVPN
public struct ModuleType: RawRepresentable, Hashable {
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
case ip
case onDemand
init(_ moduleHandler: ModuleHandler) {
rawValue = moduleHandler.id.name
}
}
extension ModuleType: Identifiable {
@ -46,3 +48,9 @@ extension ModuleType: Identifiable {
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
case .malformedModule(let module, let error):
return V.malformedModule(module.typeDescription, error.localizedDescription)
return V.malformedModule(module.moduleType.localizedDescription, error.localizedDescription)
case .permissionDenied:
return V.default

View File

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

View File

@ -25,6 +25,7 @@
import CommonUtils
import Foundation
import PassepartoutKit
extension ModuleType: LocalizableEntity {
public var localizedDescription: String {
@ -46,6 +47,10 @@ extension ModuleType: LocalizableEntity {
case .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)
)
assertMissingImplementations()
uiConfiguring?.configure(with: context)
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")
}
}
}
}