mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 17:54:50 +00:00
Domain/ 根目录原有 62 个平铺源文件,无法从目录读出主题划分。 按「目录即主题」重组为 11 个目录(Rules/Custom 为嵌套子目录):Catalog / Subscription / Rules / Runtime / Activation / Settings / SystemProxy / Plugin / Credentials / LegacyImport / Diagnostics。纯移动:类型名、模块名 与 import 均未改动,Domain/ 根下不再有散落文件。目录口径取自 GLOSSARY.md 的章节划分(配置与订阅、代理规则、代理控制与网络、设置、 凭据与诊断),而非新造分类。 配套改动: - project.yml:wrapper 与 helper 两个 target 按文件引用的 15 条显式路径 随目录改名,并补注释固定这一约定——这 15 条指向不存在的文件时, xcodegen generate 以 "missing source directory" 直接失败(实测,不静默)。 - SkeletonTests 新增 DomainLayoutTests:断言 Domain/ 根下无平铺 .swift、 树下只有 .swift。后者防的是与 App/ 同源的坑——Domain/ 整目录是 app 与 Core 的 source path,任何非源码文件都会被 XcodeGen 收进 Resources 阶段 并封进签名后的 bundle。 - README 工程结构说明同步;docs/design 与 docs/research 属历史文档不改。 验证:xcodegen generate 后 app 164 / Core 163 源文件数与整理前一致;Agent 恰好 9 个、Helper 恰好 6 个 Domain 文件(无缺失无多出);app 与 Core 各覆盖 全部 62 个。clean build 0 error;task test 1024 用例全通过(整理前 1022, +2 为新增守卫);task format 未改写任何被移动文件(证明纯移动);task lint 与整理前同为 21 条内容型 warning;packaging-gate.sh ALL CHECKS PASSED。 布局守卫已做变异验证:临时投放松散 .swift 与非 .swift 文件时两个用例均 失败并点名违规文件,清理后复验为绿。
58 lines
1.7 KiB
Swift
58 lines
1.7 KiB
Swift
import Foundation
|
|
|
|
protocol PluginMappingStoring {
|
|
func load() throws -> [String: String]
|
|
func save(_ mappings: [String: String]) throws
|
|
}
|
|
|
|
enum PluginMappingError: Error, Equatable {
|
|
case unreadable
|
|
case unsupportedVersion
|
|
case invalidName
|
|
case invalidPath
|
|
case unavailableFile
|
|
case nameExists
|
|
case nameMissing
|
|
}
|
|
|
|
struct PluginMappingFileStore: PluginMappingStoring {
|
|
var fileURL = RuntimePaths.runtimeDirectory().appendingPathComponent("plugins.json")
|
|
|
|
private struct Record: Codable {
|
|
let version: Int
|
|
let mappings: [String: String]
|
|
}
|
|
|
|
func load() throws -> [String: String] {
|
|
// A missing file is empty; an unreadable or malformed file must not erase overrides.
|
|
let data: Data
|
|
do {
|
|
data = try Data(contentsOf: fileURL)
|
|
} catch let error as CocoaError where error.code == .fileReadNoSuchFile {
|
|
return [:]
|
|
} catch {
|
|
throw PluginMappingError.unreadable
|
|
}
|
|
let record: Record
|
|
do {
|
|
record = try JSONDecoder().decode(Record.self, from: data)
|
|
} catch {
|
|
throw PluginMappingError.unreadable
|
|
}
|
|
guard record.version == 1 else { throw PluginMappingError.unsupportedVersion }
|
|
for (name, path) in record.mappings {
|
|
guard name == name.trimmingCharacters(in: .whitespacesAndNewlines), !name.isEmpty,
|
|
!name.contains("\0"), path.hasPrefix("/"), !path.contains("\0")
|
|
else { throw PluginMappingError.unreadable }
|
|
}
|
|
return record.mappings
|
|
}
|
|
|
|
func save(_ mappings: [String: String]) throws {
|
|
let encoder = JSONEncoder()
|
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
try AtomicFileWriter.write(
|
|
try encoder.encode(Record(version: 1, mappings: mappings)), to: fileURL)
|
|
}
|
|
}
|