implements PR-35
This commit is contained in:
parent
99d1070c46
commit
fa527720a4
@ -0,0 +1,22 @@
|
||||
package p.packer.models;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public record PackerRuntimeMaterializationConfig(
|
||||
PackerWalkMode mode,
|
||||
Predicate<PackerProbeResult> projectionFilter) {
|
||||
|
||||
public PackerRuntimeMaterializationConfig {
|
||||
mode = Objects.requireNonNull(mode, "mode");
|
||||
projectionFilter = Objects.requireNonNull(projectionFilter, "projectionFilter");
|
||||
}
|
||||
|
||||
public static PackerRuntimeMaterializationConfig runtimeDefault() {
|
||||
return new PackerRuntimeMaterializationConfig(PackerWalkMode.RUNTIME, probe -> true);
|
||||
}
|
||||
|
||||
public static PackerRuntimeMaterializationConfig packingBuild() {
|
||||
return new PackerRuntimeMaterializationConfig(PackerWalkMode.PACKING, probe -> true);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record PackerRuntimeWalkFile(
|
||||
String relativePath,
|
||||
@ -11,6 +12,7 @@ public record PackerRuntimeWalkFile(
|
||||
long size,
|
||||
long lastModified,
|
||||
String fingerprint,
|
||||
Optional<byte[]> contentBytes,
|
||||
Map<String, Object> metadata,
|
||||
List<PackerDiagnostic> diagnostics) {
|
||||
|
||||
@ -24,6 +26,8 @@ public record PackerRuntimeWalkFile(
|
||||
throw new IllegalArgumentException("lastModified must be non-negative");
|
||||
}
|
||||
fingerprint = fingerprint == null || fingerprint.isBlank() ? null : fingerprint;
|
||||
final Optional<byte[]> safeContentBytes = Objects.requireNonNull(contentBytes, "contentBytes");
|
||||
contentBytes = safeContentBytes.map(byte[]::clone);
|
||||
metadata = Map.copyOf(new LinkedHashMap<>(Objects.requireNonNull(metadata, "metadata")));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
package p.packer.models;
|
||||
|
||||
public enum PackerWalkMode {
|
||||
RUNTIME,
|
||||
PACKING
|
||||
}
|
||||
@ -20,11 +20,28 @@ public final class PackerRuntimeAssetMaterializer {
|
||||
Optional<PackerRegistryEntry> registryEntry,
|
||||
PackerAssetDeclarationParseResult parseResult,
|
||||
Optional<PackerAssetCacheEntry> priorAssetCache) {
|
||||
return materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
registryEntry,
|
||||
parseResult,
|
||||
priorAssetCache,
|
||||
PackerRuntimeMaterializationConfig.runtimeDefault());
|
||||
}
|
||||
|
||||
public PackerRuntimeAssetMaterialization materialize(
|
||||
Path assetRoot,
|
||||
Path manifestPath,
|
||||
Optional<PackerRegistryEntry> registryEntry,
|
||||
PackerAssetDeclarationParseResult parseResult,
|
||||
Optional<PackerAssetCacheEntry> priorAssetCache,
|
||||
PackerRuntimeMaterializationConfig materializationConfig) {
|
||||
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");
|
||||
final PackerRuntimeMaterializationConfig safeMaterializationConfig = Objects.requireNonNull(materializationConfig, "materializationConfig");
|
||||
final String currentContractFingerprint = safeParseResult.valid()
|
||||
? PackerContractFingerprint.metadataFingerprint(safeParseResult.declaration().outputMetadata())
|
||||
: null;
|
||||
@ -47,7 +64,8 @@ public final class PackerRuntimeAssetMaterializer {
|
||||
safeParseResult.declaration(),
|
||||
reusablePriorAssetCache(safePriorAssetCache, currentContractFingerprint));
|
||||
final List<PackerRuntimeWalkFile> buildCandidateFiles = walkResult.probeResults().stream()
|
||||
.map(probeResult -> toRuntimeWalkFile(normalizedAssetRoot, probeResult))
|
||||
.filter(probeResult -> shouldIncludeProbe(safeParseResult.declaration(), normalizedAssetRoot, probeResult, safeMaterializationConfig))
|
||||
.map(probeResult -> toRuntimeWalkFile(normalizedAssetRoot, probeResult, safeMaterializationConfig))
|
||||
.sorted(Comparator.comparing(PackerRuntimeWalkFile::relativePath, String.CASE_INSENSITIVE_ORDER))
|
||||
.toList();
|
||||
final long measuredBankSizeBytes = buildCandidateFiles.stream()
|
||||
@ -105,18 +123,47 @@ public final class PackerRuntimeAssetMaterializer {
|
||||
}
|
||||
}
|
||||
|
||||
private PackerRuntimeWalkFile toRuntimeWalkFile(Path assetRoot, PackerProbeResult probeResult) {
|
||||
private PackerRuntimeWalkFile toRuntimeWalkFile(
|
||||
Path assetRoot,
|
||||
PackerProbeResult probeResult,
|
||||
PackerRuntimeMaterializationConfig materializationConfig) {
|
||||
final PackerFileProbe fileProbe = probeResult.fileProbe();
|
||||
final String relativePath = assetRoot.relativize(fileProbe.path().toAbsolutePath().normalize()).toString().replace('\\', '/');
|
||||
return new PackerRuntimeWalkFile(
|
||||
assetRoot.relativize(fileProbe.path().toAbsolutePath().normalize()).toString().replace('\\', '/'),
|
||||
relativePath,
|
||||
fileProbe.mimeType(),
|
||||
fileProbe.size(),
|
||||
fileProbe.lastModified(),
|
||||
PackerFileFingerprint.sha256(fileProbe),
|
||||
materializationConfig.mode() == PackerWalkMode.PACKING
|
||||
? Optional.of(fileProbe.content())
|
||||
: Optional.empty(),
|
||||
probeResult.metadata(),
|
||||
probeResult.diagnostics());
|
||||
}
|
||||
|
||||
private boolean shouldIncludeProbe(
|
||||
PackerAssetDeclaration declaration,
|
||||
Path assetRoot,
|
||||
PackerProbeResult probeResult,
|
||||
PackerRuntimeMaterializationConfig materializationConfig) {
|
||||
if (!materializationConfig.projectionFilter().test(probeResult)) {
|
||||
return false;
|
||||
}
|
||||
if (materializationConfig.mode() != PackerWalkMode.PACKING) {
|
||||
return true;
|
||||
}
|
||||
if (declaration.artifacts().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final String relativePath = assetRoot.relativize(probeResult.fileProbe().path().toAbsolutePath().normalize())
|
||||
.toString()
|
||||
.replace('\\', '/');
|
||||
return declaration.artifacts().stream()
|
||||
.map(PackerAssetArtifactSelection::file)
|
||||
.anyMatch(relativePath::equals);
|
||||
}
|
||||
|
||||
public record PackerRuntimeAssetMaterialization(
|
||||
PackerRuntimeAsset runtimeAsset,
|
||||
Optional<PackerAssetCacheEntry> assetCacheEntry) {
|
||||
|
||||
@ -9,6 +9,8 @@ import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
import p.packer.models.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
@ -68,20 +70,102 @@ final class PackerRuntimeAssetMaterializerTest {
|
||||
assertTrue(walker.lastPriorAssetCache().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runtimeDefaultProjectionSuppressesContentBytes() throws Exception {
|
||||
final var materializer = new PackerRuntimeAssetMaterializer(new PackerAssetWalker(MAPPER));
|
||||
final Path assetRoot = Files.createDirectories(tempDir.resolve("runtime-default"));
|
||||
final Path manifestPath = assetRoot.resolve("asset.json");
|
||||
Files.writeString(manifestPath, "{}");
|
||||
writeTile(assetRoot.resolve("selected.png"), 16);
|
||||
writeTile(assetRoot.resolve("extra.png"), 16);
|
||||
|
||||
final PackerAssetDeclaration declaration = declaration(
|
||||
Map.of("tile_size", "16x16"),
|
||||
List.of(new PackerAssetArtifactSelection("selected.png", 0)),
|
||||
palettePipeline());
|
||||
final var materialized = materializer.materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
Optional.of(new PackerRegistryEntry(1, "uuid", "asset", true)),
|
||||
new PackerAssetDeclarationParseResult(declaration, List.of()),
|
||||
Optional.empty());
|
||||
|
||||
assertEquals(2, materialized.runtimeAsset().walkProjection().buildCandidateFiles().size());
|
||||
assertTrue(materialized.runtimeAsset().walkProjection().buildCandidateFiles().stream()
|
||||
.allMatch(file -> file.contentBytes().isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packingProjectionKeepsSelectedFilesAndInjectsContentBytes() throws Exception {
|
||||
final var materializer = new PackerRuntimeAssetMaterializer(new PackerAssetWalker(MAPPER));
|
||||
final Path assetRoot = Files.createDirectories(tempDir.resolve("packing"));
|
||||
final Path manifestPath = assetRoot.resolve("asset.json");
|
||||
Files.writeString(manifestPath, "{}");
|
||||
writeTile(assetRoot.resolve("selected.png"), 16);
|
||||
writeTile(assetRoot.resolve("extra.png"), 16);
|
||||
|
||||
final PackerAssetDeclaration declaration = declaration(
|
||||
Map.of("tile_size", "16x16"),
|
||||
List.of(new PackerAssetArtifactSelection("selected.png", 0)),
|
||||
palettePipeline());
|
||||
final var materialized = materializer.materialize(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
Optional.of(new PackerRegistryEntry(1, "uuid", "asset", true)),
|
||||
new PackerAssetDeclarationParseResult(declaration, List.of()),
|
||||
Optional.empty(),
|
||||
PackerRuntimeMaterializationConfig.packingBuild());
|
||||
|
||||
assertEquals(1, materialized.runtimeAsset().walkProjection().buildCandidateFiles().size());
|
||||
final var file = materialized.runtimeAsset().walkProjection().buildCandidateFiles().getFirst();
|
||||
assertEquals("selected.png", file.relativePath());
|
||||
assertTrue(file.contentBytes().isPresent());
|
||||
assertTrue(file.contentBytes().get().length > 0);
|
||||
}
|
||||
|
||||
private static PackerAssetDeclaration declaration(Map<String, String> metadata) {
|
||||
return declaration(metadata, List.of(), Map.of());
|
||||
}
|
||||
|
||||
private static PackerAssetDeclaration declaration(
|
||||
Map<String, String> metadata,
|
||||
List<PackerAssetArtifactSelection> artifacts,
|
||||
Map<String, JsonNode> pipelineMetadata) {
|
||||
return new PackerAssetDeclaration(
|
||||
1,
|
||||
"uuid",
|
||||
"asset",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
List.of(),
|
||||
artifacts,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
metadata,
|
||||
Map.<String, JsonNode>of(),
|
||||
pipelineMetadata,
|
||||
true);
|
||||
}
|
||||
|
||||
private static Map<String, JsonNode> palettePipeline() {
|
||||
final var payload = MAPPER.createObjectNode();
|
||||
payload.putArray("originalArgb8888").add(0xFFFF0000);
|
||||
payload.putArray("convertedRgb565").add(0xF800);
|
||||
final var declaration = MAPPER.createObjectNode();
|
||||
declaration.put("index", 0);
|
||||
declaration.set("palette", payload);
|
||||
final var palettes = MAPPER.createArrayNode();
|
||||
palettes.add(declaration);
|
||||
return Map.of("palettes", palettes);
|
||||
}
|
||||
|
||||
private static void writeTile(Path path, int tileSize) throws Exception {
|
||||
final BufferedImage image = new BufferedImage(tileSize, tileSize, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int y = 0; y < tileSize; y += 1) {
|
||||
for (int x = 0; x < tileSize; x += 1) {
|
||||
image.setRGB(x, y, 0xFFFF0000);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", path.toFile());
|
||||
}
|
||||
|
||||
private static final class TrackingWalker extends PackerAssetWalker {
|
||||
private Optional<PackerAssetCacheEntry> lastPriorAssetCache = Optional.empty();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user