From ffb6e497ec94986ca073b56f6cfc68fd3e606e0d Mon Sep 17 00:00:00 2001 From: John Burwell Date: Tue, 2 Jun 2026 16:41:34 -0500 Subject: [PATCH] Preserve provider for accepted source candidates --- .../Services/Sources/Core/SourceLibrary.swift | 40 +++++++++++++++++- .../World_Manager_for_MinecraftTests.swift | 42 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/World Manager for Minecraft/Services/Sources/Core/SourceLibrary.swift b/World Manager for Minecraft/Services/Sources/Core/SourceLibrary.swift index 8ce6896..f944374 100644 --- a/World Manager for Minecraft/Services/Sources/Core/SourceLibrary.swift +++ b/World Manager for Minecraft/Services/Sources/Core/SourceLibrary.swift @@ -229,7 +229,45 @@ final class SourceLibrary: ObservableObject, SourceScanSessionHosting, SourcePer } func addSource(candidate: SourceCandidate) async -> URL { - await addSource(at: candidate.sourceRootURL) + let normalizedURL = candidate.sourceRootURL.standardizedFileURL + let bookmarkData = securityScopedBookmarkData(for: normalizedURL) + + if sources.contains(where: { $0.id == normalizedURL }) { + updateSource(normalizedURL) { source in + if source.bookmarkData == nil { + source.bookmarkData = bookmarkData + } + source.accessDescriptor = SourceAccessDescriptor( + accessorIdentifier: candidate.providerID, + kind: .localFolder, + refreshStrategy: .eagerFullScan + ) + source.providerID = candidate.providerID + source.edition = candidate.edition + source.displayName = candidate.displayName + source.capabilities = source.origin.defaultCapabilities + } + sourceCandidates.removeAll { $0.id == candidate.id || $0.sourceRootURL == normalizedURL } + startScan(for: normalizedURL, mode: .fullScan) + return normalizedURL + } + + var source = MinecraftSource( + folderURL: normalizedURL, + bookmarkData: bookmarkData, + accessDescriptor: SourceAccessDescriptor( + accessorIdentifier: candidate.providerID, + kind: .localFolder, + refreshStrategy: .eagerFullScan + ) + ) + source.providerID = candidate.providerID + source.edition = candidate.edition + source.displayName = candidate.displayName + + let sourceID = addSource(source, shouldPersist: true, shouldScan: true) + sourceCandidates.removeAll { $0.id == candidate.id || $0.sourceRootURL == sourceID } + return sourceID } @discardableResult diff --git a/World Manager for MinecraftTests/World_Manager_for_MinecraftTests.swift b/World Manager for MinecraftTests/World_Manager_for_MinecraftTests.swift index 90a2285..08ee71e 100644 --- a/World Manager for MinecraftTests/World_Manager_for_MinecraftTests.swift +++ b/World Manager for MinecraftTests/World_Manager_for_MinecraftTests.swift @@ -413,6 +413,48 @@ struct World_Manager_for_MinecraftTests { #expect(snapshots.map(\.folderName).contains("a/e/f/resourcepacks")) } + @Test func sourceLibraryAddSourceCandidatePreservesJavaAggregateProvider() async throws { + let fileManager = FileManager.default + let workingURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true) + let instanceURL = workingURL.appendingPathComponent("a/b/c", isDirectory: true) + let modURL = instanceURL.appendingPathComponent("mods/ExampleMod.jar") + defer { try? fileManager.removeItem(at: workingURL) } + + try fileManager.createDirectory(at: modURL.deletingLastPathComponent(), withIntermediateDirectories: true) + try Data("jar".utf8).write(to: modURL) + + let candidate = SourceCandidate( + providerID: JavaLocalFolderSourceAccess().accessorIdentifier, + edition: .java, + sourceRootURL: workingURL, + displayName: workingURL.lastPathComponent, + confidence: .strong, + reason: "Found multiple Java sources", + detectedKinds: [.mod] + ) + let access = SourceAccessCoordinator( + accessMethods: [ + LocalFolderSourceAccess(), + JavaLocalFolderSourceAccess() + ] + ) + let library = SourceLibrary(sourceAccessMethod: access) + + let sourceID = await library.addSource(candidate: candidate) + guard let source = library.source(withID: sourceID) else { + Issue.record("Expected added source") + return + } + + #expect(source.folderURL == workingURL.standardizedFileURL) + #expect(source.edition == .java) + #expect(source.providerID == JavaLocalFolderSourceAccess().accessorIdentifier) + #expect(source.accessDescriptor.accessorIdentifier == JavaLocalFolderSourceAccess().accessorIdentifier) + + let items = try JavaContentScanner.discoverItems(in: source.folderURL) + #expect(items.contains { $0.contentKind == .mod && $0.folderName == "ExampleMod.jar" }) + } + @Test func sourceLibraryAddSourceResolvesJavaWrapperFolder() async throws { let fileManager = FileManager.default let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)