dev/multi-frontend-provider-contract #14
@ -10,7 +10,7 @@
|
||||
{"type":"discussion","id":"DSC-0057","status":"open","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[{"id":"AGD-0060","file":"AGD-0060-multi-frontend-frontend-backend-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0056","status":"open","ticket":"multi-frontend-remove-pbs-branches","title":"Remover verificacoes explicitas de PBS do codigo comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[{"id":"AGD-0059","file":"AGD-0059-multi-frontend-remove-pbs-branches.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0055","status":"open","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[{"id":"AGD-0058","file":"AGD-0058-multi-frontend-compiler-vs-language-services.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0054","status":"in_progress","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[{"id":"AGD-0057","file":"AGD-0057-multi-frontend-provider-contract.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0040","file":"DEC-0040-frontendprovider-explicito-como-contrato-de-frontend.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0057"}],"plans":[{"id":"PLN-0103","file":"PLN-0103-specify-frontend-provider-and-registry-contract.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0104","file":"PLN-0104-introduce-frontendprovider-registry-api.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0105","file":"PLN-0105-wrap-pbs-frontend-as-explicit-provider.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0106","file":"PLN-0106-migrate-common-consumers-to-provider-lookup.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0054","status":"in_progress","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[{"id":"AGD-0057","file":"AGD-0057-multi-frontend-provider-contract.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0040","file":"DEC-0040-frontendprovider-explicito-como-contrato-de-frontend.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0057"}],"plans":[{"id":"PLN-0103","file":"PLN-0103-specify-frontend-provider-and-registry-contract.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0104","file":"PLN-0104-introduce-frontendprovider-registry-api.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0105","file":"PLN-0105-wrap-pbs-frontend-as-explicit-provider.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0106","file":"PLN-0106-migrate-common-consumers-to-provider-lookup.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0053","status":"open","ticket":"pbs-lsp-call-and-type-hierarchy","title":"PBS LSP Call and Type Hierarchy","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","call-hierarchy","type-hierarchy"],"agendas":[{"id":"AGD-0056","file":"AGD-0056-pbs-lsp-call-and-type-hierarchy.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0052","status":"open","ticket":"pbs-lsp-document-links","title":"PBS LSP Document Links","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","document-links","imports"],"agendas":[{"id":"AGD-0055","file":"AGD-0055-pbs-lsp-document-links.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0051","status":"open","ticket":"pbs-lsp-diagnostics-ux","title":"PBS LSP Diagnostics UX","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","diagnostics","ux"],"agendas":[{"id":"AGD-0054","file":"AGD-0054-pbs-lsp-diagnostics-ux.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0104
|
||||
ticket: multi-frontend-provider-contract
|
||||
title: Introduce FrontendProvider registry API
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-07-15
|
||||
ref_decisions: [DEC-0040]
|
||||
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
|
||||
|
||||
@ -5,5 +5,5 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":prometeu-infra"))
|
||||
|
||||
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
|
||||
}
|
||||
api(project(":prometeu-compiler:prometeu-compiler-core"))
|
||||
}
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
package p.studio.compiler.services;
|
||||
|
||||
/**
|
||||
* Marker contract for optional frontend-owned editor services.
|
||||
*/
|
||||
public interface FrontendLanguageService {
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package p.studio.compiler.services;
|
||||
|
||||
import p.studio.compiler.models.FrontendSpec;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FrontendProvider {
|
||||
FrontendSpec specification();
|
||||
|
||||
FrontendPhaseService compiler();
|
||||
|
||||
default Optional<FrontendLanguageService> languageService() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package p.studio.compiler;
|
||||
|
||||
import p.studio.compiler.models.FrontendSpec;
|
||||
import p.studio.compiler.services.FrontendPhaseService;
|
||||
import p.studio.compiler.services.FrontendProvider;
|
||||
import p.studio.compiler.services.PBSFrontendPhaseService;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -10,29 +11,61 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FrontendRegistryService {
|
||||
private static final Map<String, FrontendSpec> FRONTEND_SPECS = new HashMap<>();
|
||||
private static final Map<String, FrontendPhaseService> FRONTEND_PHASE_SERVICES = new HashMap<>();
|
||||
private static final Map<String, FrontendProvider> FRONTEND_PROVIDERS = new HashMap<>();
|
||||
|
||||
static {
|
||||
FRONTEND_SPECS.put(PBSDefinitions.PBS.getLanguageId(), PBSDefinitions.PBS);
|
||||
FRONTEND_PHASE_SERVICES.put(PBSDefinitions.PBS.getLanguageId(), new PBSFrontendPhaseService());
|
||||
register(new StaticFrontendProvider(PBSDefinitions.PBS, new PBSFrontendPhaseService()));
|
||||
}
|
||||
|
||||
public static FrontendSpec getDefaultFrontendSpec() {
|
||||
return FRONTEND_SPECS.get(PBSDefinitions.PBS.getLanguageId());
|
||||
return require(PBSDefinitions.PBS.getLanguageId()).specification();
|
||||
}
|
||||
|
||||
public static Optional<FrontendSpec> getFrontendSpec(final String languageId) {
|
||||
return Optional.ofNullable(FRONTEND_SPECS.get(languageId));
|
||||
return getProvider(languageId).map(FrontendProvider::specification);
|
||||
}
|
||||
|
||||
public static Optional<FrontendPhaseService> getFrontendPhaseService(final String languageId) {
|
||||
return Optional.ofNullable(FRONTEND_PHASE_SERVICES.get(languageId));
|
||||
return getProvider(languageId).map(FrontendProvider::compiler);
|
||||
}
|
||||
|
||||
public static Optional<FrontendProvider> getProvider(final String languageId) {
|
||||
return Optional.ofNullable(FRONTEND_PROVIDERS.get(languageId));
|
||||
}
|
||||
|
||||
public static FrontendProvider require(final String languageId) {
|
||||
final var provider = FRONTEND_PROVIDERS.get(languageId);
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("Unknown frontend languageId: " + languageId);
|
||||
}
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static List<FrontendSpec> listFrontendSpecs() {
|
||||
return FRONTEND_SPECS.values().stream()
|
||||
return FRONTEND_PROVIDERS.values().stream()
|
||||
.map(FrontendProvider::specification)
|
||||
.sorted((left, right) -> left.getLanguageId().compareToIgnoreCase(right.getLanguageId()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public static List<FrontendProvider> listProviders() {
|
||||
return FRONTEND_PROVIDERS.values().stream()
|
||||
.sorted((left, right) -> left.specification().getLanguageId().compareToIgnoreCase(right.specification().getLanguageId()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
static void register(final FrontendProvider provider) {
|
||||
final var languageId = provider.specification().getLanguageId();
|
||||
if (FRONTEND_PROVIDERS.containsKey(languageId)) {
|
||||
throw new IllegalArgumentException("Duplicate frontend languageId: " + languageId);
|
||||
}
|
||||
|
||||
FRONTEND_PROVIDERS.put(languageId, provider);
|
||||
}
|
||||
|
||||
private record StaticFrontendProvider(
|
||||
FrontendSpec specification,
|
||||
FrontendPhaseService compiler) implements FrontendProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package p.studio.compiler;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class FrontendRegistryServiceTest {
|
||||
|
||||
@Test
|
||||
void requireShouldResolveProviderByLanguageId() {
|
||||
final var provider = FrontendRegistryService.require(PBSDefinitions.PBS.getLanguageId());
|
||||
|
||||
assertEquals(PBSDefinitions.PBS.getLanguageId(), provider.specification().getLanguageId());
|
||||
assertSame(PBSDefinitions.PBS, provider.specification());
|
||||
}
|
||||
|
||||
@Test
|
||||
void requireShouldFailClearlyForUnknownLanguageId() {
|
||||
final var thrown = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> FrontendRegistryService.require("unknown-language"));
|
||||
|
||||
assertTrue(thrown.getMessage().contains("unknown-language"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void specListingShouldBeProviderDerived() {
|
||||
final var specs = FrontendRegistryService.listFrontendSpecs();
|
||||
|
||||
assertEquals(1, specs.size());
|
||||
assertEquals(PBSDefinitions.PBS.getLanguageId(), specs.getFirst().getLanguageId());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user