implements PR-07d asset mutation and structural sync orchestration

This commit is contained in:
bQUARKz 2026-03-12 09:38:41 +00:00
parent 03ca81cd1e
commit 288178c44e
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
5 changed files with 84 additions and 11 deletions

View File

@ -0,0 +1,16 @@
package p.studio.events;
import p.studio.projects.ProjectReference;
import p.studio.workspaces.assets.AssetWorkspaceSelectionKey;
import java.util.Objects;
public record StudioAssetsStructuralSyncRequestedEvent(
ProjectReference project,
AssetWorkspaceSelectionKey preferredSelectionKey,
String reason) implements StudioEvent {
public StudioAssetsStructuralSyncRequestedEvent {
Objects.requireNonNull(project, "project");
Objects.requireNonNull(reason, "reason");
}
}

View File

@ -155,6 +155,12 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
applyAssetSummaryPatch(event.summary()); applyAssetSummaryPatch(event.summary());
} }
}); });
workspaceBus.subscribe(StudioAssetsStructuralSyncRequestedEvent.class, event -> {
if (projectMatches(event.project())) {
pendingSelectionKey = event.preferredSelectionKey();
refresh();
}
});
} }
private boolean projectMatches(ProjectReference project) { private boolean projectMatches(ProjectReference project) {
@ -313,9 +319,8 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
} }
AssetCreationWizard.showAndWait(root.getScene().getWindow(), projectReference, assetCreationService) AssetCreationWizard.showAndWait(root.getScene().getWindow(), projectReference, assetCreationService)
.ifPresent(result -> { .ifPresent(result -> {
pendingSelectionKey = result.selectionKey();
appendLog("Asset created: " + projectRelativePath(result.assetRoot()) + "."); appendLog("Asset created: " + projectRelativePath(result.assetRoot()) + ".");
refresh(); requestStructuralSync("asset created", result.selectionKey());
}); });
} }
@ -680,8 +685,10 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
mutationService.apply(projectReference, preview); mutationService.apply(projectReference, preview);
appendLog("Applied " + actionLabel(preview.action()) + "."); appendLog("Applied " + actionLabel(preview.action()) + ".");
stagedMutationPreview = null; stagedMutationPreview = null;
if (!applyMutationSummaryPatch(preview)) { if (AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(preview.action()) == AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH) {
refresh(); applyMutationSummaryPatch(preview);
} else {
requestStructuralSync("mutation applied: " + preview.action().name().toLowerCase(Locale.ROOT), null);
} }
} catch (RuntimeException runtimeException) { } catch (RuntimeException runtimeException) {
final String message = rootCauseMessage(runtimeException); final String message = rootCauseMessage(runtimeException);
@ -700,7 +707,7 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
.ifPresent(preview -> { .ifPresent(preview -> {
appendLog("Applied " + actionLabel(preview.action()) + "."); appendLog("Applied " + actionLabel(preview.action()) + ".");
stagedMutationPreview = null; stagedMutationPreview = null;
refresh(); requestStructuralSync("mutation applied: " + preview.action().name().toLowerCase(Locale.ROOT), null);
}); });
} }
@ -713,8 +720,10 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
mutationService.apply(projectReference, preview); mutationService.apply(projectReference, preview);
appendLog("Applied " + actionLabel(preview.action()) + "."); appendLog("Applied " + actionLabel(preview.action()) + ".");
stagedMutationPreview = null; stagedMutationPreview = null;
if (!applyMutationSummaryPatch(preview)) { if (AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(preview.action()) == AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH) {
refresh(); applyMutationSummaryPatch(preview);
} else {
requestStructuralSync("mutation applied: " + preview.action().name().toLowerCase(Locale.ROOT), null);
} }
} catch (RuntimeException runtimeException) { } catch (RuntimeException runtimeException) {
final String message = rootCauseMessage(runtimeException); final String message = rootCauseMessage(runtimeException);
@ -725,17 +734,16 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
} }
} }
private boolean applyMutationSummaryPatch(AssetWorkspaceMutationPreview preview) { private void applyMutationSummaryPatch(AssetWorkspaceMutationPreview preview) {
final AssetWorkspaceAssetSummary updatedSummary = switch (preview.action()) { final AssetWorkspaceAssetSummary updatedSummary = switch (preview.action()) {
case INCLUDE_IN_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.INCLUDED); case INCLUDE_IN_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.INCLUDED);
case EXCLUDE_FROM_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.EXCLUDED); case EXCLUDE_FROM_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.EXCLUDED);
default -> null; default -> null;
}; };
if (updatedSummary == null) { if (updatedSummary == null) {
return false; return;
} }
workspaceBus.publish(new StudioAssetsAssetSummaryPatchedEvent(projectReference, updatedSummary)); workspaceBus.publish(new StudioAssetsAssetSummaryPatchedEvent(projectReference, updatedSummary));
return true;
} }
private Node createStagedMutationPanel(AssetWorkspaceMutationPreview preview) { private Node createStagedMutationPanel(AssetWorkspaceMutationPreview preview) {
@ -886,7 +894,11 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
mutationService.apply(projectReference, preview); mutationService.apply(projectReference, preview);
appendLog("Applied " + actionLabel(preview.action()) + "."); appendLog("Applied " + actionLabel(preview.action()) + ".");
stagedMutationPreview = null; stagedMutationPreview = null;
refresh(); if (AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(preview.action()) == AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH) {
applyMutationSummaryPatch(preview);
} else {
requestStructuralSync("mutation applied: " + preview.action().name().toLowerCase(Locale.ROOT), null);
}
} catch (RuntimeException runtimeException) { } catch (RuntimeException runtimeException) {
final String message = rootCauseMessage(runtimeException); final String message = rootCauseMessage(runtimeException);
appendLog("Mutation failed: " + message); appendLog("Mutation failed: " + message);
@ -895,6 +907,10 @@ public final class AssetWorkspace implements Workspace, AssetWorkspaceInteractio
} }
} }
private void requestStructuralSync(String reason, AssetWorkspaceSelectionKey preferredSelectionKey) {
workspaceBus.publish(new StudioAssetsStructuralSyncRequestedEvent(projectReference, preferredSelectionKey, reason));
}
private Path firstPreviewInput(AssetWorkspaceAssetDetails details) { private Path firstPreviewInput(AssetWorkspaceAssetDetails details) {
return details.inputsByRole().values().stream() return details.inputsByRole().values().stream()
.flatMap(List::stream) .flatMap(List::stream)

View File

@ -0,0 +1,15 @@
package p.studio.workspaces.assets;
import java.util.Objects;
public final class AssetWorkspaceMutationUpdatePlanner {
private AssetWorkspaceMutationUpdatePlanner() {
}
public static AssetWorkspaceMutationUpdateStrategy forSuccessfulAction(AssetWorkspaceAction action) {
return switch (Objects.requireNonNull(action, "action")) {
case INCLUDE_IN_BUILD, EXCLUDE_FROM_BUILD -> AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH;
case REGISTER, RELOCATE, REMOVE -> AssetWorkspaceMutationUpdateStrategy.STRUCTURAL_SYNC;
};
}
}

View File

@ -0,0 +1,6 @@
package p.studio.workspaces.assets;
public enum AssetWorkspaceMutationUpdateStrategy {
LOCAL_PATCH,
STRUCTURAL_SYNC
}

View File

@ -0,0 +1,20 @@
package p.studio.workspaces.assets;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
final class AssetWorkspaceMutationUpdatePlannerTest {
@Test
void classifiesLocalPatchMutations() {
assertEquals(AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH, AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(AssetWorkspaceAction.INCLUDE_IN_BUILD));
assertEquals(AssetWorkspaceMutationUpdateStrategy.LOCAL_PATCH, AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(AssetWorkspaceAction.EXCLUDE_FROM_BUILD));
}
@Test
void classifiesStructuralSyncMutations() {
assertEquals(AssetWorkspaceMutationUpdateStrategy.STRUCTURAL_SYNC, AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(AssetWorkspaceAction.REGISTER));
assertEquals(AssetWorkspaceMutationUpdateStrategy.STRUCTURAL_SYNC, AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(AssetWorkspaceAction.RELOCATE));
assertEquals(AssetWorkspaceMutationUpdateStrategy.STRUCTURAL_SYNC, AssetWorkspaceMutationUpdatePlanner.forSuccessfulAction(AssetWorkspaceAction.REMOVE));
}
}