Add outbound source capabilities model
This commit is contained in:
parent
e6f529e0fc
commit
58ed0ca7ca
@ -13,6 +13,7 @@ struct MinecraftSource: Identifiable, Hashable, Sendable {
|
|||||||
var origin: MinecraftSourceOrigin
|
var origin: MinecraftSourceOrigin
|
||||||
var accessDescriptor: SourceAccessDescriptor
|
var accessDescriptor: SourceAccessDescriptor
|
||||||
var availability: SourceAvailability
|
var availability: SourceAvailability
|
||||||
|
var capabilities: SourceCapabilities
|
||||||
var bookmarkData: Data?
|
var bookmarkData: Data?
|
||||||
var displayName: String
|
var displayName: String
|
||||||
var displayItems: [MinecraftContentItem]
|
var displayItems: [MinecraftContentItem]
|
||||||
@ -56,6 +57,7 @@ struct MinecraftSource: Identifiable, Hashable, Sendable {
|
|||||||
refreshStrategy: resolvedOrigin.defaultRefreshStrategy
|
refreshStrategy: resolvedOrigin.defaultRefreshStrategy
|
||||||
)
|
)
|
||||||
self.availability = availability
|
self.availability = availability
|
||||||
|
self.capabilities = resolvedOrigin.defaultCapabilities
|
||||||
self.bookmarkData = bookmarkData
|
self.bookmarkData = bookmarkData
|
||||||
self.displayName = normalizedFolderURL.lastPathComponent
|
self.displayName = normalizedFolderURL.lastPathComponent
|
||||||
self.displayItems = []
|
self.displayItems = []
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// SourceCapabilities.swift
|
||||||
|
// World Manager for Minecraft
|
||||||
|
//
|
||||||
|
// Created by OpenAI on 2026-05-29.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct SourceCapabilities: Hashable, Sendable, Codable {
|
||||||
|
var canScan: Bool = true
|
||||||
|
var canMaterializeItems: Bool = true
|
||||||
|
var canExportPortablePackages: Bool = true
|
||||||
|
|
||||||
|
static let localFolder = SourceCapabilities(
|
||||||
|
canScan: true,
|
||||||
|
canMaterializeItems: true,
|
||||||
|
canExportPortablePackages: true
|
||||||
|
)
|
||||||
|
|
||||||
|
static let connectedDevice = SourceCapabilities(
|
||||||
|
canScan: true,
|
||||||
|
canMaterializeItems: true,
|
||||||
|
canExportPortablePackages: true
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -77,6 +77,15 @@ nonisolated enum MinecraftSourceOrigin: Hashable, Sendable, Codable {
|
|||||||
return .staged
|
return .staged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated var defaultCapabilities: SourceCapabilities {
|
||||||
|
switch self {
|
||||||
|
case .localFolder:
|
||||||
|
return .localFolder
|
||||||
|
case .connectedDevice:
|
||||||
|
return .connectedDevice
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated enum MinecraftSourceKind: String, Hashable, Sendable, Codable {
|
nonisolated enum MinecraftSourceKind: String, Hashable, Sendable, Codable {
|
||||||
|
|||||||
@ -131,6 +131,7 @@ final class SourceLibrary: ObservableObject, SourceScanSessionHosting, SourcePer
|
|||||||
source.bookmarkData = bookmarkData
|
source.bookmarkData = bookmarkData
|
||||||
}
|
}
|
||||||
source.accessDescriptor = sourceAccessMethod.accessDescriptor(for: source)
|
source.accessDescriptor = sourceAccessMethod.accessDescriptor(for: source)
|
||||||
|
source.capabilities = source.origin.defaultCapabilities
|
||||||
}
|
}
|
||||||
startScan(for: normalizedURL, mode: .fullScan)
|
startScan(for: normalizedURL, mode: .fullScan)
|
||||||
return normalizedURL
|
return normalizedURL
|
||||||
@ -155,6 +156,7 @@ final class SourceLibrary: ObservableObject, SourceScanSessionHosting, SourcePer
|
|||||||
existingSource.origin = source.origin
|
existingSource.origin = source.origin
|
||||||
existingSource.accessDescriptor = source.accessDescriptor
|
existingSource.accessDescriptor = source.accessDescriptor
|
||||||
existingSource.availability = source.availability
|
existingSource.availability = source.availability
|
||||||
|
existingSource.capabilities = source.capabilities
|
||||||
if existingSource.bookmarkData == nil {
|
if existingSource.bookmarkData == nil {
|
||||||
existingSource.bookmarkData = source.bookmarkData
|
existingSource.bookmarkData = source.bookmarkData
|
||||||
}
|
}
|
||||||
@ -165,6 +167,7 @@ final class SourceLibrary: ObservableObject, SourceScanSessionHosting, SourcePer
|
|||||||
} else {
|
} else {
|
||||||
var resolvedSource = source
|
var resolvedSource = source
|
||||||
resolvedSource.accessDescriptor = sourceAccessMethod.accessDescriptor(for: resolvedSource)
|
resolvedSource.accessDescriptor = sourceAccessMethod.accessDescriptor(for: resolvedSource)
|
||||||
|
resolvedSource.capabilities = resolvedSource.origin.defaultCapabilities
|
||||||
sources.append(resolvedSource)
|
sources.append(resolvedSource)
|
||||||
sources.sort { $0.displayName.localizedStandardCompare($1.displayName) == .orderedAscending }
|
sources.sort { $0.displayName.localizedStandardCompare($1.displayName) == .orderedAscending }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,11 @@ struct AppleMobileDeviceSourceAccess: ConnectedDeviceSourceAccessMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities {
|
||||||
|
_ = source
|
||||||
|
return .connectedDevice
|
||||||
|
}
|
||||||
|
|
||||||
nonisolated func listConnectedDevices() async throws -> [ConnectedDevice] {
|
nonisolated func listConnectedDevices() async throws -> [ConnectedDevice] {
|
||||||
let devices = try await AppleMobileDeviceAccess.connectedDevices()
|
let devices = try await AppleMobileDeviceAccess.connectedDevices()
|
||||||
return devices.compactMap { device in
|
return devices.compactMap { device in
|
||||||
|
|||||||
@ -16,6 +16,7 @@ protocol SourceAccessMethod: Sendable {
|
|||||||
nonisolated var accessorIdentifier: SourceAccessorIdentifier { get }
|
nonisolated var accessorIdentifier: SourceAccessorIdentifier { get }
|
||||||
nonisolated func accessDescriptor(for source: MinecraftSource) -> SourceAccessDescriptor
|
nonisolated func accessDescriptor(for source: MinecraftSource) -> SourceAccessDescriptor
|
||||||
nonisolated func availability(for source: MinecraftSource) async -> SourceAvailability
|
nonisolated func availability(for source: MinecraftSource) async -> SourceAvailability
|
||||||
|
nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities
|
||||||
nonisolated func discoverItems(
|
nonisolated func discoverItems(
|
||||||
for source: MinecraftSource,
|
for source: MinecraftSource,
|
||||||
mode: SourceDiscoveryMode,
|
mode: SourceDiscoveryMode,
|
||||||
@ -49,6 +50,10 @@ extension SourceAccessMethod {
|
|||||||
return .unknown
|
return .unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities {
|
||||||
|
source.origin.defaultCapabilities
|
||||||
|
}
|
||||||
|
|
||||||
nonisolated func discoverItems(
|
nonisolated func discoverItems(
|
||||||
for source: MinecraftSource,
|
for source: MinecraftSource,
|
||||||
mode: SourceDiscoveryMode,
|
mode: SourceDiscoveryMode,
|
||||||
@ -171,6 +176,10 @@ struct SourceAccessCoordinator: SourceAccessMethod {
|
|||||||
return await accessMethod(for: source).availability(for: source)
|
return await accessMethod(for: source).availability(for: source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities {
|
||||||
|
return await accessMethod(for: source).capabilities(for: source)
|
||||||
|
}
|
||||||
|
|
||||||
nonisolated func enrich(_ item: MinecraftContentItem, for source: MinecraftSource) async -> MinecraftContentItem {
|
nonisolated func enrich(_ item: MinecraftContentItem, for source: MinecraftSource) async -> MinecraftContentItem {
|
||||||
return await accessMethod(for: source).enrich(item, for: source)
|
return await accessMethod(for: source).enrich(item, for: source)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,11 @@ struct LocalFolderSourceAccess: SourceAccessMethod {
|
|||||||
return FileManager.default.fileExists(atPath: candidateURL.path) ? .available : .unavailable
|
return FileManager.default.fileExists(atPath: candidateURL.path) ? .available : .unavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities {
|
||||||
|
_ = source
|
||||||
|
return .localFolder
|
||||||
|
}
|
||||||
|
|
||||||
nonisolated func discoverItems(
|
nonisolated func discoverItems(
|
||||||
for source: MinecraftSource,
|
for source: MinecraftSource,
|
||||||
mode: SourceDiscoveryMode,
|
mode: SourceDiscoveryMode,
|
||||||
|
|||||||
@ -12,6 +12,33 @@ import Testing
|
|||||||
@MainActor
|
@MainActor
|
||||||
struct World_Manager_for_MinecraftTests {
|
struct World_Manager_for_MinecraftTests {
|
||||||
|
|
||||||
|
@Test func sourceOriginsExposeOutboundCapabilities() async throws {
|
||||||
|
let localSource = MinecraftSource(folderURL: URL(fileURLWithPath: "/tmp/local"))
|
||||||
|
#expect(localSource.capabilities == .localFolder)
|
||||||
|
|
||||||
|
let device = ConnectedDevice(
|
||||||
|
udid: "device",
|
||||||
|
name: "Device",
|
||||||
|
productType: nil,
|
||||||
|
osVersion: nil,
|
||||||
|
connection: .usb,
|
||||||
|
trustState: .trusted
|
||||||
|
)
|
||||||
|
let container = DeviceAppContainer(
|
||||||
|
deviceUDID: device.udid,
|
||||||
|
appID: "com.mojang.minecraftpe",
|
||||||
|
appName: "Minecraft",
|
||||||
|
accessMode: .documents,
|
||||||
|
minecraftFolderRelativePath: "Documents/games/com.mojang"
|
||||||
|
)
|
||||||
|
let deviceSource = MinecraftSource(
|
||||||
|
folderURL: URL(fileURLWithPath: "/tmp/device"),
|
||||||
|
origin: .connectedDevice(device: device, container: container)
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(deviceSource.capabilities == .connectedDevice)
|
||||||
|
}
|
||||||
|
|
||||||
@Test func packIdentityUsesUUIDAndVersion() async throws {
|
@Test func packIdentityUsesUUIDAndVersion() async throws {
|
||||||
let first = PackIdentity(
|
let first = PackIdentity(
|
||||||
type: .behaviorPack,
|
type: .behaviorPack,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user