Scan nested Java aggregate roots
This commit is contained in:
parent
6e728724bb
commit
ca21654b44
@ -733,47 +733,49 @@ enum JavaContentScanner {
|
||||
let fileManager = FileManager.default
|
||||
var discoveredItems: [MinecraftContentItem] = []
|
||||
|
||||
let savesRootURL = existingDirectory(
|
||||
named: "saves",
|
||||
in: searchRootURL,
|
||||
fileManager: fileManager
|
||||
) ?? searchRootURL
|
||||
let worldItems = try discoverWorlds(in: savesRootURL, fileManager: fileManager)
|
||||
discoveredItems.append(contentsOf: worldItems)
|
||||
|
||||
if let resourcePacksURL = existingDirectory(named: "resourcepacks", in: searchRootURL, fileManager: fileManager) {
|
||||
let resourcePackItems = try discoverResourcePacks(in: resourcePacksURL, fileManager: fileManager)
|
||||
discoveredItems.append(contentsOf: resourcePackItems)
|
||||
}
|
||||
|
||||
if let dataPacksURL = existingDirectory(named: "datapacks", in: searchRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: dataPacksURL,
|
||||
contentKind: .dataPack,
|
||||
platformType: .dataPack,
|
||||
packageExtension: "zip",
|
||||
for scanRootURL in contentScanRoots(for: searchRootURL, fileManager: fileManager) {
|
||||
let savesRootURL = existingDirectory(
|
||||
named: "saves",
|
||||
in: scanRootURL,
|
||||
fileManager: fileManager
|
||||
))
|
||||
}
|
||||
) ?? scanRootURL
|
||||
let worldItems = try discoverWorlds(in: savesRootURL, fileManager: fileManager)
|
||||
discoveredItems.append(contentsOf: worldItems)
|
||||
|
||||
if let shaderPacksURL = existingDirectory(named: "shaderpacks", in: searchRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: shaderPacksURL,
|
||||
contentKind: .shaderPack,
|
||||
platformType: .shaderPack,
|
||||
packageExtension: "zip",
|
||||
fileManager: fileManager
|
||||
))
|
||||
}
|
||||
if let resourcePacksURL = existingDirectory(named: "resourcepacks", in: scanRootURL, fileManager: fileManager) {
|
||||
let resourcePackItems = try discoverResourcePacks(in: resourcePacksURL, fileManager: fileManager)
|
||||
discoveredItems.append(contentsOf: resourcePackItems)
|
||||
}
|
||||
|
||||
if let modsURL = existingDirectory(named: "mods", in: searchRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: modsURL,
|
||||
contentKind: .mod,
|
||||
platformType: .mod,
|
||||
packageExtension: "jar",
|
||||
fileManager: fileManager
|
||||
))
|
||||
if let dataPacksURL = existingDirectory(named: "datapacks", in: scanRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: dataPacksURL,
|
||||
contentKind: .dataPack,
|
||||
platformType: .dataPack,
|
||||
packageExtension: "zip",
|
||||
fileManager: fileManager
|
||||
))
|
||||
}
|
||||
|
||||
if let shaderPacksURL = existingDirectory(named: "shaderpacks", in: scanRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: shaderPacksURL,
|
||||
contentKind: .shaderPack,
|
||||
platformType: .shaderPack,
|
||||
packageExtension: "zip",
|
||||
fileManager: fileManager
|
||||
))
|
||||
}
|
||||
|
||||
if let modsURL = existingDirectory(named: "mods", in: scanRootURL, fileManager: fileManager) {
|
||||
discoveredItems.append(contentsOf: try discoverJavaPackages(
|
||||
in: modsURL,
|
||||
contentKind: .mod,
|
||||
platformType: .mod,
|
||||
packageExtension: "jar",
|
||||
fileManager: fileManager
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
discoveredItems.sort(by: WorldScanner.sortItems)
|
||||
@ -806,21 +808,32 @@ enum JavaContentScanner {
|
||||
|
||||
nonisolated static func collectionSnapshots(in sourceRootURL: URL) -> [CollectionSnapshot] {
|
||||
let fileManager = FileManager.default
|
||||
let candidateRoots = [
|
||||
existingDirectory(named: "saves", in: sourceRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "resourcepacks", in: sourceRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "datapacks", in: sourceRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "shaderpacks", in: sourceRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "mods", in: sourceRootURL, fileManager: fileManager)
|
||||
]
|
||||
var snapshots: [CollectionSnapshot] = []
|
||||
for scanRootURL in contentScanRoots(for: sourceRootURL, fileManager: fileManager) {
|
||||
let candidateRoots = [
|
||||
existingDirectory(named: "saves", in: scanRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "resourcepacks", in: scanRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "datapacks", in: scanRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "shaderpacks", in: scanRootURL, fileManager: fileManager),
|
||||
existingDirectory(named: "mods", in: scanRootURL, fileManager: fileManager)
|
||||
]
|
||||
|
||||
return candidateRoots.compactMap { collectionURL in
|
||||
guard let collectionURL else {
|
||||
return nil
|
||||
for collectionURL in candidateRoots {
|
||||
guard let collectionURL else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let snapshot = collectionSnapshot(
|
||||
for: collectionURL,
|
||||
sourceRootURL: sourceRootURL,
|
||||
fileManager: fileManager
|
||||
) {
|
||||
snapshots.append(snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
return collectionSnapshot(for: collectionURL, fileManager: fileManager)
|
||||
}
|
||||
|
||||
return snapshots
|
||||
}
|
||||
|
||||
nonisolated private static func discoverWorlds(in savesRootURL: URL, fileManager: FileManager) throws -> [MinecraftContentItem] {
|
||||
@ -1080,6 +1093,27 @@ enum JavaContentScanner {
|
||||
return folders
|
||||
}
|
||||
|
||||
nonisolated private static func contentScanRoots(for sourceRootURL: URL, fileManager: FileManager) -> [URL] {
|
||||
let standardizedRoot = sourceRootURL.standardizedFileURL
|
||||
if javaProbeScore(for: standardizedRoot, fileManager: fileManager).value > 0 {
|
||||
return [standardizedRoot]
|
||||
}
|
||||
|
||||
let discoveredRoots = boundedCandidateFolders(
|
||||
from: standardizedRoot,
|
||||
maxDepth: 4,
|
||||
maxFolderCount: 600,
|
||||
fileManager: fileManager
|
||||
).filter { candidateURL in
|
||||
candidateURL != standardizedRoot
|
||||
&& javaProbeScore(for: candidateURL, fileManager: fileManager).value > 0
|
||||
}
|
||||
|
||||
return uniqueStandardizedURLs(discoveredRoots).sorted {
|
||||
$0.path.localizedStandardCompare($1.path) == .orderedAscending
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated private static func uniqueStandardizedURLs(_ urls: [URL]) -> [URL] {
|
||||
var seen = Set<String>()
|
||||
var result: [URL] = []
|
||||
@ -1099,6 +1133,7 @@ enum JavaContentScanner {
|
||||
|
||||
nonisolated private static func collectionSnapshot(
|
||||
for collectionURL: URL,
|
||||
sourceRootURL: URL,
|
||||
fileManager: FileManager
|
||||
) -> CollectionSnapshot? {
|
||||
guard fileManager.fileExists(atPath: collectionURL.path) else {
|
||||
@ -1135,12 +1170,15 @@ enum JavaContentScanner {
|
||||
].joined(separator: "@")
|
||||
}.joined(separator: "|")
|
||||
|
||||
let folderName = relativePath(from: sourceRootURL.standardizedFileURL, to: collectionURL.standardizedFileURL)
|
||||
?? collectionURL.lastPathComponent
|
||||
|
||||
return CollectionSnapshot(
|
||||
folderName: collectionURL.lastPathComponent,
|
||||
folderName: folderName,
|
||||
modifiedDate: modifiedDate,
|
||||
childDirectoryCount: childSnapshots.count,
|
||||
fingerprint: [
|
||||
collectionURL.lastPathComponent,
|
||||
folderName,
|
||||
String(childSnapshots.count),
|
||||
modifiedDate?.timeIntervalSince1970.formatted() ?? "nil",
|
||||
childFingerprint
|
||||
@ -1148,6 +1186,16 @@ enum JavaContentScanner {
|
||||
)
|
||||
}
|
||||
|
||||
nonisolated private static func relativePath(from rootURL: URL, to childURL: URL) -> String? {
|
||||
let rootPath = rootURL.standardizedFileURL.path
|
||||
let childPath = childURL.standardizedFileURL.path
|
||||
guard childPath.hasPrefix(rootPath + "/") else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return String(childPath.dropFirst(rootPath.count + 1))
|
||||
}
|
||||
|
||||
nonisolated private static func displayName(for item: MinecraftContentItem) -> String {
|
||||
guard item.contentKind == .world else {
|
||||
return item.folderName
|
||||
|
||||
@ -385,6 +385,34 @@ struct World_Manager_for_MinecraftTests {
|
||||
#expect(candidates.first?.detectedKinds.contains(.mod) == true)
|
||||
}
|
||||
|
||||
@Test func javaAggregateRootDiscoversNestedInstanceItems() async throws {
|
||||
let fileManager = FileManager.default
|
||||
let workingURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
||||
let firstInstanceURL = workingURL.appendingPathComponent("a/b/c", isDirectory: true)
|
||||
let secondInstanceURL = workingURL.appendingPathComponent("a/e/f", isDirectory: true)
|
||||
defer { try? fileManager.removeItem(at: workingURL) }
|
||||
|
||||
try fileManager.createDirectory(
|
||||
at: firstInstanceURL.appendingPathComponent("mods", isDirectory: true),
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
try Data("jar".utf8).write(to: firstInstanceURL.appendingPathComponent("mods/ExampleMod.jar"))
|
||||
|
||||
try fileManager.createDirectory(
|
||||
at: secondInstanceURL.appendingPathComponent("resourcepacks", isDirectory: true),
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
try Data("zip".utf8).write(to: secondInstanceURL.appendingPathComponent("resourcepacks/ExamplePack.zip"))
|
||||
|
||||
let items = try JavaContentScanner.discoverItems(in: workingURL)
|
||||
let snapshots = JavaContentScanner.collectionSnapshots(in: workingURL)
|
||||
|
||||
#expect(items.contains { $0.contentKind == .mod && $0.folderName == "ExampleMod.jar" })
|
||||
#expect(items.contains { $0.contentKind == .resourcePack && $0.folderName == "ExamplePack.zip" })
|
||||
#expect(snapshots.map(\.folderName).contains("a/b/c/mods"))
|
||||
#expect(snapshots.map(\.folderName).contains("a/e/f/resourcepacks"))
|
||||
}
|
||||
|
||||
@Test func sourceLibraryAddSourceResolvesJavaWrapperFolder() async throws {
|
||||
let fileManager = FileManager.default
|
||||
let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user