import Foundation enum Proto: String, Codable, Sendable { case vless case vmess case trojan case hy2 case naive case awg case unknown var label: String { rawValue } static func detect(_ uri: String) -> Proto { let lower = uri.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() if lower.hasPrefix("vless://") { return .vless } if lower.hasPrefix("vmess://") { return .vmess } if lower.hasPrefix("trojan://") { return .trojan } if lower.hasPrefix("hysteria2://") || lower.hasPrefix("hy2://") { return .hy2 } if lower.hasPrefix("naive+") || lower.hasPrefix("naive://") { return .naive } // Desktop DetectProtocol: bare http(s)/quic with userinfo → naive if lower.hasPrefix("https://") || lower.hasPrefix("http://") || lower.hasPrefix("quic://") { return .naive } if lower.hasPrefix("awg://") || lower.hasPrefix("amneziawg://") || lower.hasPrefix("wireguard://") { return .awg } if lower.localizedCaseInsensitiveContains("[Interface]") && lower.contains("Jc") { return .awg } return .unknown } /// Protocols with a working in-process core in the current iOS appex. var supportedOnIOS: Bool { switch self { case .vless, .vmess, .trojan: return true case .hy2: // YAML builder ready; runtime needs unified NavisCore (LibXray Go runtime conflict). return false case .naive, .awg, .unknown: return false } } var iosUnsupportedReason: String? { switch self { case .hy2: return "Hysteria2: пока используйте VLESS/VMess/Trojan (единый NavisCore для Hy2 — следующий шаг)" case .naive: return "NaiveProxy на iOS недоступен (Chromium-ядро)" case .awg: return "AmneziaWG на iOS — следующим этапом" case .unknown: return "Неизвестный протокол" default: return nil } } } struct Profile: Identifiable, Codable, Equatable, Sendable { var id: String var name: String var uri: String var host: String var protocolKind: Proto { Proto.detect(uri) } init(id: String, name: String, uri: String, host: String = "") { self.id = id self.name = name self.uri = uri self.host = host.isEmpty ? LinkParser.hostOf(uri) : host } } struct PingResult: Identifiable, Sendable { var id: String var ms: Int64 = -1 var ok: Bool = false var error: String? } struct TunnelConfig: Codable, Sendable { var name: String var uri: String var startedAt: Double }