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:
parent
f286ed8ff9
commit
454efb8e50
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -46,6 +46,9 @@ extension ModuleType {
|
||||||
|
|
||||||
case .onDemand:
|
case .onDemand:
|
||||||
return OnDemandModule.Builder()
|
return OnDemandModule.Builder()
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatalError("Unknown module type: \(rawValue)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue