implements PR-027 runtime walkresult and cache integration
This commit is contained in:
parent
1442adc7b3
commit
b4733a0e49
@ -53,6 +53,9 @@ public enum OutputFormatCatalog {
|
||||
return UNKNOWN;
|
||||
}
|
||||
final String normalized = manifestValue.trim().toLowerCase(Locale.ROOT);
|
||||
if ("sound/bank_v1".equals(normalized)) {
|
||||
return SOUND_V1;
|
||||
}
|
||||
for (OutputFormatCatalog candidate : values()) {
|
||||
if (candidate == UNKNOWN) {
|
||||
continue;
|
||||
|
||||
@ -2,7 +2,9 @@ package p.packer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import p.packer.events.PackerEventSink;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerAssetWalker;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
import p.packer.repositories.PackerRuntimeLoader;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
import p.packer.services.*;
|
||||
@ -29,7 +31,13 @@ public final class Packer implements Closeable {
|
||||
final var workspaceFoundation = new PackerWorkspaceFoundation(mapper);
|
||||
final var declarationParser = new PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
final var runtimeLoader = new PackerRuntimeLoader(workspaceFoundation, declarationParser, assetWalker);
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
final var runtimeLoader = new PackerRuntimeLoader(
|
||||
workspaceFoundation,
|
||||
declarationParser,
|
||||
cacheRepository,
|
||||
assetMaterializer);
|
||||
final var runtimeRegistry = new PackerRuntimeRegistry(runtimeLoader);
|
||||
final var assetReferenceResolver = new PackerAssetReferenceResolver(workspaceFoundation.lookup());
|
||||
final var assetDetailsService = new PackerAssetDetailsService(runtimeRegistry, assetReferenceResolver);
|
||||
@ -37,7 +45,7 @@ public final class Packer implements Closeable {
|
||||
runtimeRegistry,
|
||||
assetReferenceResolver,
|
||||
workspaceFoundation.lookup());
|
||||
final var runtimePatchService = new PackerRuntimePatchService(declarationParser);
|
||||
final var runtimePatchService = new PackerRuntimePatchService(declarationParser, assetMaterializer);
|
||||
final var writeCoordinator = new PackerProjectWriteCoordinator();
|
||||
return new Packer(new FileSystemPackerWorkspaceService(
|
||||
mapper,
|
||||
@ -46,6 +54,7 @@ public final class Packer implements Closeable {
|
||||
assetActionReadService,
|
||||
runtimePatchService,
|
||||
runtimeRegistry,
|
||||
cacheRepository,
|
||||
writeCoordinator,
|
||||
resolvedEventSink), runtimeRegistry, writeCoordinator);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.packer.models;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -8,12 +9,24 @@ public record PackerRuntimeAsset(
|
||||
Path assetRoot,
|
||||
Path manifestPath,
|
||||
Optional<PackerRegistryEntry> registryEntry,
|
||||
PackerAssetDeclarationParseResult parsedDeclaration) {
|
||||
PackerAssetDeclarationParseResult parsedDeclaration,
|
||||
PackerRuntimeWalkProjection walkProjection,
|
||||
List<PackerDiagnostic> walkDiagnostics) {
|
||||
|
||||
public PackerRuntimeAsset(
|
||||
Path assetRoot,
|
||||
Path manifestPath,
|
||||
Optional<PackerRegistryEntry> registryEntry,
|
||||
PackerAssetDeclarationParseResult parsedDeclaration) {
|
||||
this(assetRoot, manifestPath, registryEntry, parsedDeclaration, PackerRuntimeWalkProjection.EMPTY, List.of());
|
||||
}
|
||||
|
||||
public PackerRuntimeAsset {
|
||||
assetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
||||
manifestPath = Objects.requireNonNull(manifestPath, "manifestPath").toAbsolutePath().normalize();
|
||||
registryEntry = Objects.requireNonNull(registryEntry, "registryEntry");
|
||||
parsedDeclaration = Objects.requireNonNull(parsedDeclaration, "parsedDeclaration");
|
||||
walkProjection = Objects.requireNonNull(walkProjection, "walkProjection");
|
||||
walkDiagnostics = List.copyOf(Objects.requireNonNull(walkDiagnostics, "walkDiagnostics"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,15 @@ import java.util.Objects;
|
||||
public record PackerRuntimeSnapshot(
|
||||
long generation,
|
||||
PackerRegistryState registry,
|
||||
List<PackerRuntimeAsset> assets) {
|
||||
List<PackerRuntimeAsset> assets,
|
||||
PackerWorkspaceCacheState cacheState) {
|
||||
|
||||
public PackerRuntimeSnapshot(
|
||||
long generation,
|
||||
PackerRegistryState registry,
|
||||
List<PackerRuntimeAsset> assets) {
|
||||
this(generation, registry, assets, PackerWorkspaceCacheState.EMPTY);
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot {
|
||||
if (generation <= 0L) {
|
||||
@ -14,5 +22,6 @@ public record PackerRuntimeSnapshot(
|
||||
}
|
||||
registry = Objects.requireNonNull(registry, "registry");
|
||||
assets = List.copyOf(Objects.requireNonNull(assets, "assets"));
|
||||
cacheState = Objects.requireNonNull(cacheState, "cacheState");
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package p.packer.models;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public record PackerRuntimeWalkFile(
|
||||
String relativePath,
|
||||
String mimeType,
|
||||
long size,
|
||||
long lastModified,
|
||||
String fingerprint,
|
||||
Map<String, Object> metadata,
|
||||
List<PackerDiagnostic> diagnostics) {
|
||||
|
||||
public PackerRuntimeWalkFile {
|
||||
relativePath = Objects.requireNonNull(relativePath, "relativePath");
|
||||
mimeType = mimeType == null || mimeType.isBlank() ? null : mimeType;
|
||||
if (size < 0L) {
|
||||
throw new IllegalArgumentException("size must be non-negative");
|
||||
}
|
||||
if (lastModified < 0L) {
|
||||
throw new IllegalArgumentException("lastModified must be non-negative");
|
||||
}
|
||||
fingerprint = fingerprint == null || fingerprint.isBlank() ? null : fingerprint;
|
||||
metadata = Map.copyOf(new LinkedHashMap<>(Objects.requireNonNull(metadata, "metadata")));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package p.packer.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record PackerRuntimeWalkProjection(
|
||||
List<String> availableFiles,
|
||||
List<PackerRuntimeWalkFile> buildCandidateFiles,
|
||||
long measuredBankSizeBytes) {
|
||||
public static final PackerRuntimeWalkProjection EMPTY = new PackerRuntimeWalkProjection(List.of(), List.of(), 0L);
|
||||
|
||||
public PackerRuntimeWalkProjection {
|
||||
availableFiles = List.copyOf(Objects.requireNonNull(availableFiles, "availableFiles"));
|
||||
buildCandidateFiles = List.copyOf(Objects.requireNonNull(buildCandidateFiles, "buildCandidateFiles"));
|
||||
if (measuredBankSizeBytes < 0L) {
|
||||
throw new IllegalArgumentException("measuredBankSizeBytes must be non-negative");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,9 +14,6 @@ import p.packer.models.PackerWalkResult;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -132,12 +129,7 @@ public abstract class PackerAbstractBankWalker<R> {
|
||||
}
|
||||
|
||||
protected String computeFingerprint(final PackerFileProbe fileProbe) {
|
||||
try {
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return HexFormat.of().formatHex(digest.digest(fileProbe.content()));
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException("SHA-256 fingerprint is unavailable", exception);
|
||||
}
|
||||
return PackerFileFingerprint.sha256(fileProbe);
|
||||
}
|
||||
|
||||
private Optional<PackerFileCacheEntry> resolvePriorFileCache(
|
||||
|
||||
@ -146,7 +146,7 @@ public class PackerAssetWalker {
|
||||
return RequirementBuildResult.fail("Missing sample rate for sound bank");
|
||||
}
|
||||
final var sampleRate = Integer.parseInt(sampleRateStr);
|
||||
final var channelsStr = metadata.get("");
|
||||
final var channelsStr = metadata.get("channels");
|
||||
if (StringUtils.isBlank(channelsStr)) {
|
||||
return RequirementBuildResult.fail("Missing channels for sound bank");
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package p.packer.repositories;
|
||||
|
||||
import p.packer.models.PackerFileProbe;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
final class PackerFileFingerprint {
|
||||
private PackerFileFingerprint() {
|
||||
}
|
||||
|
||||
static String sha256(final PackerFileProbe fileProbe) {
|
||||
try {
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return HexFormat.of().formatHex(digest.digest(fileProbe.content()));
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException("SHA-256 fingerprint is unavailable", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package p.packer.repositories;
|
||||
|
||||
import p.packer.models.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public final class PackerRuntimeAssetMaterializer {
|
||||
private final PackerAssetWalker assetWalker;
|
||||
|
||||
public PackerRuntimeAssetMaterializer(PackerAssetWalker assetWalker) {
|
||||
this.assetWalker = Objects.requireNonNull(assetWalker, "assetWalker");
|
||||
}
|
||||
|
||||
public PackerRuntimeAssetMaterialization materialize(
|
||||
Path assetRoot,
|
||||
Path manifestPath,
|
||||
Optional<PackerRegistryEntry> registryEntry,
|
||||
PackerAssetDeclarationParseResult parseResult,
|
||||
Optional<PackerAssetCacheEntry> priorAssetCache) {
|
||||
final Path normalizedAssetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
||||
final Path normalizedManifestPath = Objects.requireNonNull(manifestPath, "manifestPath").toAbsolutePath().normalize();
|
||||
final Optional<PackerRegistryEntry> safeRegistryEntry = Objects.requireNonNull(registryEntry, "registryEntry");
|
||||
final PackerAssetDeclarationParseResult safeParseResult = Objects.requireNonNull(parseResult, "parseResult");
|
||||
final Optional<PackerAssetCacheEntry> safePriorAssetCache = Objects.requireNonNull(priorAssetCache, "priorAssetCache");
|
||||
|
||||
if (safeRegistryEntry.isEmpty() || !safeParseResult.valid()) {
|
||||
return new PackerRuntimeAssetMaterialization(
|
||||
new PackerRuntimeAsset(
|
||||
normalizedAssetRoot,
|
||||
normalizedManifestPath,
|
||||
safeRegistryEntry,
|
||||
safeParseResult,
|
||||
PackerRuntimeWalkProjection.EMPTY,
|
||||
List.of()),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
final List<String> availableFiles = listAvailableFiles(normalizedAssetRoot);
|
||||
final PackerWalkResult walkResult = assetWalker.walk(
|
||||
normalizedAssetRoot,
|
||||
safeParseResult.declaration(),
|
||||
safePriorAssetCache);
|
||||
final List<PackerRuntimeWalkFile> buildCandidateFiles = walkResult.probeResults().stream()
|
||||
.map(probeResult -> toRuntimeWalkFile(normalizedAssetRoot, probeResult))
|
||||
.sorted(Comparator.comparing(PackerRuntimeWalkFile::relativePath, String.CASE_INSENSITIVE_ORDER))
|
||||
.toList();
|
||||
final long measuredBankSizeBytes = buildCandidateFiles.stream()
|
||||
.mapToLong(PackerRuntimeWalkFile::size)
|
||||
.sum();
|
||||
final PackerRuntimeWalkProjection walkProjection = new PackerRuntimeWalkProjection(
|
||||
availableFiles,
|
||||
buildCandidateFiles,
|
||||
measuredBankSizeBytes);
|
||||
final PackerRuntimeAsset runtimeAsset = new PackerRuntimeAsset(
|
||||
normalizedAssetRoot,
|
||||
normalizedManifestPath,
|
||||
safeRegistryEntry,
|
||||
safeParseResult,
|
||||
walkProjection,
|
||||
walkResult.diagnostics());
|
||||
final PackerAssetCacheEntry assetCacheEntry = new PackerAssetCacheEntry(
|
||||
safeRegistryEntry.get().assetId(),
|
||||
buildCandidateFiles.stream()
|
||||
.map(file -> new PackerFileCacheEntry(
|
||||
file.relativePath(),
|
||||
file.mimeType(),
|
||||
file.size(),
|
||||
file.lastModified(),
|
||||
file.fingerprint(),
|
||||
file.metadata()))
|
||||
.toList());
|
||||
return new PackerRuntimeAssetMaterialization(runtimeAsset, Optional.of(assetCacheEntry));
|
||||
}
|
||||
|
||||
private List<String> listAvailableFiles(Path assetRoot) {
|
||||
try (var paths = Files.list(assetRoot)
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(path -> !path.getFileName().toString().equalsIgnoreCase("asset.json"))
|
||||
.map(path -> assetRoot.relativize(path.toAbsolutePath().normalize()).toString().replace('\\', '/'))
|
||||
.sorted(String.CASE_INSENSITIVE_ORDER)) {
|
||||
return paths.toList();
|
||||
} catch (IOException exception) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private PackerRuntimeWalkFile toRuntimeWalkFile(Path assetRoot, PackerProbeResult probeResult) {
|
||||
final PackerFileProbe fileProbe = probeResult.fileProbe();
|
||||
return new PackerRuntimeWalkFile(
|
||||
assetRoot.relativize(fileProbe.path().toAbsolutePath().normalize()).toString().replace('\\', '/'),
|
||||
fileProbe.mimeType(),
|
||||
fileProbe.size(),
|
||||
fileProbe.lastModified(),
|
||||
PackerFileFingerprint.sha256(fileProbe),
|
||||
probeResult.metadata(),
|
||||
probeResult.diagnostics());
|
||||
}
|
||||
|
||||
public record PackerRuntimeAssetMaterialization(
|
||||
PackerRuntimeAsset runtimeAsset,
|
||||
Optional<PackerAssetCacheEntry> assetCacheEntry) {
|
||||
public PackerRuntimeAssetMaterialization {
|
||||
runtimeAsset = Objects.requireNonNull(runtimeAsset, "runtimeAsset");
|
||||
assetCacheEntry = Objects.requireNonNull(assetCacheEntry, "assetCacheEntry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,10 @@ package p.packer.repositories;
|
||||
|
||||
import p.packer.messages.InitWorkspaceRequest;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.models.PackerAssetCacheEntry;
|
||||
import p.packer.models.PackerRuntimeAsset;
|
||||
import p.packer.models.PackerRuntimeSnapshot;
|
||||
import p.packer.models.PackerWorkspaceCacheState;
|
||||
import p.packer.services.PackerAssetDeclarationParser;
|
||||
import p.packer.services.PackerWorkspaceFoundation;
|
||||
import p.packer.PackerWorkspacePaths;
|
||||
@ -18,15 +20,18 @@ import java.util.stream.Collectors;
|
||||
public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
private final PackerAssetDeclarationParser parser;
|
||||
private final PackerAssetWalker assetWalker;
|
||||
private final FileSystemPackerCacheRepository cacheRepository;
|
||||
private final PackerRuntimeAssetMaterializer assetMaterializer;
|
||||
|
||||
public PackerRuntimeLoader(
|
||||
final PackerWorkspaceFoundation workspaceFoundation,
|
||||
final PackerAssetDeclarationParser parser,
|
||||
final PackerAssetWalker assetWalker) {
|
||||
final FileSystemPackerCacheRepository cacheRepository,
|
||||
final PackerRuntimeAssetMaterializer assetMaterializer) {
|
||||
this.workspaceFoundation = Objects.requireNonNull(workspaceFoundation, "workspaceFoundation");
|
||||
this.parser = Objects.requireNonNull(parser, "parser");
|
||||
this.assetWalker = Objects.requireNonNull(assetWalker, "assetWalker");
|
||||
this.cacheRepository = Objects.requireNonNull(cacheRepository, "cacheRepository");
|
||||
this.assetMaterializer = Objects.requireNonNull(assetMaterializer, "assetMaterializer");
|
||||
}
|
||||
|
||||
private boolean isAssetJson(Path path, BasicFileAttributes attrs) {
|
||||
@ -39,6 +44,7 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
workspaceFoundation.initWorkspace(new InitWorkspaceRequest(safeProject));
|
||||
|
||||
final var registry = workspaceFoundation.loadRegistry(safeProject);
|
||||
final PackerWorkspaceCacheState priorCacheState = loadPriorCacheState(safeProject);
|
||||
final var registryByRoot = registry
|
||||
.assets()
|
||||
.stream()
|
||||
@ -47,6 +53,7 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
entry -> entry));
|
||||
|
||||
final List<PackerRuntimeAsset> assets = new ArrayList<>();
|
||||
final List<PackerAssetCacheEntry> refreshedCacheEntries = new ArrayList<>();
|
||||
final var assetsRoot = PackerWorkspacePaths.assetsRoot(safeProject);
|
||||
if (Files.isDirectory(assetsRoot)) {
|
||||
try (final var paths = Files.find(assetsRoot, Integer.MAX_VALUE, this::isAssetJson)) {
|
||||
@ -58,12 +65,16 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
final var assetRoot = manifestPath.getParent();
|
||||
final var registryEntry = Optional.ofNullable(registryByRoot.get(assetRoot));
|
||||
final var parseResult = parser.parse(manifestPath);
|
||||
if (parseResult.valid()) {
|
||||
final var walkResult = assetWalker.walk(assetRoot, parseResult.declaration());
|
||||
|
||||
}
|
||||
final var runtimeAsset = new PackerRuntimeAsset(assetRoot, manifestPath, registryEntry, parseResult);
|
||||
assets.add(runtimeAsset);
|
||||
final Optional<PackerAssetCacheEntry> priorAssetCache = registryEntry
|
||||
.flatMap(entry -> priorCacheState.findAsset(entry.assetId()));
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
registryEntry,
|
||||
parseResult,
|
||||
priorAssetCache);
|
||||
assets.add(materialized.runtimeAsset());
|
||||
materialized.assetCacheEntry().ifPresent(refreshedCacheEntries::add);
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
throw new p.packer.exceptions.PackerRegistryException(
|
||||
@ -72,6 +83,20 @@ public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
}
|
||||
}
|
||||
|
||||
return new PackerRuntimeSnapshot(generation, registry, assets);
|
||||
final PackerWorkspaceCacheState refreshedCacheState = new PackerWorkspaceCacheState(
|
||||
PackerWorkspaceCacheState.CURRENT_SCHEMA_VERSION,
|
||||
refreshedCacheEntries.stream()
|
||||
.sorted(Comparator.comparingInt(PackerAssetCacheEntry::assetId))
|
||||
.toList());
|
||||
cacheRepository.save(safeProject, refreshedCacheState);
|
||||
return new PackerRuntimeSnapshot(generation, registry, assets, refreshedCacheState);
|
||||
}
|
||||
|
||||
private PackerWorkspaceCacheState loadPriorCacheState(PackerProjectContext project) {
|
||||
try {
|
||||
return cacheRepository.load(project);
|
||||
} catch (p.packer.exceptions.PackerRegistryException exception) {
|
||||
return PackerWorkspaceCacheState.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import p.packer.messages.assets.*;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
|
||||
import p.packer.messages.diagnostics.PackerDiagnosticSeverity;
|
||||
import p.packer.models.*;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -28,6 +29,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
private final PackerAssetActionReadService actionReadService;
|
||||
private final PackerRuntimePatchService runtimePatchService;
|
||||
private final PackerRuntimeRegistry runtimeRegistry;
|
||||
private final FileSystemPackerCacheRepository cacheRepository;
|
||||
private final PackerProjectWriteCoordinator writeCoordinator;
|
||||
private final PackerEventSink eventSink;
|
||||
|
||||
@ -38,6 +40,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
PackerAssetActionReadService actionReadService,
|
||||
PackerRuntimePatchService runtimePatchService,
|
||||
PackerRuntimeRegistry runtimeRegistry,
|
||||
FileSystemPackerCacheRepository cacheRepository,
|
||||
PackerProjectWriteCoordinator writeCoordinator,
|
||||
PackerEventSink eventSink) {
|
||||
this.mapper = Objects.requireNonNull(mapper, "mapper");
|
||||
@ -46,6 +49,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
this.actionReadService = Objects.requireNonNull(actionReadService, "actionReadService");
|
||||
this.runtimePatchService = Objects.requireNonNull(runtimePatchService, "runtimePatchService");
|
||||
this.runtimeRegistry = Objects.requireNonNull(runtimeRegistry, "runtimeRegistry");
|
||||
this.cacheRepository = Objects.requireNonNull(cacheRepository, "cacheRepository");
|
||||
this.writeCoordinator = Objects.requireNonNull(writeCoordinator, "writeCoordinator");
|
||||
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
|
||||
}
|
||||
@ -84,6 +88,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
final var registryEntry = registryByRoot.get(assetRoot);
|
||||
final var parsed = runtimeAsset.parsedDeclaration();
|
||||
diagnostics.addAll(parsed.diagnostics());
|
||||
diagnostics.addAll(runtimeAsset.walkDiagnostics());
|
||||
diagnostics.addAll(identityMismatchDiagnostics(registryEntry, parsed, assetManifestPath));
|
||||
final var summary = buildSummary(project, assetRoot, registryEntry, parsed);
|
||||
assets.add(summary);
|
||||
@ -220,13 +225,14 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
writeManifest(manifestPath, request, entry.assetUuid());
|
||||
final PackerRegistryState updated = workspaceFoundation.appendAllocatedEntry(registry, entry);
|
||||
workspaceFoundation.saveRegistry(project, updated);
|
||||
runtimeRegistry.update(project, (snapshot, generation) -> runtimePatchService.afterCreateAsset(
|
||||
final var runtime = runtimeRegistry.update(project, (snapshot, generation) -> runtimePatchService.afterCreateAsset(
|
||||
snapshot,
|
||||
generation,
|
||||
updated,
|
||||
entry,
|
||||
assetRoot,
|
||||
manifestPath));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
final CreateAssetResult result = new CreateAssetResult(
|
||||
PackerOperationStatus.SUCCESS,
|
||||
"Asset created: " + relativeAssetRoot,
|
||||
@ -270,12 +276,13 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
declaration.assetUuid());
|
||||
final PackerRegistryState updated = workspaceFoundation.appendAllocatedEntry(registry, entry);
|
||||
workspaceFoundation.saveRegistry(project, updated);
|
||||
runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterRegisterAsset(
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterRegisterAsset(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
updated,
|
||||
entry,
|
||||
assetRoot));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
final RegisterAssetResult result = new RegisterAssetResult(
|
||||
PackerOperationStatus.SUCCESS,
|
||||
"Asset registered: " + relativeAssetRoot,
|
||||
@ -316,11 +323,12 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
if (!updatedRegistry.equals(registry)) {
|
||||
workspaceFoundation.saveRegistry(project, updatedRegistry);
|
||||
}
|
||||
runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterDeleteAsset(
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterDeleteAsset(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
updatedRegistry,
|
||||
assetRoot));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
final DeleteAssetResult result = new DeleteAssetResult(
|
||||
PackerOperationStatus.SUCCESS,
|
||||
"Asset deleted: " + relativeAssetRoot,
|
||||
@ -372,7 +380,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
if (!updatedRegistry.equals(registry)) {
|
||||
workspaceFoundation.saveRegistry(project, updatedRegistry);
|
||||
}
|
||||
runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterMoveAsset(
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterMoveAsset(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
updatedRegistry,
|
||||
@ -380,6 +388,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
sourceRoot,
|
||||
targetRoot,
|
||||
targetManifestPath));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
final AssetReference canonicalReference = updatedEntry
|
||||
.map(packerRegistryEntry -> AssetReference.forAssetId(packerRegistryEntry.assetId()))
|
||||
.orElseGet(() -> AssetReference.forRelativeAssetRoot(targetRelativeRoot));
|
||||
@ -558,6 +567,10 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return PackerWorkspacePaths.relativeAssetRoot(project, assetRoot).replace('\\', '/');
|
||||
}
|
||||
|
||||
private void saveRuntimeCache(PackerProjectContext project, PackerRuntimeSnapshot snapshot) {
|
||||
cacheRepository.save(project, snapshot.cacheState());
|
||||
}
|
||||
|
||||
private PackerRegistryState removeRegistryEntry(
|
||||
PackerRegistryState registry,
|
||||
PackerRegistryEntry entry) {
|
||||
@ -628,12 +641,13 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
try {
|
||||
patchManifestContract(manifest, request);
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterUpdateAssetContract(
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterUpdateAssetContract(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
evaluation.resolved().registryEntry()));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
return new UpdateAssetContractResponse(true, null);
|
||||
} catch (IOException exception) {
|
||||
return new UpdateAssetContractResponse(false, "Unable to update asset contract: " + exception.getMessage());
|
||||
|
||||
@ -42,6 +42,7 @@ public final class PackerAssetDetailsService {
|
||||
final var manifestPath = runtimeAsset.manifestPath();
|
||||
final var parsed = runtimeAsset.parsedDeclaration();
|
||||
diagnostics.addAll(parsed.diagnostics());
|
||||
diagnostics.addAll(runtimeAsset.walkDiagnostics());
|
||||
if (!parsed.valid()) {
|
||||
return failureResult(project, request.assetReference(), resolved, diagnostics);
|
||||
}
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
package p.packer.services;
|
||||
|
||||
import p.packer.models.*;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class PackerRuntimePatchService {
|
||||
private final PackerAssetDeclarationParser declarationParser;
|
||||
private final PackerRuntimeAssetMaterializer assetMaterializer;
|
||||
|
||||
public PackerRuntimePatchService(PackerAssetDeclarationParser declarationParser) {
|
||||
public PackerRuntimePatchService(
|
||||
PackerAssetDeclarationParser declarationParser,
|
||||
PackerRuntimeAssetMaterializer assetMaterializer) {
|
||||
this.declarationParser = Objects.requireNonNull(declarationParser, "declarationParser");
|
||||
this.assetMaterializer = Objects.requireNonNull(assetMaterializer, "assetMaterializer");
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterCreateAsset(
|
||||
@ -23,13 +29,19 @@ public final class PackerRuntimePatchService {
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>(snapshot.assets().stream()
|
||||
.filter(candidate -> !candidate.assetRoot().equals(assetRoot.toAbsolutePath().normalize()))
|
||||
.toList());
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
Optional.of(entry),
|
||||
parsed));
|
||||
parsed,
|
||||
snapshot.cacheState().findAsset(entry.assetId()));
|
||||
updatedAssets.add(materialized.runtimeAsset());
|
||||
updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
return new PackerRuntimeSnapshot(
|
||||
generation,
|
||||
updatedRegistry,
|
||||
updatedAssets,
|
||||
mergeCacheState(snapshot.cacheState(), updatedRegistry, materialized.assetCacheEntry()));
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterRegisterAsset(
|
||||
@ -40,13 +52,17 @@ public final class PackerRuntimePatchService {
|
||||
Path assetRoot) {
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
|
||||
boolean patched = false;
|
||||
Optional<PackerAssetCacheEntry> refreshedCacheEntry = Optional.empty();
|
||||
for (PackerRuntimeAsset asset : snapshot.assets()) {
|
||||
if (asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize())) {
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
asset.assetRoot(),
|
||||
asset.manifestPath(),
|
||||
Optional.of(entry),
|
||||
asset.parsedDeclaration()));
|
||||
asset.parsedDeclaration(),
|
||||
snapshot.cacheState().findAsset(entry.assetId()));
|
||||
updatedAssets.add(materialized.runtimeAsset());
|
||||
refreshedCacheEntry = materialized.assetCacheEntry();
|
||||
patched = true;
|
||||
} else {
|
||||
updatedAssets.add(asset);
|
||||
@ -55,7 +71,11 @@ public final class PackerRuntimePatchService {
|
||||
if (!patched) {
|
||||
throw new IllegalStateException("Unable to patch runtime snapshot for unregistered asset: " + assetRoot);
|
||||
}
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
return new PackerRuntimeSnapshot(
|
||||
generation,
|
||||
updatedRegistry,
|
||||
updatedAssets,
|
||||
mergeCacheState(snapshot.cacheState(), updatedRegistry, refreshedCacheEntry));
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterDeleteAsset(
|
||||
@ -66,7 +86,11 @@ public final class PackerRuntimePatchService {
|
||||
final List<PackerRuntimeAsset> updatedAssets = snapshot.assets().stream()
|
||||
.filter(asset -> !asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize()))
|
||||
.toList();
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
return new PackerRuntimeSnapshot(
|
||||
generation,
|
||||
updatedRegistry,
|
||||
updatedAssets,
|
||||
pruneCacheState(snapshot.cacheState(), updatedRegistry));
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterMoveAsset(
|
||||
@ -80,13 +104,17 @@ public final class PackerRuntimePatchService {
|
||||
final PackerAssetDeclarationParseResult parsed = declarationParser.parse(targetManifestPath);
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
|
||||
boolean patched = false;
|
||||
Optional<PackerAssetCacheEntry> refreshedCacheEntry = Optional.empty();
|
||||
for (PackerRuntimeAsset asset : snapshot.assets()) {
|
||||
if (asset.assetRoot().equals(sourceRoot.toAbsolutePath().normalize())) {
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
targetRoot,
|
||||
targetManifestPath,
|
||||
updatedRegistryEntry,
|
||||
parsed));
|
||||
parsed,
|
||||
updatedRegistryEntry.flatMap(entry -> snapshot.cacheState().findAsset(entry.assetId())));
|
||||
updatedAssets.add(materialized.runtimeAsset());
|
||||
refreshedCacheEntry = materialized.assetCacheEntry();
|
||||
patched = true;
|
||||
} else {
|
||||
updatedAssets.add(asset);
|
||||
@ -96,7 +124,11 @@ public final class PackerRuntimePatchService {
|
||||
throw new IllegalStateException("Unable to patch runtime snapshot for moved asset: " + sourceRoot);
|
||||
}
|
||||
updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
return new PackerRuntimeSnapshot(
|
||||
generation,
|
||||
updatedRegistry,
|
||||
updatedAssets,
|
||||
mergeCacheState(snapshot.cacheState(), updatedRegistry, refreshedCacheEntry));
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterUpdateAssetContract(
|
||||
@ -108,26 +140,69 @@ public final class PackerRuntimePatchService {
|
||||
final PackerAssetDeclarationParseResult parsed = declarationParser.parse(manifestPath);
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
|
||||
boolean patched = false;
|
||||
Optional<PackerAssetCacheEntry> refreshedCacheEntry = Optional.empty();
|
||||
for (PackerRuntimeAsset asset : snapshot.assets()) {
|
||||
if (asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize())) {
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
asset.assetRoot(),
|
||||
asset.manifestPath(),
|
||||
asset.registryEntry(),
|
||||
parsed));
|
||||
parsed,
|
||||
asset.registryEntry().flatMap(entry -> snapshot.cacheState().findAsset(entry.assetId())));
|
||||
updatedAssets.add(materialized.runtimeAsset());
|
||||
refreshedCacheEntry = materialized.assetCacheEntry();
|
||||
patched = true;
|
||||
} else {
|
||||
updatedAssets.add(asset);
|
||||
}
|
||||
}
|
||||
if (!patched) {
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
final var materialized = assetMaterializer.materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
registryEntry,
|
||||
parsed));
|
||||
parsed,
|
||||
registryEntry.flatMap(entry -> snapshot.cacheState().findAsset(entry.assetId())));
|
||||
updatedAssets.add(materialized.runtimeAsset());
|
||||
refreshedCacheEntry = materialized.assetCacheEntry();
|
||||
updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
|
||||
}
|
||||
return new PackerRuntimeSnapshot(generation, snapshot.registry(), updatedAssets);
|
||||
return new PackerRuntimeSnapshot(
|
||||
generation,
|
||||
snapshot.registry(),
|
||||
updatedAssets,
|
||||
mergeCacheState(snapshot.cacheState(), snapshot.registry(), refreshedCacheEntry));
|
||||
}
|
||||
|
||||
private PackerWorkspaceCacheState mergeCacheState(
|
||||
PackerWorkspaceCacheState currentCacheState,
|
||||
PackerRegistryState registry,
|
||||
Optional<PackerAssetCacheEntry> refreshedCacheEntry) {
|
||||
final Map<Integer, PackerAssetCacheEntry> byAssetId = new LinkedHashMap<>();
|
||||
currentCacheState.assets().forEach(entry -> byAssetId.put(entry.assetId(), entry));
|
||||
refreshedCacheEntry.ifPresent(entry -> byAssetId.put(entry.assetId(), entry));
|
||||
return pruneCacheEntries(byAssetId, registry);
|
||||
}
|
||||
|
||||
private PackerWorkspaceCacheState pruneCacheState(
|
||||
PackerWorkspaceCacheState currentCacheState,
|
||||
PackerRegistryState registry) {
|
||||
final Map<Integer, PackerAssetCacheEntry> byAssetId = new LinkedHashMap<>();
|
||||
currentCacheState.assets().forEach(entry -> byAssetId.put(entry.assetId(), entry));
|
||||
return pruneCacheEntries(byAssetId, registry);
|
||||
}
|
||||
|
||||
private PackerWorkspaceCacheState pruneCacheEntries(
|
||||
Map<Integer, PackerAssetCacheEntry> byAssetId,
|
||||
PackerRegistryState registry) {
|
||||
final Set<Integer> activeAssetIds = registry.assets().stream()
|
||||
.map(PackerRegistryEntry::assetId)
|
||||
.collect(Collectors.toSet());
|
||||
return new PackerWorkspaceCacheState(
|
||||
PackerWorkspaceCacheState.CURRENT_SCHEMA_VERSION,
|
||||
byAssetId.values().stream()
|
||||
.filter(entry -> activeAssetIds.contains(entry.assetId()))
|
||||
.sorted(Comparator.comparingInt(PackerAssetCacheEntry::assetId))
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@ import p.packer.events.PackerEvent;
|
||||
import p.packer.events.PackerEventKind;
|
||||
import p.packer.messages.*;
|
||||
import p.packer.messages.assets.*;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerAssetWalker;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
import p.packer.repositories.PackerRuntimeLoader;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
import p.packer.repositories.PackerRuntimeSnapshotLoader;
|
||||
@ -138,7 +140,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(PackerAssetState.REGISTERED, detailsResult.details().summary().state());
|
||||
assertEquals("new_atlas", detailsResult.details().summary().identity().assetName());
|
||||
assertNotNull(detailsResult.details().summary().identity().assetUuid());
|
||||
assertTrue(detailsResult.diagnostics().isEmpty());
|
||||
assertTrue(detailsResult.diagnostics().stream().noneMatch(diagnostic -> diagnostic.blocking()));
|
||||
assertTrue(detailsResult.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for tile bank cannot be empty")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -679,11 +682,14 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
final var mapper = new ObjectMapper();
|
||||
final var foundation = new p.packer.services.PackerWorkspaceFoundation(mapper);
|
||||
final var parser = new p.packer.services.PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
final var runtimeRegistry = new PackerRuntimeRegistry(loader);
|
||||
final var resolver = new p.packer.services.PackerAssetReferenceResolver(foundation.lookup());
|
||||
final var detailsService = new p.packer.services.PackerAssetDetailsService(runtimeRegistry, resolver);
|
||||
final var actionReadService = new p.packer.services.PackerAssetActionReadService(runtimeRegistry, resolver, foundation.lookup());
|
||||
final var runtimePatchService = new p.packer.services.PackerRuntimePatchService(parser);
|
||||
final var runtimePatchService = new p.packer.services.PackerRuntimePatchService(parser, assetMaterializer);
|
||||
final var writeCoordinator = new p.packer.services.PackerProjectWriteCoordinator();
|
||||
return new FileSystemPackerWorkspaceService(
|
||||
new ObjectMapper(),
|
||||
@ -692,6 +698,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
actionReadService,
|
||||
runtimePatchService,
|
||||
runtimeRegistry,
|
||||
cacheRepository,
|
||||
writeCoordinator,
|
||||
eventSink);
|
||||
}
|
||||
@ -701,7 +708,9 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
final var foundation = new p.packer.services.PackerWorkspaceFoundation(mapper);
|
||||
final var parser = new p.packer.services.PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
return new CountingLoader(new PackerRuntimeLoader(foundation, parser, assetWalker));
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
return new CountingLoader(new PackerRuntimeLoader(foundation, parser, cacheRepository, assetMaterializer));
|
||||
}
|
||||
|
||||
private static final class CountingLoader implements PackerRuntimeSnapshotLoader {
|
||||
|
||||
@ -11,7 +11,9 @@ import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.PackerAssetState;
|
||||
import p.packer.messages.assets.PackerBuildParticipation;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerAssetWalker;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
import p.packer.repositories.PackerRuntimeLoader;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
import p.packer.testing.PackerFixtureLocator;
|
||||
@ -43,7 +45,8 @@ final class PackerAssetDetailsServiceTest {
|
||||
assertEquals("TILES/indexed_v1", result.details().outputFormat().displayName());
|
||||
assertEquals(List.of(OutputCodecCatalog.NONE), result.details().availableOutputCodecs());
|
||||
assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE));
|
||||
assertTrue(result.diagnostics().isEmpty());
|
||||
assertTrue(result.diagnostics().stream().noneMatch(diagnostic -> diagnostic.blocking()));
|
||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for tile bank cannot be empty")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -137,7 +140,13 @@ final class PackerAssetDetailsServiceTest {
|
||||
final var foundation = new PackerWorkspaceFoundation(mapper);
|
||||
final var parser = new PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser, assetWalker));
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
final var runtimeRegistry = new PackerRuntimeRegistry(new PackerRuntimeLoader(
|
||||
foundation,
|
||||
parser,
|
||||
cacheRepository,
|
||||
assetMaterializer));
|
||||
final var resolver = new PackerAssetReferenceResolver(foundation.lookup());
|
||||
return new PackerAssetDetailsService(runtimeRegistry, resolver);
|
||||
}
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
package p.packer.services;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerAssetWalker;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
import p.packer.repositories.PackerRuntimeLoader;
|
||||
import p.packer.testing.PackerFixtureLocator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class PackerRuntimePatchServiceTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void afterUpdateAssetContractRefreshesOneAssetProjectionAndCacheState() throws Exception {
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed"));
|
||||
writeTilePng(projectRoot.resolve("assets/ui/atlas/tile.png"));
|
||||
final var foundation = new PackerWorkspaceFoundation(mapper);
|
||||
final var parser = new PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
final var loader = new PackerRuntimeLoader(foundation, parser, cacheRepository, assetMaterializer);
|
||||
final var patchService = new PackerRuntimePatchService(parser, assetMaterializer);
|
||||
final var project = new PackerProjectContext("main", projectRoot);
|
||||
|
||||
final var initialSnapshot = loader.load(project, 1L);
|
||||
final var registryEntry = initialSnapshot.registry().assets().getFirst();
|
||||
final var updatedSnapshot = patchService.afterUpdateAssetContract(
|
||||
initialSnapshot,
|
||||
2L,
|
||||
projectRoot.resolve("assets/ui/atlas"),
|
||||
projectRoot.resolve("assets/ui/atlas/asset.json"),
|
||||
Optional.of(registryEntry));
|
||||
|
||||
assertTrue(updatedSnapshot.cacheState().findAsset(registryEntry.assetId()).isPresent());
|
||||
assertEquals(1, updatedSnapshot.assets().getFirst().walkProjection().buildCandidateFiles().size());
|
||||
assertEquals("tile.png", updatedSnapshot.assets().getFirst().walkProjection().buildCandidateFiles().getFirst().relativePath());
|
||||
}
|
||||
|
||||
private Path copyFixture(String relativePath, Path targetRoot) throws Exception {
|
||||
final Path sourceRoot = PackerFixtureLocator.fixtureRoot(relativePath);
|
||||
try (var stream = Files.walk(sourceRoot)) {
|
||||
for (Path source : stream.sorted(Comparator.naturalOrder()).toList()) {
|
||||
final Path target = targetRoot.resolve(sourceRoot.relativize(source).toString());
|
||||
if (Files.isDirectory(source)) {
|
||||
Files.createDirectories(target);
|
||||
} else {
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return targetRoot;
|
||||
}
|
||||
|
||||
private void writeTilePng(Path path) throws Exception {
|
||||
Files.createDirectories(path.getParent());
|
||||
final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < 16; y += 1) {
|
||||
for (int x = 0; x < 16; x += 1) {
|
||||
image.setRGB(x, y, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
}
|
||||
@ -3,15 +3,21 @@ package p.packer.services;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.PackerWorkspacePaths;
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.repositories.FileSystemPackerCacheRepository;
|
||||
import p.packer.repositories.PackerAssetWalker;
|
||||
import p.packer.repositories.PackerRuntimeAssetMaterializer;
|
||||
import p.packer.repositories.PackerRuntimeLoader;
|
||||
import p.packer.repositories.PackerRuntimeRegistry;
|
||||
import p.packer.testing.PackerFixtureLocator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -66,6 +72,24 @@ final class PackerRuntimeRegistryTest {
|
||||
assertEquals(2, refreshed.snapshot().assets().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runtimeSnapshotRetainsWalkProjectionAndPersistsCacheForRegisteredAssets() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("cache"));
|
||||
writeTilePng(projectRoot.resolve("assets/ui/atlas/tile.png"));
|
||||
final PackerRuntimeRegistry registry = runtimeRegistry();
|
||||
final PackerProjectContext project = project(projectRoot);
|
||||
|
||||
final PackerProjectRuntime runtime = registry.getOrLoad(project);
|
||||
|
||||
assertTrue(Files.isRegularFile(PackerWorkspacePaths.cachePath(project)));
|
||||
assertTrue(runtime.snapshot().cacheState().findAsset(1).isPresent());
|
||||
final var runtimeAsset = runtime.snapshot().assets().getFirst();
|
||||
assertEquals(List.of("tile.png"), runtimeAsset.walkProjection().availableFiles());
|
||||
assertEquals(1, runtimeAsset.walkProjection().buildCandidateFiles().size());
|
||||
assertEquals("tile.png", runtimeAsset.walkProjection().buildCandidateFiles().getFirst().relativePath());
|
||||
assertTrue(runtimeAsset.walkProjection().measuredBankSizeBytes() > 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void disposeMarksRuntimeInactiveAndRemovesItFromRegistry() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("dispose"));
|
||||
@ -85,7 +109,13 @@ final class PackerRuntimeRegistryTest {
|
||||
final var foundation = new PackerWorkspaceFoundation(mapper);
|
||||
final var parser = new PackerAssetDeclarationParser(mapper);
|
||||
final var assetWalker = new PackerAssetWalker(mapper);
|
||||
return new PackerRuntimeRegistry(new PackerRuntimeLoader(foundation, parser, assetWalker));
|
||||
final var cacheRepository = new FileSystemPackerCacheRepository(mapper);
|
||||
final var assetMaterializer = new PackerRuntimeAssetMaterializer(assetWalker);
|
||||
return new PackerRuntimeRegistry(new PackerRuntimeLoader(
|
||||
foundation,
|
||||
parser,
|
||||
cacheRepository,
|
||||
assetMaterializer));
|
||||
}
|
||||
|
||||
private PackerProjectContext project(Path root) {
|
||||
@ -107,4 +137,15 @@ final class PackerRuntimeRegistryTest {
|
||||
}
|
||||
return targetRoot;
|
||||
}
|
||||
|
||||
private void writeTilePng(Path path) throws Exception {
|
||||
Files.createDirectories(path.getParent());
|
||||
final BufferedImage image = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < 16; y += 1) {
|
||||
for (int x = 0; x < 16; x += 1) {
|
||||
image.setRGB(x, y, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user