250 lines
12 KiB
Java
250 lines
12 KiB
Java
package p.packer.services;
|
|
|
|
import p.packer.PackerOperationStatus;
|
|
import p.packer.PackerProjectContext;
|
|
import p.packer.assets.AssetFamilyCatalog;
|
|
import p.packer.assets.AssetReference;
|
|
import p.packer.assets.OutputCodecCatalog;
|
|
import p.packer.assets.PackerAssetState;
|
|
import p.packer.assets.PackerBuildParticipation;
|
|
import p.packer.diagnostics.PackerDiagnosticCategory;
|
|
import p.packer.diagnostics.PackerDiagnosticSeverity;
|
|
import p.packer.messages.GetAssetDetailsRequest;
|
|
import p.packer.messages.GetAssetDetailsResult;
|
|
import p.packer.models.PackerAssetDeclaration;
|
|
import p.packer.models.PackerAssetDeclarationParseResult;
|
|
import p.packer.models.PackerAssetDetails;
|
|
import p.packer.models.PackerAssetIdentity;
|
|
import p.packer.models.PackerAssetSummary;
|
|
import p.packer.models.PackerDiagnostic;
|
|
import p.packer.models.PackerRegistryEntry;
|
|
import p.packer.models.PackerRuntimeAsset;
|
|
import p.packer.models.PackerRuntimeSnapshot;
|
|
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
|
|
public final class PackerAssetDetailsService {
|
|
private final PackerRuntimeRegistry runtimeRegistry;
|
|
|
|
public PackerAssetDetailsService(PackerRuntimeRegistry runtimeRegistry) {
|
|
this.runtimeRegistry = Objects.requireNonNull(runtimeRegistry, "runtimeRegistry");
|
|
}
|
|
|
|
public GetAssetDetailsResult getAssetDetails(GetAssetDetailsRequest request) {
|
|
final PackerProjectContext project = Objects.requireNonNull(request, "request").project();
|
|
final PackerRuntimeSnapshot snapshot = runtimeRegistry.getOrLoad(project).snapshot();
|
|
final ResolvedAssetReference resolved = resolveReference(project, snapshot, request.assetReference());
|
|
final List<PackerDiagnostic> diagnostics = new ArrayList<>(resolved.diagnostics());
|
|
|
|
if (resolved.runtimeAsset().isEmpty()) {
|
|
diagnostics.add(new PackerDiagnostic(
|
|
PackerDiagnosticSeverity.ERROR,
|
|
PackerDiagnosticCategory.STRUCTURAL,
|
|
"asset.json was not found for the requested asset root.",
|
|
resolved.assetRoot().resolve("asset.json"),
|
|
true));
|
|
return failureResult(project, request.assetReference(), resolved, diagnostics);
|
|
}
|
|
|
|
final PackerRuntimeAsset runtimeAsset = resolved.runtimeAsset().get();
|
|
final Path manifestPath = runtimeAsset.manifestPath();
|
|
final PackerAssetDeclarationParseResult parsed = runtimeAsset.parsedDeclaration();
|
|
diagnostics.addAll(parsed.diagnostics());
|
|
if (!parsed.valid()) {
|
|
return failureResult(project, request.assetReference(), resolved, diagnostics);
|
|
}
|
|
|
|
final PackerAssetDeclaration declaration = parsed.declaration();
|
|
diagnostics.addAll(identityMismatchDiagnostics(resolved.registryEntry(), declaration, manifestPath));
|
|
final PackerOutputContractCatalog.OutputContractDefinition outputContract = PackerOutputContractCatalog.definitionFor(
|
|
declaration.outputFormat(),
|
|
declaration.outputCodec());
|
|
final PackerAssetState state = resolved.registryEntry().isPresent()
|
|
? PackerAssetState.REGISTERED
|
|
: PackerAssetState.UNREGISTERED;
|
|
final PackerAssetSummary summary = new PackerAssetSummary(
|
|
canonicalReference(project, resolved.assetRoot(), resolved.registryEntry()),
|
|
new PackerAssetIdentity(
|
|
resolved.registryEntry().map(PackerRegistryEntry::assetId).orElse(null),
|
|
resolved.registryEntry().map(PackerRegistryEntry::assetUuid).orElse(declaration.assetUuid()),
|
|
declaration.name(),
|
|
resolved.assetRoot()),
|
|
state,
|
|
resolved.registryEntry().map(entry -> entry.includedInBuild()
|
|
? PackerBuildParticipation.INCLUDED
|
|
: PackerBuildParticipation.EXCLUDED).orElse(PackerBuildParticipation.EXCLUDED),
|
|
declaration.assetFamily(),
|
|
declaration.preloadEnabled(),
|
|
!diagnostics.isEmpty());
|
|
final PackerAssetDetails details = new PackerAssetDetails(
|
|
summary,
|
|
declaration.outputFormat(),
|
|
declaration.outputCodec(),
|
|
outputContract.availableCodecs(),
|
|
outputContract.codecConfigurationFieldsByCodec(),
|
|
resolveInputs(resolved.assetRoot(), declaration.inputsByRole()),
|
|
diagnostics);
|
|
return new GetAssetDetailsResult(
|
|
diagnostics.stream().anyMatch(PackerDiagnostic::blocking) ? PackerOperationStatus.PARTIAL : PackerOperationStatus.SUCCESS,
|
|
"Asset details resolved from runtime snapshot.",
|
|
PackerReadMessageMapper.toAssetDetailsDTO(details),
|
|
PackerReadMessageMapper.toDiagnosticDTOs(diagnostics));
|
|
}
|
|
|
|
private GetAssetDetailsResult failureResult(
|
|
PackerProjectContext project,
|
|
AssetReference requestedReference,
|
|
ResolvedAssetReference resolved,
|
|
List<PackerDiagnostic> diagnostics) {
|
|
final PackerAssetState state = resolved.registryEntry().isPresent()
|
|
? PackerAssetState.REGISTERED
|
|
: PackerAssetState.UNREGISTERED;
|
|
final PackerAssetSummary summary = new PackerAssetSummary(
|
|
canonicalReferenceOrRequested(project, requestedReference, resolved),
|
|
new PackerAssetIdentity(
|
|
resolved.registryEntry().map(PackerRegistryEntry::assetId).orElse(null),
|
|
resolved.registryEntry().map(PackerRegistryEntry::assetUuid).orElse(null),
|
|
resolved.assetRoot().getFileName().toString(),
|
|
resolved.assetRoot()),
|
|
state,
|
|
resolved.registryEntry().map(entry -> entry.includedInBuild()
|
|
? PackerBuildParticipation.INCLUDED
|
|
: PackerBuildParticipation.EXCLUDED).orElse(PackerBuildParticipation.EXCLUDED),
|
|
AssetFamilyCatalog.UNKNOWN,
|
|
false,
|
|
true);
|
|
final PackerAssetDetails details = new PackerAssetDetails(
|
|
summary,
|
|
"unknown",
|
|
OutputCodecCatalog.UNKNOWN,
|
|
List.of(OutputCodecCatalog.NONE),
|
|
Map.of(OutputCodecCatalog.NONE, List.of()),
|
|
Map.of(),
|
|
diagnostics);
|
|
return new GetAssetDetailsResult(
|
|
PackerOperationStatus.FAILED,
|
|
"Asset declaration is invalid or unreadable.",
|
|
PackerReadMessageMapper.toAssetDetailsDTO(details),
|
|
PackerReadMessageMapper.toDiagnosticDTOs(diagnostics));
|
|
}
|
|
|
|
private Map<String, List<Path>> resolveInputs(Path assetRoot, Map<String, List<String>> inputsByRole) {
|
|
final Map<String, List<Path>> resolved = new LinkedHashMap<>();
|
|
inputsByRole.forEach((role, inputs) -> resolved.put(
|
|
role,
|
|
inputs.stream().map(input -> assetRoot.resolve(input).toAbsolutePath().normalize()).toList()));
|
|
return Map.copyOf(resolved);
|
|
}
|
|
|
|
private List<PackerDiagnostic> identityMismatchDiagnostics(
|
|
Optional<PackerRegistryEntry> registryEntry,
|
|
PackerAssetDeclaration declaration,
|
|
Path manifestPath) {
|
|
if (registryEntry.isEmpty() || registryEntry.get().assetUuid().equals(declaration.assetUuid())) {
|
|
return List.of();
|
|
}
|
|
return List.of(new PackerDiagnostic(
|
|
PackerDiagnosticSeverity.ERROR,
|
|
PackerDiagnosticCategory.STRUCTURAL,
|
|
"Field 'asset_uuid' does not match the registered asset identity.",
|
|
manifestPath,
|
|
true));
|
|
}
|
|
|
|
private ResolvedAssetReference resolveReference(PackerProjectContext project, PackerRuntimeSnapshot snapshot, AssetReference assetReference) {
|
|
final PackerRegistryLookup lookup = new PackerRegistryLookup();
|
|
final String reference = Objects.requireNonNull(assetReference, "assetReference").rawValue();
|
|
final Optional<PackerRegistryEntry> byId = parseAssetId(reference).flatMap(assetId -> lookup.findByAssetId(snapshot.registry(), assetId));
|
|
if (byId.isPresent()) {
|
|
final Path assetRoot = PackerWorkspacePaths.assetRoot(project, byId.get().root());
|
|
return new ResolvedAssetReference(assetRoot, byId, findRuntimeAsset(snapshot, assetRoot), List.of());
|
|
}
|
|
|
|
final Optional<PackerRegistryEntry> byUuid = lookup.findByAssetUuid(snapshot.registry(), reference);
|
|
if (byUuid.isPresent()) {
|
|
final Path assetRoot = PackerWorkspacePaths.assetRoot(project, byUuid.get().root());
|
|
return new ResolvedAssetReference(assetRoot, byUuid, findRuntimeAsset(snapshot, assetRoot), List.of());
|
|
}
|
|
|
|
final Path candidateRoot = PackerWorkspacePaths.assetRoot(project, reference);
|
|
final Optional<PackerRuntimeAsset> runtimeAsset = findRuntimeAsset(snapshot, candidateRoot);
|
|
if (runtimeAsset.isPresent()) {
|
|
return new ResolvedAssetReference(candidateRoot, lookup.findByRoot(project, snapshot.registry(), candidateRoot), runtimeAsset, List.of());
|
|
}
|
|
|
|
final Optional<PackerRegistryEntry> registryEntry = lookup.findByRoot(project, snapshot.registry(), candidateRoot);
|
|
if (registryEntry.isPresent()) {
|
|
return new ResolvedAssetReference(candidateRoot, registryEntry, Optional.empty(), List.of());
|
|
}
|
|
|
|
return new ResolvedAssetReference(
|
|
candidateRoot,
|
|
Optional.empty(),
|
|
Optional.empty(),
|
|
List.of(new PackerDiagnostic(
|
|
PackerDiagnosticSeverity.ERROR,
|
|
PackerDiagnosticCategory.STRUCTURAL,
|
|
"Requested asset reference could not be resolved.",
|
|
candidateRoot,
|
|
true)));
|
|
}
|
|
|
|
private AssetReference canonicalReference(
|
|
PackerProjectContext project,
|
|
Path assetRoot,
|
|
Optional<PackerRegistryEntry> registryEntry) {
|
|
if (registryEntry.isPresent()) {
|
|
return AssetReference.forAssetId(registryEntry.get().assetId());
|
|
}
|
|
return AssetReference.forRelativeAssetRoot(PackerWorkspacePaths.assetsRoot(project)
|
|
.relativize(assetRoot.toAbsolutePath().normalize())
|
|
.toString()
|
|
.replace('\\', '/'));
|
|
}
|
|
|
|
private AssetReference canonicalReferenceOrRequested(
|
|
PackerProjectContext project,
|
|
AssetReference requestedReference,
|
|
ResolvedAssetReference resolved) {
|
|
if (resolved.registryEntry().isPresent() || resolved.runtimeAsset().isPresent()) {
|
|
return canonicalReference(project, resolved.assetRoot(), resolved.registryEntry());
|
|
}
|
|
return requestedReference;
|
|
}
|
|
|
|
private Optional<Integer> parseAssetId(String reference) {
|
|
try {
|
|
return Optional.of(Integer.parseInt(reference.trim()));
|
|
} catch (NumberFormatException ignored) {
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
|
|
private Optional<PackerRuntimeAsset> findRuntimeAsset(PackerRuntimeSnapshot snapshot, Path assetRoot) {
|
|
final Path normalizedRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
|
return snapshot.assets().stream()
|
|
.filter(candidate -> candidate.assetRoot().equals(normalizedRoot))
|
|
.findFirst();
|
|
}
|
|
|
|
private record ResolvedAssetReference(
|
|
Path assetRoot,
|
|
Optional<PackerRegistryEntry> registryEntry,
|
|
Optional<PackerRuntimeAsset> runtimeAsset,
|
|
List<PackerDiagnostic> diagnostics) {
|
|
|
|
private ResolvedAssetReference {
|
|
assetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
|
registryEntry = Objects.requireNonNull(registryEntry, "registryEntry");
|
|
runtimeAsset = Objects.requireNonNull(runtimeAsset, "runtimeAsset");
|
|
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
|
}
|
|
}
|
|
}
|