mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 11:14:51 +00:00
73 lines
2.5 KiB
Swift
73 lines
2.5 KiB
Swift
import Foundation
|
|
|
|
/// 写入侧窄 seam:设置工作流只依赖已提交快照、当前 runtime 的
|
|
/// typed listener facts 与提交入口。生产实现是 `ProxyRuntimeController`
|
|
/// 的薄扩展;测试注入 fake。Domain 快照类型只出现在本缝与 adapter。读取侧是
|
|
/// 运行时控制器的主 actor 状态,本缝随之主 actor 绑定。
|
|
@MainActor
|
|
protocol SettingsCommitting: AnyObject {
|
|
var committedSettings: ProxyRuntimeSettings { get }
|
|
var runtimeListenFacts: RuntimeListenFacts? { get }
|
|
var runtimeListenerProcessID: Int32? { get }
|
|
func updateListening(_ proposed: ProxyListeningConfiguration) async throws
|
|
func updateProxyExceptions(_ value: String) async
|
|
}
|
|
|
|
extension ProxyRuntimeController: SettingsCommitting {
|
|
var committedSettings: ProxyRuntimeSettings { settings }
|
|
var runtimeListenFacts: RuntimeListenFacts? { effectiveRuntimeListenFacts }
|
|
var runtimeListenerProcessID: Int32? { effectiveRuntimeListenerProcessID }
|
|
|
|
}
|
|
|
|
/// Settings field adapters keep port IDs, endpoints, and validation facts aligned.
|
|
enum SettingsPortAdapter {
|
|
/// UI 形状端口标识 → 端点类型(module 内部复用 Domain 端口语义)。
|
|
static func endpoint(for id: SettingsPortID, group: SettingsListeningGroup) -> ProxyEndpointKind {
|
|
switch (group, id) {
|
|
case (.local, .socks): .socks
|
|
case (.local, .http): .http
|
|
case (.lan, .socks): .lanSocks
|
|
case (.lan, .http): .lanHTTP
|
|
}
|
|
}
|
|
|
|
/// 全局端口校验保留四个端点的身份,错误只归位到当前编辑组涉及的字段。
|
|
static func fieldIssues(
|
|
from errors: [PortSettingError], group: SettingsListeningGroup
|
|
) -> [SettingsFieldIssue] {
|
|
errors.flatMap { error -> [SettingsFieldIssue] in
|
|
let endpoints: [ProxyEndpointKind]
|
|
switch error {
|
|
case .portOutOfRange(let endpoint, _):
|
|
endpoints = [endpoint]
|
|
case .duplicatePort(let endpoint, let otherEndpoint, _):
|
|
endpoints = [endpoint, otherEndpoint]
|
|
}
|
|
return endpoints.compactMap { endpoint in
|
|
guard
|
|
let id = SettingsPortID.allCases.first(where: {
|
|
Self.endpoint(for: $0, group: group) == endpoint
|
|
})
|
|
else { return nil }
|
|
return .port(id, error: error)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension SettingsPortOccupancy {
|
|
/// 探测层判定 → UI 形状事实。
|
|
init(_ occupancy: PortOccupancy) {
|
|
switch occupancy {
|
|
case .free:
|
|
self = .free
|
|
case .occupied(let facts):
|
|
self = .occupied(occupier: facts.occupier)
|
|
case .unknown(let detail):
|
|
self = .unknown(detail: detail)
|
|
}
|
|
}
|
|
}
|