@shortlink-org/portolan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/catalog/enum_test.go +46 -0
- package/catalog/model.go +932 -0
- package/catalog/roundtrip_test.go +185 -0
- package/catalog/via_test.go +38 -0
- package/cli/portolan.mjs +368 -0
- package/go.mod +14 -0
- package/go.sum +14 -0
- package/index.html +13 -0
- package/internal/commands/cargo.go +154 -0
- package/internal/commands/commands.go +143 -0
- package/internal/commands/commands_test.go +323 -0
- package/internal/commands/gradle.go +77 -0
- package/internal/commands/justfile.go +82 -0
- package/internal/commands/makefile.go +106 -0
- package/internal/commands/maven.go +93 -0
- package/internal/commands/packagejson.go +159 -0
- package/internal/commands/pyproject.go +169 -0
- package/internal/commands/taskfile.go +101 -0
- package/internal/commands/testdata/estate/.cargo/config.toml +7 -0
- package/internal/commands/testdata/estate/Makefile +33 -0
- package/internal/commands/testdata/estate/Taskfile.yml +21 -0
- package/internal/commands/testdata/estate/build.gradle.kts +21 -0
- package/internal/commands/testdata/estate/justfile +20 -0
- package/internal/commands/testdata/estate/package.json +13 -0
- package/internal/commands/testdata/estate/pom.xml +29 -0
- package/internal/commands/testdata/estate/pyproject.toml +27 -0
- package/internal/commands/testdata/estate/xtask/src/main.rs +27 -0
- package/internal/commands/testdata/golden/commands.json +305 -0
- package/internal/gohttp/analyze.go +2500 -0
- package/internal/gohttp/endpoints.go +1067 -0
- package/internal/gohttp/roots.go +320 -0
- package/internal/gohttp/typed.go +143 -0
- package/internal/goscan/constants.go +85 -0
- package/internal/goscan/goscan_test.go +227 -0
- package/internal/goscan/names.go +52 -0
- package/internal/goscan/parse_test.go +11 -0
- package/internal/goscan/source.go +36 -0
- package/internal/goscan/tree.go +155 -0
- package/internal/goscan/types.go +99 -0
- package/internal/wsdl/ids.go +127 -0
- package/internal/wsdl/ids_test.go +21 -0
- package/internal/wsdl/model.go +62 -0
- package/internal/wsdl/parse.go +920 -0
- package/internal/wsdl/parse_test.go +133 -0
- package/package.json +117 -0
- package/plugin/describe.go +107 -0
- package/plugin/describe_test.go +114 -0
- package/plugin/protocol.go +117 -0
- package/plugin/schematest/schematest.go +126 -0
- package/plugins/README.md +1160 -0
- package/plugins/extract-adr/describe.go +19 -0
- package/plugins/extract-adr/describe_test.go +11 -0
- package/plugins/extract-adr/extract.go +153 -0
- package/plugins/extract-adr/extract_test.go +350 -0
- package/plugins/extract-adr/history.go +99 -0
- package/plugins/extract-adr/main.go +65 -0
- package/plugins/extract-adr/options.schema.json +26 -0
- package/plugins/extract-adr/parse.go +642 -0
- package/plugins/extract-adr/parse_test.go +405 -0
- package/plugins/extract-asyncapi/describe.go +19 -0
- package/plugins/extract-asyncapi/describe_test.go +11 -0
- package/plugins/extract-asyncapi/extract.go +315 -0
- package/plugins/extract-asyncapi/extract_test.go +197 -0
- package/plugins/extract-asyncapi/main.go +48 -0
- package/plugins/extract-asyncapi/options.schema.json +26 -0
- package/plugins/extract-asyncapi/spec.go +151 -0
- package/plugins/extract-celery/README.md +169 -0
- package/plugins/extract-celery/extract.py +346 -0
- package/plugins/extract-celery/extract_test.py +192 -0
- package/plugins/extract-celery/main.py +66 -0
- package/plugins/extract-celery/options.py +31 -0
- package/plugins/extract-celery/options.schema.json +31 -0
- package/plugins/extract-celery/producers.py +156 -0
- package/plugins/extract-celery/routes_test.py +106 -0
- package/plugins/extract-commands/describe.go +19 -0
- package/plugins/extract-commands/describe_test.go +11 -0
- package/plugins/extract-commands/extract.go +79 -0
- package/plugins/extract-commands/extract_test.go +92 -0
- package/plugins/extract-commands/main.go +55 -0
- package/plugins/extract-commands/options.schema.json +20 -0
- package/plugins/extract-csr/avro.go +258 -0
- package/plugins/extract-csr/describe.go +19 -0
- package/plugins/extract-csr/describe_test.go +11 -0
- package/plugins/extract-csr/extract.go +338 -0
- package/plugins/extract-csr/extract_test.go +374 -0
- package/plugins/extract-csr/jsonschema.go +343 -0
- package/plugins/extract-csr/lock.go +25 -0
- package/plugins/extract-csr/main.go +84 -0
- package/plugins/extract-csr/options.schema.json +43 -0
- package/plugins/extract-csr/subject.go +102 -0
- package/plugins/extract-django/README.md +386 -0
- package/plugins/extract-django/apps.py +100 -0
- package/plugins/extract-django/auth.py +302 -0
- package/plugins/extract-django/auth_test.py +188 -0
- package/plugins/extract-django/clients.py +121 -0
- package/plugins/extract-django/contracts.py +458 -0
- package/plugins/extract-django/database.py +119 -0
- package/plugins/extract-django/domain.py +318 -0
- package/plugins/extract-django/events.py +86 -0
- package/plugins/extract-django/extract.py +601 -0
- package/plugins/extract-django/extract_test.py +397 -0
- package/plugins/extract-django/flows.py +1278 -0
- package/plugins/extract-django/flows_test.py +338 -0
- package/plugins/extract-django/ids.py +42 -0
- package/plugins/extract-django/ids_test.py +30 -0
- package/plugins/extract-django/lifecycle.py +222 -0
- package/plugins/extract-django/lifecycle_test.py +76 -0
- package/plugins/extract-django/main.py +66 -0
- package/plugins/extract-django/openapi.py +229 -0
- package/plugins/extract-django/openapi_test.py +60 -0
- package/plugins/extract-django/operations.py +96 -0
- package/plugins/extract-django/options.py +61 -0
- package/plugins/extract-django/options.schema.json +126 -0
- package/plugins/extract-django/routing.py +214 -0
- package/plugins/extract-django/routing_test.py +198 -0
- package/plugins/extract-django/serializers.py +558 -0
- package/plugins/extract-django/store.py +434 -0
- package/plugins/extract-django/transport.py +242 -0
- package/plugins/extract-flows/describe.go +19 -0
- package/plugins/extract-flows/describe_test.go +11 -0
- package/plugins/extract-flows/extract.go +91 -0
- package/plugins/extract-flows/main.go +45 -0
- package/plugins/extract-flows/options.schema.json +16 -0
- package/plugins/extract-flows/parse.go +593 -0
- package/plugins/extract-flows/parse_test.go +204 -0
- package/plugins/extract-glossary/describe.go +19 -0
- package/plugins/extract-glossary/describe_test.go +11 -0
- package/plugins/extract-glossary/extract.go +115 -0
- package/plugins/extract-glossary/extract_test.go +220 -0
- package/plugins/extract-glossary/main.go +59 -0
- package/plugins/extract-glossary/options.schema.json +21 -0
- package/plugins/extract-glossary/parse.go +214 -0
- package/plugins/extract-glossary/parse_test.go +203 -0
- package/plugins/extract-go/aggregate.go +214 -0
- package/plugins/extract-go/client.go +409 -0
- package/plugins/extract-go/client_test.go +266 -0
- package/plugins/extract-go/describe.go +19 -0
- package/plugins/extract-go/describe_test.go +11 -0
- package/plugins/extract-go/enum.go +195 -0
- package/plugins/extract-go/enum_test.go +82 -0
- package/plugins/extract-go/event.go +99 -0
- package/plugins/extract-go/extract.go +191 -0
- package/plugins/extract-go/extract_test.go +261 -0
- package/plugins/extract-go/flow.go +1441 -0
- package/plugins/extract-go/flow_test.go +609 -0
- package/plugins/extract-go/httpclient.go +174 -0
- package/plugins/extract-go/httpclient_test.go +296 -0
- package/plugins/extract-go/ids.go +92 -0
- package/plugins/extract-go/layout.go +225 -0
- package/plugins/extract-go/layout_test.go +125 -0
- package/plugins/extract-go/lifecycle.go +324 -0
- package/plugins/extract-go/lifecycle_test.go +108 -0
- package/plugins/extract-go/main.go +79 -0
- package/plugins/extract-go/operation.go +157 -0
- package/plugins/extract-go/options.schema.json +66 -0
- package/plugins/extract-go/source.go +316 -0
- package/plugins/extract-go/transport.go +321 -0
- package/plugins/extract-go/transport_test.go +145 -0
- package/plugins/extract-go/wiring.go +434 -0
- package/plugins/extract-go-nats/describe.go +19 -0
- package/plugins/extract-go-nats/describe_test.go +11 -0
- package/plugins/extract-go-nats/extract.go +177 -0
- package/plugins/extract-go-nats/extract_test.go +389 -0
- package/plugins/extract-go-nats/index.go +379 -0
- package/plugins/extract-go-nats/main.go +42 -0
- package/plugins/extract-go-nats/options.schema.json +21 -0
- package/plugins/extract-go-nats/resolve.go +161 -0
- package/plugins/extract-go-nats/sites.go +224 -0
- package/plugins/extract-graphql/describe.go +19 -0
- package/plugins/extract-graphql/describe_test.go +11 -0
- package/plugins/extract-graphql/extract.go +433 -0
- package/plugins/extract-graphql/extract_test.go +256 -0
- package/plugins/extract-graphql/ids.go +49 -0
- package/plugins/extract-graphql/lex.go +237 -0
- package/plugins/extract-graphql/main.go +51 -0
- package/plugins/extract-graphql/options.schema.json +31 -0
- package/plugins/extract-graphql/parse.go +621 -0
- package/plugins/extract-graphql/parse_test.go +122 -0
- package/plugins/extract-http-clients/describe.go +19 -0
- package/plugins/extract-http-clients/describe_test.go +11 -0
- package/plugins/extract-http-clients/extract.go +705 -0
- package/plugins/extract-http-clients/extract_test.go +1263 -0
- package/plugins/extract-http-clients/main.go +42 -0
- package/plugins/extract-http-clients/options.schema.json +33 -0
- package/plugins/extract-java/README.md +190 -0
- package/plugins/extract-java/build/org/portolan/extract/Catalog.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$2.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Call.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Client.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients$Route.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Clients.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain$Aggregate.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Domain.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events$Found.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Events.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Extract.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Binding.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Draft.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Frame.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Flows.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Ids.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Json.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle$Move.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Lifecycle.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Main.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Operation.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi$Spec.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/OpenApi.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$1.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations$UseCase.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Operations.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Rpc.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto$Service.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Proto.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Builder.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Input.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol$Options.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Protocol.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Project.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source$Unit.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Source.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport$Endpoint.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Transport.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Line.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml$Reader.class +0 -0
- package/plugins/extract-java/build/org/portolan/extract/Yaml.class +0 -0
- package/plugins/extract-java/options.schema.json +70 -0
- package/plugins/extract-openapi/describe.go +19 -0
- package/plugins/extract-openapi/describe_test.go +11 -0
- package/plugins/extract-openapi/discover.go +243 -0
- package/plugins/extract-openapi/extract.go +526 -0
- package/plugins/extract-openapi/extract_test.go +545 -0
- package/plugins/extract-openapi/main.go +75 -0
- package/plugins/extract-openapi/options.schema.json +62 -0
- package/plugins/extract-openapi/spec.go +350 -0
- package/plugins/extract-project/README.md +22 -0
- package/plugins/extract-project/describe.go +19 -0
- package/plugins/extract-project/describe_test.go +11 -0
- package/plugins/extract-project/extract.go +221 -0
- package/plugins/extract-project/extract_test.go +109 -0
- package/plugins/extract-project/main.go +41 -0
- package/plugins/extract-project/options.schema.json +48 -0
- package/plugins/extract-proto/ast.go +125 -0
- package/plugins/extract-proto/consumes.go +77 -0
- package/plugins/extract-proto/describe.go +19 -0
- package/plugins/extract-proto/extract.go +293 -0
- package/plugins/extract-proto/extract_test.go +459 -0
- package/plugins/extract-proto/ids.go +89 -0
- package/plugins/extract-proto/ids_test.go +57 -0
- package/plugins/extract-proto/lex.go +285 -0
- package/plugins/extract-proto/main.go +100 -0
- package/plugins/extract-proto/module.go +120 -0
- package/plugins/extract-proto/options.schema.json +51 -0
- package/plugins/extract-proto/parse.go +720 -0
- package/plugins/extract-proto/parse_test.go +307 -0
- package/plugins/extract-proto/provides.go +236 -0
- package/plugins/extract-proto/resolve.go +222 -0
- package/plugins/extract-proto/resolve_test.go +119 -0
- package/plugins/extract-python-kafka/README.md +36 -0
- package/plugins/extract-python-kafka/extract.py +211 -0
- package/plugins/extract-python-kafka/extract_test.py +68 -0
- package/plugins/extract-python-kafka/main.py +59 -0
- package/plugins/extract-python-kafka/options.py +29 -0
- package/plugins/extract-python-kafka/options.schema.json +31 -0
- package/plugins/extract-redis/README.md +11 -0
- package/plugins/extract-redis/describe.go +19 -0
- package/plugins/extract-redis/describe_test.go +11 -0
- package/plugins/extract-redis/extract.go +183 -0
- package/plugins/extract-redis/extract_test.go +168 -0
- package/plugins/extract-redis/keyspaces.go +469 -0
- package/plugins/extract-redis/main.go +44 -0
- package/plugins/extract-redis/options.schema.json +30 -0
- package/plugins/extract-river/describe.go +19 -0
- package/plugins/extract-river/describe_test.go +11 -0
- package/plugins/extract-river/extract.go +507 -0
- package/plugins/extract-river/extract_test.go +132 -0
- package/plugins/extract-river/main.go +42 -0
- package/plugins/extract-river/options.schema.json +21 -0
- package/plugins/extract-rust/Cargo.lock +133 -0
- package/plugins/extract-rust/Cargo.toml +23 -0
- package/plugins/extract-rust/README.md +204 -0
- package/plugins/extract-rust/options.schema.json +65 -0
- package/plugins/extract-rust/rustfmt.toml +1 -0
- package/plugins/extract-sql/ddl.go +893 -0
- package/plugins/extract-sql/ddl_test.go +401 -0
- package/plugins/extract-sql/describe.go +19 -0
- package/plugins/extract-sql/describe_test.go +11 -0
- package/plugins/extract-sql/layout.go +221 -0
- package/plugins/extract-sql/layout_test.go +55 -0
- package/plugins/extract-sql/lineage.go +107 -0
- package/plugins/extract-sql/main.go +141 -0
- package/plugins/extract-sql/maps.go +564 -0
- package/plugins/extract-sql/maps_java.go +117 -0
- package/plugins/extract-sql/maps_rust.go +333 -0
- package/plugins/extract-sql/maps_rust_test.go +70 -0
- package/plugins/extract-sql/maps_test.go +204 -0
- package/plugins/extract-sql/maps_ts.go +398 -0
- package/plugins/extract-sql/maps_ts_test.go +136 -0
- package/plugins/extract-sql/options.schema.json +53 -0
- package/plugins/extract-sql/projection.go +70 -0
- package/plugins/extract-sql/projection_test.go +50 -0
- package/plugins/extract-sql/store.go +420 -0
- package/plugins/extract-sql/store_test.go +233 -0
- package/plugins/extract-sql/view.go +295 -0
- package/plugins/extract-ts/README.md +306 -0
- package/plugins/extract-ts/ast.test.ts +178 -0
- package/plugins/extract-ts/ast.ts +648 -0
- package/plugins/extract-ts/clients.ts +185 -0
- package/plugins/extract-ts/domain.ts +164 -0
- package/plugins/extract-ts/extract.test.ts +208 -0
- package/plugins/extract-ts/extract.ts +200 -0
- package/plugins/extract-ts/flows.ts +783 -0
- package/plugins/extract-ts/graphql.test.ts +73 -0
- package/plugins/extract-ts/graphql.ts +181 -0
- package/plugins/extract-ts/ids.test.ts +21 -0
- package/plugins/extract-ts/ids.ts +52 -0
- package/plugins/extract-ts/lifecycle.ts +138 -0
- package/plugins/extract-ts/main.ts +48 -0
- package/plugins/extract-ts/openapi.test.ts +64 -0
- package/plugins/extract-ts/openapi.ts +99 -0
- package/plugins/extract-ts/operations.ts +93 -0
- package/plugins/extract-ts/options.schema.json +96 -0
- package/plugins/extract-ts/source.test.ts +174 -0
- package/plugins/extract-ts/source.ts +465 -0
- package/plugins/extract-ts/transport.ts +147 -0
- package/plugins/extract-ts/tsconfig.json +13 -0
- package/plugins/extract-ts/wiring.ts +190 -0
- package/plugins/extract-watermill/describe.go +19 -0
- package/plugins/extract-watermill/describe_test.go +11 -0
- package/plugins/extract-watermill/extract.go +1228 -0
- package/plugins/extract-watermill/extract_test.go +234 -0
- package/plugins/extract-watermill/main.go +41 -0
- package/plugins/extract-watermill/options.schema.json +21 -0
- package/plugins/extract-wsdl/describe.go +19 -0
- package/plugins/extract-wsdl/describe_test.go +11 -0
- package/plugins/extract-wsdl/extract.go +148 -0
- package/plugins/extract-wsdl/extract_test.go +45 -0
- package/plugins/extract-wsdl/main.go +53 -0
- package/plugins/extract-wsdl/options.schema.json +59 -0
- package/plugins/fetch-bsr/auth.go +114 -0
- package/plugins/fetch-bsr/bsr.go +240 -0
- package/plugins/fetch-bsr/cache.go +66 -0
- package/plugins/fetch-bsr/describe.go +19 -0
- package/plugins/fetch-bsr/fetch.go +192 -0
- package/plugins/fetch-bsr/fetch_test.go +465 -0
- package/plugins/fetch-bsr/lock.go +71 -0
- package/plugins/fetch-bsr/main.go +117 -0
- package/plugins/fetch-bsr/options.schema.json +42 -0
- package/plugins/fetch-csr/auth.go +95 -0
- package/plugins/fetch-csr/cache.go +69 -0
- package/plugins/fetch-csr/describe.go +19 -0
- package/plugins/fetch-csr/describe_test.go +11 -0
- package/plugins/fetch-csr/fetch.go +254 -0
- package/plugins/fetch-csr/fetch_test.go +494 -0
- package/plugins/fetch-csr/lock.go +95 -0
- package/plugins/fetch-csr/main.go +122 -0
- package/plugins/fetch-csr/options.schema.json +42 -0
- package/plugins/fetch-csr/registry.go +209 -0
- package/plugins/fetch-git/cache.go +65 -0
- package/plugins/fetch-git/describe.go +19 -0
- package/plugins/fetch-git/describe_test.go +11 -0
- package/plugins/fetch-git/fetch.go +181 -0
- package/plugins/fetch-git/fetch_test.go +332 -0
- package/plugins/fetch-git/git.go +169 -0
- package/plugins/fetch-git/lock.go +72 -0
- package/plugins/fetch-git/main.go +127 -0
- package/plugins/fetch-git/offline.go +39 -0
- package/plugins/fetch-git/options.schema.json +42 -0
- package/plugins/fetch-git/pin.go +90 -0
- package/plugins/fetch-git/pin_test.go +74 -0
- package/plugins/gen-backstage/describe.go +17 -0
- package/plugins/gen-backstage/main.go +22 -0
- package/plugins/gen-backstage/options.schema.json +30 -0
- package/plugins/gen-backstage/plugin.go +473 -0
- package/plugins/gen-backstage/plugin_test.go +145 -0
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +162 -0
- package/plugins/gen-markdown/aggregate.go +362 -0
- package/plugins/gen-markdown/canonical.go +93 -0
- package/plugins/gen-markdown/context.go +90 -0
- package/plugins/gen-markdown/coverage_test.go +89 -0
- package/plugins/gen-markdown/describe.go +19 -0
- package/plugins/gen-markdown/describe_test.go +11 -0
- package/plugins/gen-markdown/external.go +71 -0
- package/plugins/gen-markdown/flow.go +277 -0
- package/plugins/gen-markdown/glossary.go +76 -0
- package/plugins/gen-markdown/glossary_test.go +148 -0
- package/plugins/gen-markdown/llms.go +302 -0
- package/plugins/gen-markdown/main.go +22 -0
- package/plugins/gen-markdown/markdown.go +254 -0
- package/plugins/gen-markdown/markdown_test.go +100 -0
- package/plugins/gen-markdown/module.go +107 -0
- package/plugins/gen-markdown/options.schema.json +16 -0
- package/plugins/gen-markdown/plugin.go +39 -0
- package/plugins/gen-markdown/quality_test.go +176 -0
- package/plugins/gen-markdown/redis_test.go +38 -0
- package/plugins/gen-markdown/render.go +344 -0
- package/plugins/gen-markdown/render_test.go +186 -0
- package/plugins/gen-markdown/service.go +456 -0
- package/plugins/gen-markdown/source.go +177 -0
- package/plugins/gen-markdown/store.go +201 -0
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +17 -0
- package/plugins/gen-mermaid/main.go +22 -0
- package/plugins/gen-mermaid/options.schema.json +10 -0
- package/plugins/gen-mermaid/plugin.go +104 -0
- package/plugins/gen-mermaid/plugin_test.go +33 -0
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +261 -0
- package/plugins/openapi/ids_test.go +98 -0
- package/plugins/pyplugin/catalog.py +209 -0
- package/plugins/pyplugin/celery_conf.py +436 -0
- package/plugins/pyplugin/celery_tasks.py +76 -0
- package/plugins/pyplugin/kafka.py +570 -0
- package/plugins/pyplugin/names.py +38 -0
- package/plugins/pyplugin/protocol.py +79 -0
- package/plugins/pyplugin/source.py +285 -0
- package/plugins/verify-codeowners/describe.go +19 -0
- package/plugins/verify-codeowners/describe_test.go +11 -0
- package/plugins/verify-codeowners/main.go +75 -0
- package/plugins/verify-codeowners/match.go +85 -0
- package/plugins/verify-codeowners/match_test.go +47 -0
- package/plugins/verify-codeowners/options.schema.json +15 -0
- package/plugins/verify-codeowners/owners.go +164 -0
- package/plugins/verify-codeowners/owners_test.go +225 -0
- package/plugins/verify-codeowners/parse.go +90 -0
- package/plugins/verify-codeowners/parse_test.go +62 -0
- package/plugins/verify-otel/describe.go +19 -0
- package/plugins/verify-otel/describe_test.go +11 -0
- package/plugins/verify-otel/main.go +53 -0
- package/plugins/verify-otel/match.go +336 -0
- package/plugins/verify-otel/options.schema.json +30 -0
- package/plugins/verify-otel/otlp.go +200 -0
- package/plugins/verify-otel/verify.go +734 -0
- package/plugins/verify-otel/verify_test.go +460 -0
- package/portolan.json +1073 -0
- package/public/favicon.svg +12 -0
- package/schema/portolan.schema.json +2558 -0
- package/scripts/README.md +53 -0
- package/scripts/build-report.mjs +60 -0
- package/scripts/build-report.test.mjs +44 -0
- package/scripts/builtin-plugins.mjs +49 -0
- package/scripts/catalog-sources.mjs +67 -0
- package/scripts/diff.mjs +315 -0
- package/scripts/diff.test.mjs +151 -0
- package/scripts/forge-comment.mjs +144 -0
- package/scripts/forge-comment.test.mjs +151 -0
- package/scripts/forge-release.mjs +138 -0
- package/scripts/forge-release.test.mjs +106 -0
- package/scripts/forge.mjs +61 -0
- package/scripts/gen-likec4.mjs +854 -0
- package/scripts/gen-likec4.test.mjs +258 -0
- package/scripts/gen.mjs +603 -0
- package/scripts/generated-docs.test.mjs +66 -0
- package/scripts/local-api.mjs +1192 -0
- package/scripts/local-api.test.mjs +387 -0
- package/scripts/manifest.mjs +214 -0
- package/scripts/manifest.test.mjs +148 -0
- package/scripts/output-path.mjs +79 -0
- package/scripts/output-path.test.mjs +45 -0
- package/scripts/package-smoke.mjs +37 -0
- package/scripts/plugin-host.mjs +419 -0
- package/scripts/plugin-host.test.mjs +109 -0
- package/scripts/plugin-wasm-worker.mjs +34 -0
- package/scripts/run-builtin.mjs +81 -0
- package/scripts/schema.mjs +357 -0
- package/scripts/site-docs.mjs +295 -0
- package/scripts/site-docs.test.mjs +181 -0
- package/scripts/vendor-lock.mjs +58 -0
- package/scripts/vendor-lock.test.mjs +69 -0
- package/src/README.md +38 -0
- package/src/app/App.tsx +448 -0
- package/src/app/BranchPicker.test.ts +46 -0
- package/src/app/BranchPicker.tsx +153 -0
- package/src/app/Breadcrumbs.test.ts +38 -0
- package/src/app/Breadcrumbs.tsx +223 -0
- package/src/app/BuildStamp.tsx +146 -0
- package/src/app/CommandPalette.tsx +292 -0
- package/src/app/HashScroll.tsx +45 -0
- package/src/app/Sidebar.tsx +1742 -0
- package/src/app/TopBar.tsx +393 -0
- package/src/app/density.tsx +64 -0
- package/src/app/forge-access.test.ts +27 -0
- package/src/app/forge-access.tsx +80 -0
- package/src/app/panels.tsx +141 -0
- package/src/app/pins.tsx +242 -0
- package/src/app/query-client.ts +24 -0
- package/src/app/responsive.ts +55 -0
- package/src/app/shortcuts.test.ts +39 -0
- package/src/app/shortcuts.tsx +282 -0
- package/src/app/theme.tsx +51 -0
- package/src/app/title.test.ts +14 -0
- package/src/app/title.ts +30 -0
- package/src/app/toast.tsx +59 -0
- package/src/app/tree-keys.test.ts +80 -0
- package/src/app/tree-keys.ts +87 -0
- package/src/app/ui-store.ts +52 -0
- package/src/catalog-error.test.ts +105 -0
- package/src/catalog-profile.test.ts +59 -0
- package/src/catalog-profile.ts +101 -0
- package/src/catalog-stores.test.ts +431 -0
- package/src/catalog.test.ts +1215 -0
- package/src/catalog.ts +3125 -0
- package/src/chat/Cards.tsx +408 -0
- package/src/chat/ChatPanel.tsx +79 -0
- package/src/chat/ChatSettings.tsx +219 -0
- package/src/chat/Composer.tsx +99 -0
- package/src/chat/Conversation.tsx +281 -0
- package/src/chat/Header.tsx +46 -0
- package/src/chat/Message.tsx +49 -0
- package/src/chat/Notice.tsx +195 -0
- package/src/chat/Starter.tsx +67 -0
- package/src/chat/Steps.tsx +45 -0
- package/src/chat/Waiting.tsx +87 -0
- package/src/chat/flags.test.ts +59 -0
- package/src/chat/flags.ts +155 -0
- package/src/chat/linkify.test.ts +30 -0
- package/src/chat/linkify.ts +45 -0
- package/src/chat/prefs.ts +56 -0
- package/src/chat/prompt.test.ts +43 -0
- package/src/chat/prompt.ts +157 -0
- package/src/chat/store.ts +35 -0
- package/src/chat/tools.ts +58 -0
- package/src/chat/transport.ts +70 -0
- package/src/components/AdrRow.tsx +29 -0
- package/src/components/AnchorLink.tsx +67 -0
- package/src/components/ApiReference.tsx +134 -0
- package/src/components/AsyncApiReference.tsx +120 -0
- package/src/components/CatalogStamp.tsx +155 -0
- package/src/components/ChannelRows.tsx +132 -0
- package/src/components/CommandRows.tsx +139 -0
- package/src/components/DiagramSkeleton.tsx +71 -0
- package/src/components/EditorLink.tsx +85 -0
- package/src/components/FieldTree.tsx +305 -0
- package/src/components/FlowTrigger.tsx +34 -0
- package/src/components/Ident.tsx +89 -0
- package/src/components/Integrations.tsx +243 -0
- package/src/components/LifecycleDiagram.tsx +110 -0
- package/src/components/MachineDocs.tsx +42 -0
- package/src/components/Markdown.tsx +173 -0
- package/src/components/MarkdownTable.tsx +206 -0
- package/src/components/Mermaid.tsx +211 -0
- package/src/components/MethodRows.tsx +309 -0
- package/src/components/Overlay.tsx +125 -0
- package/src/components/PageHeader.tsx +189 -0
- package/src/components/RowActions.tsx +97 -0
- package/src/components/SchemaDocument.tsx +89 -0
- package/src/components/Select.tsx +126 -0
- package/src/components/ShapeRows.tsx +118 -0
- package/src/components/SourceDoc.tsx +82 -0
- package/src/components/SourcePreview.tsx +441 -0
- package/src/components/TabRow.tsx +182 -0
- package/src/components/TechIcon.tsx +34 -0
- package/src/components/Toc.tsx +132 -0
- package/src/components/WhatLinksHere.tsx +273 -0
- package/src/components/WsdlReference.tsx +84 -0
- package/src/components/ddd-icons.tsx +107 -0
- package/src/components/kind.tsx +157 -0
- package/src/components/logo.tsx +61 -0
- package/src/components/primitives.tsx +264 -0
- package/src/data.test.ts +18 -0
- package/src/data.ts +202 -0
- package/src/enrich.test.ts +1150 -0
- package/src/enrich.ts +672 -0
- package/src/er/ErCanvas.tsx +533 -0
- package/src/er/RedisSchema.tsx +74 -0
- package/src/er/RelationCard.tsx +267 -0
- package/src/er/StoreHeader.tsx +155 -0
- package/src/er/TableNode.tsx +84 -0
- package/src/er/ViewNode.tsx +83 -0
- package/src/er/layout.test.ts +115 -0
- package/src/er/layout.ts +77 -0
- package/src/er/lineage.test.ts +140 -0
- package/src/er/lineage.ts +107 -0
- package/src/er/markers.tsx +79 -0
- package/src/er/spec.test.ts +344 -0
- package/src/er/spec.ts +426 -0
- package/src/flow/ChainList.tsx +162 -0
- package/src/flow/FlowTable.tsx +123 -0
- package/src/flow/FlowToolbar.tsx +372 -0
- package/src/flow/StepDetail.tsx +380 -0
- package/src/flow/StepRail.tsx +428 -0
- package/src/flow/answers.test.ts +50 -0
- package/src/flow/answers.ts +58 -0
- package/src/flow/chain.test.ts +383 -0
- package/src/flow/chain.ts +353 -0
- package/src/flow/chapters.test.ts +185 -0
- package/src/flow/chapters.ts +225 -0
- package/src/flow/continues.test.ts +102 -0
- package/src/flow/continues.ts +65 -0
- package/src/flow/cross-context.test.ts +97 -0
- package/src/flow/cross-context.ts +35 -0
- package/src/flow/mermaid.test.ts +22 -0
- package/src/flow/mermaid.ts +141 -0
- package/src/flow/outline.test.ts +212 -0
- package/src/flow/outline.ts +170 -0
- package/src/flow/paths.test.ts +129 -0
- package/src/flow/paths.ts +147 -0
- package/src/flow/prefs.test.ts +80 -0
- package/src/flow/prefs.ts +104 -0
- package/src/graph/DependencyGraph.tsx +283 -0
- package/src/graph/DependencyNodes.tsx +162 -0
- package/src/graph/EdgeLabel.tsx +61 -0
- package/src/graph/FocusedEventGraph.tsx +211 -0
- package/src/graph/GraphToolbar.tsx +187 -0
- package/src/graph/RoutedEdge.tsx +80 -0
- package/src/graph/dependency-layout.test.ts +360 -0
- package/src/graph/dependency-layout.ts +444 -0
- package/src/graph/elk.test.ts +70 -0
- package/src/graph/elk.ts +287 -0
- package/src/graph/nodes.tsx +97 -0
- package/src/graph/theme.ts +81 -0
- package/src/graph/useElkFlow.ts +125 -0
- package/src/index.css +1547 -0
- package/src/language/InLanguage.tsx +40 -0
- package/src/language/TermCard.tsx +132 -0
- package/src/language/cards.test.ts +114 -0
- package/src/language/cards.ts +111 -0
- package/src/lib/admonition.test.ts +49 -0
- package/src/lib/admonition.ts +48 -0
- package/src/lib/adr.test.ts +131 -0
- package/src/lib/adr.ts +68 -0
- package/src/lib/api.test.ts +122 -0
- package/src/lib/api.ts +135 -0
- package/src/lib/backlinks.test.ts +330 -0
- package/src/lib/backlinks.ts +567 -0
- package/src/lib/branch-compare.ts +16 -0
- package/src/lib/build-info.test.ts +76 -0
- package/src/lib/build-info.ts +80 -0
- package/src/lib/catalog-diff.test.ts +275 -0
- package/src/lib/catalog-diff.ts +655 -0
- package/src/lib/clipboard.ts +43 -0
- package/src/lib/comparison-memory.test.ts +40 -0
- package/src/lib/comparison-memory.ts +39 -0
- package/src/lib/context-color.ts +27 -0
- package/src/lib/context-map.test.ts +148 -0
- package/src/lib/context-map.ts +399 -0
- package/src/lib/data-model.test.ts +245 -0
- package/src/lib/data-model.ts +399 -0
- package/src/lib/data-problems.test.ts +178 -0
- package/src/lib/data-problems.ts +314 -0
- package/src/lib/derive.test.ts +251 -0
- package/src/lib/derive.ts +515 -0
- package/src/lib/editor-link.test.ts +125 -0
- package/src/lib/editor-link.ts +96 -0
- package/src/lib/editor-prefs.ts +27 -0
- package/src/lib/event-graph.test.ts +237 -0
- package/src/lib/event-graph.ts +327 -0
- package/src/lib/export-canvas.ts +37 -0
- package/src/lib/export-file.test.ts +30 -0
- package/src/lib/export-file.ts +20 -0
- package/src/lib/flow-tree.test.ts +177 -0
- package/src/lib/flow-tree.ts +160 -0
- package/src/lib/forge-refs.test.ts +50 -0
- package/src/lib/forge-refs.ts +60 -0
- package/src/lib/format.test.ts +35 -0
- package/src/lib/format.ts +64 -0
- package/src/lib/github-catalog.test.ts +301 -0
- package/src/lib/github-catalog.ts +610 -0
- package/src/lib/integrations.test.ts +119 -0
- package/src/lib/integrations.ts +133 -0
- package/src/lib/kinds.test.ts +91 -0
- package/src/lib/kinds.ts +204 -0
- package/src/lib/lifecycle.test.ts +93 -0
- package/src/lib/lifecycle.ts +165 -0
- package/src/lib/local-api.ts +194 -0
- package/src/lib/motion-features.ts +11 -0
- package/src/lib/motion.test.ts +120 -0
- package/src/lib/motion.tsx +269 -0
- package/src/lib/palette-recent.test.ts +80 -0
- package/src/lib/palette-recent.ts +90 -0
- package/src/lib/palette.test.ts +365 -0
- package/src/lib/palette.ts +593 -0
- package/src/lib/pins.test.ts +139 -0
- package/src/lib/pins.ts +130 -0
- package/src/lib/problems.test.ts +112 -0
- package/src/lib/project-preview.ts +5 -0
- package/src/lib/proto-problems.test.ts +121 -0
- package/src/lib/proto-problems.ts +65 -0
- package/src/lib/queries.test.ts +113 -0
- package/src/lib/queries.ts +131 -0
- package/src/lib/registry.test.ts +219 -0
- package/src/lib/registry.ts +208 -0
- package/src/lib/scenarios.ts +547 -0
- package/src/lib/setup-info.test.ts +199 -0
- package/src/lib/setup-info.ts +415 -0
- package/src/lib/shape.test.ts +289 -0
- package/src/lib/shape.ts +454 -0
- package/src/lib/sidebar-prefs.test.ts +32 -0
- package/src/lib/sidebar-prefs.ts +75 -0
- package/src/lib/source-code.test.ts +159 -0
- package/src/lib/source-code.ts +286 -0
- package/src/lib/source-doc.test.ts +131 -0
- package/src/lib/source-doc.ts +103 -0
- package/src/lib/source-highlight.test.ts +29 -0
- package/src/lib/source-highlight.ts +24 -0
- package/src/lib/source-link.test.ts +153 -0
- package/src/lib/source-link.ts +201 -0
- package/src/lib/spec-files.test.ts +24 -0
- package/src/lib/spec-files.ts +23 -0
- package/src/lib/table-directive.test.ts +139 -0
- package/src/lib/table-directive.ts +106 -0
- package/src/lib/tech.test.ts +120 -0
- package/src/lib/tech.ts +421 -0
- package/src/lib/terms.test.ts +67 -0
- package/src/lib/terms.ts +179 -0
- package/src/lib/wire-problems.test.ts +263 -0
- package/src/lib/wire-problems.ts +281 -0
- package/src/likec4/C4View.tsx +55 -0
- package/src/likec4/CanvasBridge.tsx +187 -0
- package/src/likec4/FlowView.tsx +235 -0
- package/src/likec4/InteractiveView.tsx +144 -0
- package/src/likec4/canvas-viewport.test.ts +149 -0
- package/src/likec4/canvas-viewport.ts +135 -0
- package/src/likec4/flow-edges.test.ts +51 -0
- package/src/likec4/flow-edges.ts +89 -0
- package/src/likec4/frame-theme.test.ts +76 -0
- package/src/likec4/frame-theme.ts +101 -0
- package/src/likec4/highlight-css.test.ts +49 -0
- package/src/likec4/highlight-css.ts +52 -0
- package/src/likec4/ids.test.ts +151 -0
- package/src/likec4/ids.ts +115 -0
- package/src/likec4/levels.tsx +70 -0
- package/src/likec4/mapping.test.ts +66 -0
- package/src/likec4/mapping.ts +54 -0
- package/src/likec4/reserved.json +141 -0
- package/src/likec4/view-index.ts +78 -0
- package/src/main.tsx +13 -0
- package/src/map/ContextMapGraph.tsx +274 -0
- package/src/map/ContextNode.tsx +70 -0
- package/src/map/FloatingEdge.tsx +86 -0
- package/src/merge.test.ts +1038 -0
- package/src/merge.ts +1004 -0
- package/src/pages/AdrDetail.tsx +242 -0
- package/src/pages/AdrIndex.tsx +114 -0
- package/src/pages/AggregatePage.tsx +727 -0
- package/src/pages/BlockPage.tsx +478 -0
- package/src/pages/CatalogFailure.tsx +111 -0
- package/src/pages/Changes.tsx +313 -0
- package/src/pages/ContextMap.tsx +534 -0
- package/src/pages/ContextPage.tsx +346 -0
- package/src/pages/EnumPage.tsx +282 -0
- package/src/pages/EventPage.tsx +686 -0
- package/src/pages/ExternalPage.tsx +204 -0
- package/src/pages/FlowDetail.tsx +663 -0
- package/src/pages/FlowIndex.tsx +303 -0
- package/src/pages/GraphPage.tsx +217 -0
- package/src/pages/Language.tsx +257 -0
- package/src/pages/ModulePage.tsx +366 -0
- package/src/pages/NotFound.tsx +54 -0
- package/src/pages/Overview.tsx +344 -0
- package/src/pages/Problems.tsx +360 -0
- package/src/pages/RegistryIndex.tsx +118 -0
- package/src/pages/ServicePage.tsx +746 -0
- package/src/pages/Settings.tsx +975 -0
- package/src/pages/StorePage.tsx +114 -0
- package/src/peek/PeekCard.test.ts +35 -0
- package/src/peek/PeekCard.tsx +118 -0
- package/src/peek/PeekLayer.tsx +147 -0
- package/src/peek/model.test.ts +156 -0
- package/src/peek/model.ts +253 -0
- package/src/routes.test.ts +222 -0
- package/src/routes.ts +449 -0
- package/src/selection/DetailPanel.tsx +1407 -0
- package/src/selection/hash.test.ts +73 -0
- package/src/selection/hash.ts +58 -0
- package/src/selection/model.test.ts +199 -0
- package/src/selection/model.ts +473 -0
- package/src/selection/pages.test.ts +115 -0
- package/src/selection/pages.ts +268 -0
- package/src/selection/store.test.ts +62 -0
- package/src/selection/store.ts +46 -0
- package/src/selection/sync.tsx +99 -0
- package/src/table/ColumnsMenu.tsx +72 -0
- package/src/table/DataTable.tsx +780 -0
- package/src/table/Facets.tsx +54 -0
- package/src/table/GroupMenu.tsx +69 -0
- package/src/table/Toolbar.tsx +190 -0
- package/src/table/cells.tsx +117 -0
- package/src/table/compare.test.ts +117 -0
- package/src/table/compare.ts +196 -0
- package/src/table/export.test.ts +88 -0
- package/src/table/export.ts +66 -0
- package/src/table/facet-groups.test.ts +74 -0
- package/src/table/facet-groups.ts +56 -0
- package/src/table/grouping.test.ts +84 -0
- package/src/table/grouping.ts +82 -0
- package/src/table/infer.test.ts +57 -0
- package/src/table/infer.ts +50 -0
- package/src/table/persist.test.ts +76 -0
- package/src/table/persist.ts +112 -0
- package/src/table/sort-url.test.ts +75 -0
- package/src/table/sort-url.ts +52 -0
- package/src/table/types.ts +80 -0
- package/src/table/useTableState.ts +220 -0
- package/src/test-catalog.ts +7 -0
- package/src/testing/estate/data__catalog.json +2847 -0
- package/src/testing/estate/data__flows.json +1724 -0
- package/src/testing/estate/examples__auth__portolan__api.json +212 -0
- package/src/testing/estate/examples__auth__portolan__domain.json +1204 -0
- package/src/testing/estate/examples__auth__portolan__glossary.json +226 -0
- package/src/testing/estate/examples__auth__portolan__observed.json +757 -0
- package/src/testing/estate/examples__auth__portolan__stores.json +195 -0
- package/src/testing/estate/examples__shop__cart__portolan__api.json +234 -0
- package/src/testing/estate/examples__shop__cart__portolan__commands.json +86 -0
- package/src/testing/estate/examples__shop__cart__portolan__domain.json +966 -0
- package/src/testing/estate/examples__shop__cart__portolan__observed.json +498 -0
- package/src/testing/estate/examples__shop__cart__portolan__stores.json +208 -0
- package/src/testing/estate/examples__shop__oms__portolan__glossary.json +98 -0
- package/src/testing/estate/examples__shop__oms__portolan__proto.json +186 -0
- package/src/testing/estate.ts +49 -0
- package/src/trail/Trail.tsx +81 -0
- package/src/trail/model.test.ts +157 -0
- package/src/trail/model.ts +189 -0
- package/src/trail/record.tsx +32 -0
- package/src/trail/store.test.ts +66 -0
- package/src/trail/store.ts +102 -0
- package/tsconfig.json +35 -0
- package/vite.config.ts +171 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/parser"
|
|
6
|
+
"go/token"
|
|
7
|
+
"os"
|
|
8
|
+
"path"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"regexp"
|
|
11
|
+
"strconv"
|
|
12
|
+
"strings"
|
|
13
|
+
|
|
14
|
+
"github.com/pgplex/pgparser/nodes"
|
|
15
|
+
pgsql "github.com/pgplex/pgparser/parser"
|
|
16
|
+
|
|
17
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// Which domain field a column carries.
|
|
21
|
+
//
|
|
22
|
+
// This is the fact that makes a schema readable next to a model rather than
|
|
23
|
+
// beside it, and it is the one thing here that cannot be read off the layout.
|
|
24
|
+
// Column names do not carry it: `password_hash` is not `Password` by any rule
|
|
25
|
+
// that is not a guess, and a rule that guesses would be wrong quietly.
|
|
26
|
+
//
|
|
27
|
+
// What does carry it is the statement that writes the row:
|
|
28
|
+
//
|
|
29
|
+
// `INSERT INTO users (id, email, password_hash, created_at, version)
|
|
30
|
+
// VALUES ($1, $2, $3, $4, 1)`,
|
|
31
|
+
// u.ID, u.Email.String(), u.Password.String(), u.CreatedAt)
|
|
32
|
+
//
|
|
33
|
+
// Five columns, four arguments. The alignment is through the placeholders, not
|
|
34
|
+
// through the column order: `version` is written as a literal, and counting
|
|
35
|
+
// positions would have mapped it to a field it has nothing to do with.
|
|
36
|
+
//
|
|
37
|
+
// The rule is all-or-nothing per column. Either the chain from column to `$N`
|
|
38
|
+
// to argument to field is unambiguous, or the column gets no `maps` at all -
|
|
39
|
+
// a blank is a reader looking the column up themselves, and a wrong one is a
|
|
40
|
+
// reader believing something untrue.
|
|
41
|
+
func readMaps(root, repositoryDir, aggregate string, b *plugin.Builder) map[string]map[string]string {
|
|
42
|
+
out := map[string]map[string]string{}
|
|
43
|
+
|
|
44
|
+
dir := mapSourceDir(root, repositoryDir, aggregate)
|
|
45
|
+
entries, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(dir)))
|
|
46
|
+
if err != nil {
|
|
47
|
+
return out
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The root type is the one the aggregate's package is named after, which is
|
|
51
|
+
// the same rule the domain extractor used to pick it.
|
|
52
|
+
root_ := title(aggregate)
|
|
53
|
+
fset := token.NewFileSet()
|
|
54
|
+
|
|
55
|
+
files := make([]*ast.File, 0, len(entries))
|
|
56
|
+
|
|
57
|
+
for _, entry := range entries {
|
|
58
|
+
name := entry.Name()
|
|
59
|
+
if entry.IsDir() || !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
file, err := parser.ParseFile(fset, filepath.Join(root, filepath.FromSlash(dir), name), nil, parser.SkipObjectResolution)
|
|
64
|
+
if err != nil {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
files = append(files, file)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A statement is routinely assembled from a shared column list rather than
|
|
71
|
+
// written whole, so the package's string constants are collected before
|
|
72
|
+
// anything is read and folded into the literals that name them.
|
|
73
|
+
constants := stringConstants(files)
|
|
74
|
+
|
|
75
|
+
for _, file := range files {
|
|
76
|
+
packages := importNames(file)
|
|
77
|
+
|
|
78
|
+
// One walk per declaration rather than one per file, because which
|
|
79
|
+
// identifier is the aggregate changes at every function: in Save it
|
|
80
|
+
// is the parameter typed as the root, and inside the loop over its
|
|
81
|
+
// lines the same shape of expression on the loop variable is a line's
|
|
82
|
+
// field, not the aggregate's.
|
|
83
|
+
for _, decl := range file.Decls {
|
|
84
|
+
fn, ok := decl.(*ast.FuncDecl)
|
|
85
|
+
if !ok || fn.Body == nil {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
s := scope{packages: packages, roots: rootParams(fn, root_), aliases: rangeAliases(fn)}
|
|
89
|
+
|
|
90
|
+
ast.Inspect(fn.Body, func(node ast.Node) bool {
|
|
91
|
+
call, ok := node.(*ast.CallExpr)
|
|
92
|
+
if !ok {
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
table, sql, columns, values, args := readInsert(call, constants)
|
|
97
|
+
if table == "" {
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
mapped := out[table]
|
|
102
|
+
if mapped == nil {
|
|
103
|
+
mapped = map[string]string{}
|
|
104
|
+
out[table] = mapped
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// The parser records that a value IS a placeholder but not which
|
|
108
|
+
// one - ParamRef.Number comes back zero - so the numbers are read
|
|
109
|
+
// off the text and paired with the placeholders in order. If the
|
|
110
|
+
// two do not agree on how many there are, nothing is mapped:
|
|
111
|
+
// mapping by position from a disagreement is how a column ends up
|
|
112
|
+
// confidently pointing at the wrong field.
|
|
113
|
+
numbers := placeholderNumbers(sql)
|
|
114
|
+
if numbers == nil || countParams(values) != len(numbers) {
|
|
115
|
+
if countParams(values) > 0 {
|
|
116
|
+
b.Warn(table, "the insert into "+table+" has placeholders this reader cannot number; its columns are left unmapped")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
seen := 0
|
|
123
|
+
for i, column := range columns {
|
|
124
|
+
if i >= len(values) {
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if _, ok := values[i].(*nodes.ParamRef); !ok {
|
|
129
|
+
// A literal, a default, an expression: written by the
|
|
130
|
+
// statement rather than carried from the aggregate.
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
number := numbers[seen]
|
|
135
|
+
seen++
|
|
136
|
+
|
|
137
|
+
if number < 1 || number > len(args) {
|
|
138
|
+
b.Warn(table, "the insert refers to $"+itoa(number)+" but the call passes "+itoa(len(args))+" values; "+column+" is left unmapped")
|
|
139
|
+
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
field := fieldOf(args[number-1], s)
|
|
144
|
+
if field == "" {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
mapped[column] = root_ + "." + field
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return true
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return out
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// scope is what fieldOf needs to know about the code around an argument that
|
|
159
|
+
// the argument itself does not say: which bare names are packages, and which
|
|
160
|
+
// one is the aggregate.
|
|
161
|
+
type scope struct {
|
|
162
|
+
// packages are the names the file's imports bind. `time.Now()` and
|
|
163
|
+
// `q.ID()` are the same shape of expression, and this is what tells them
|
|
164
|
+
// apart without a type checker.
|
|
165
|
+
packages map[string]bool
|
|
166
|
+
// roots are the parameters of the enclosing function typed as the
|
|
167
|
+
// aggregate's root. Nil when it has none, in which case any bare name is
|
|
168
|
+
// taken for the aggregate, as it always was; a function that names the
|
|
169
|
+
// root can only mean the root by it.
|
|
170
|
+
roots map[string]bool
|
|
171
|
+
// aliases are the variables of the enclosing function's range loops and
|
|
172
|
+
// what each one ranges over: `for _, l := range q.Lines()` makes l stand
|
|
173
|
+
// for one of q.Lines(), so l.SKU() is Lines.SKU - a field of the
|
|
174
|
+
// elements of a field, which is what a child table's column carries.
|
|
175
|
+
aliases map[string]ast.Expr
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// without is the scope with one alias forgotten, which is what following
|
|
179
|
+
// that alias reads the rest of the expression in: an alias cannot stand for
|
|
180
|
+
// itself, however the loop was written.
|
|
181
|
+
func (s scope) without(alias string) scope {
|
|
182
|
+
rest := make(map[string]ast.Expr, len(s.aliases))
|
|
183
|
+
for name, expr := range s.aliases {
|
|
184
|
+
if name != alias {
|
|
185
|
+
rest[name] = expr
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
s.aliases = rest
|
|
189
|
+
|
|
190
|
+
return s
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// rangeAliases reads every range loop of fn: the value variable, and the
|
|
194
|
+
// expression it walks.
|
|
195
|
+
func rangeAliases(fn *ast.FuncDecl) map[string]ast.Expr {
|
|
196
|
+
var aliases map[string]ast.Expr
|
|
197
|
+
ast.Inspect(fn.Body, func(node ast.Node) bool {
|
|
198
|
+
loop, ok := node.(*ast.RangeStmt)
|
|
199
|
+
if !ok {
|
|
200
|
+
return true
|
|
201
|
+
}
|
|
202
|
+
value, ok := loop.Value.(*ast.Ident)
|
|
203
|
+
if !ok || value.Name == "_" {
|
|
204
|
+
return true
|
|
205
|
+
}
|
|
206
|
+
if aliases == nil {
|
|
207
|
+
aliases = map[string]ast.Expr{}
|
|
208
|
+
}
|
|
209
|
+
aliases[value.Name] = loop.X
|
|
210
|
+
|
|
211
|
+
return true
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
return aliases
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// receiver says whether a bare identifier stands for the aggregate.
|
|
218
|
+
func (s scope) receiver(ident *ast.Ident) bool {
|
|
219
|
+
if s.packages[ident.Name] {
|
|
220
|
+
return false
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return s.roots == nil || s.roots[ident.Name]
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// importNames reads what the file calls each package it imports: the alias
|
|
227
|
+
// when there is one, the last path element otherwise. The last element is a
|
|
228
|
+
// guess only for a package whose directory is not its name, and a package like
|
|
229
|
+
// that used as a receiver would have to be a variable of the same name to be
|
|
230
|
+
// wrong.
|
|
231
|
+
func importNames(file *ast.File) map[string]bool {
|
|
232
|
+
names := map[string]bool{}
|
|
233
|
+
for _, spec := range file.Imports {
|
|
234
|
+
name := ""
|
|
235
|
+
if spec.Name != nil {
|
|
236
|
+
name = spec.Name.Name
|
|
237
|
+
} else {
|
|
238
|
+
name = path.Base(unquote(spec.Path.Value))
|
|
239
|
+
}
|
|
240
|
+
if name == "" || name == "_" || name == "." {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
names[name] = true
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return names
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// rootParams names the parameters of fn whose type is the aggregate's root:
|
|
250
|
+
// `u *user.User` when the root is User, whether the type is a pointer, is
|
|
251
|
+
// qualified by its package or is written bare. Nil when there is none.
|
|
252
|
+
func rootParams(fn *ast.FuncDecl, root string) map[string]bool {
|
|
253
|
+
var roots map[string]bool
|
|
254
|
+
if fn.Type.Params == nil {
|
|
255
|
+
return nil
|
|
256
|
+
}
|
|
257
|
+
for _, param := range fn.Type.Params.List {
|
|
258
|
+
if !typeNamed(param.Type, root) {
|
|
259
|
+
continue
|
|
260
|
+
}
|
|
261
|
+
for _, name := range param.Names {
|
|
262
|
+
if roots == nil {
|
|
263
|
+
roots = map[string]bool{}
|
|
264
|
+
}
|
|
265
|
+
roots[name.Name] = true
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return roots
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func typeNamed(expr ast.Expr, name string) bool {
|
|
273
|
+
switch t := expr.(type) {
|
|
274
|
+
case *ast.StarExpr:
|
|
275
|
+
return typeNamed(t.X, name)
|
|
276
|
+
case *ast.SelectorExpr:
|
|
277
|
+
return t.Sel.Name == name
|
|
278
|
+
case *ast.Ident:
|
|
279
|
+
return t.Name == name
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return false
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// readInsert pulls the parts out of a call whose argument is an INSERT: the
|
|
286
|
+
// table, its column list, the values written into it, and the arguments the
|
|
287
|
+
// placeholders stand for.
|
|
288
|
+
func readInsert(call *ast.CallExpr, constants map[string]string) (string, string, []string, []nodes.Node, []ast.Expr) {
|
|
289
|
+
for i, arg := range call.Args {
|
|
290
|
+
sql, ok := stringValue(arg, constants)
|
|
291
|
+
if !ok || !strings.Contains(strings.ToUpper(sql), "INSERT INTO") {
|
|
292
|
+
continue
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
tree, err := pgsql.Parse(sql)
|
|
296
|
+
if err != nil || len(tree.Items) == 0 {
|
|
297
|
+
return "", "", nil, nil, nil
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
insert, ok := tree.Items[0].(*nodes.InsertStmt)
|
|
301
|
+
if !ok || insert.Relation == nil {
|
|
302
|
+
return "", "", nil, nil, nil
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var columns []string
|
|
306
|
+
for _, item := range items(insert.Cols) {
|
|
307
|
+
if target, ok := item.(*nodes.ResTarget); ok {
|
|
308
|
+
columns = append(columns, target.Name)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return relationName(insert.Relation), sql, columns, insertValues(insert), call.Args[i+1:]
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return "", "", nil, nil, nil
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// stringConstants collects the package's string constants, which is where a
|
|
319
|
+
// column list usually lives when a statement is not written whole.
|
|
320
|
+
func stringConstants(files []*ast.File) map[string]string {
|
|
321
|
+
out := map[string]string{}
|
|
322
|
+
|
|
323
|
+
for _, file := range files {
|
|
324
|
+
for _, decl := range file.Decls {
|
|
325
|
+
gen, ok := decl.(*ast.GenDecl)
|
|
326
|
+
if !ok || gen.Tok != token.CONST {
|
|
327
|
+
continue
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
for _, spec := range gen.Specs {
|
|
331
|
+
value, ok := spec.(*ast.ValueSpec)
|
|
332
|
+
if !ok {
|
|
333
|
+
continue
|
|
334
|
+
}
|
|
335
|
+
for i, name := range value.Names {
|
|
336
|
+
if i >= len(value.Values) {
|
|
337
|
+
break
|
|
338
|
+
}
|
|
339
|
+
if literal, ok := value.Values[i].(*ast.BasicLit); ok && literal.Kind == token.STRING {
|
|
340
|
+
out[name.Name] = unquote(literal.Value)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return out
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// stringValue folds an expression down to the string it is, following the
|
|
351
|
+
// concatenation and the constants a statement is commonly built from. Anything
|
|
352
|
+
// it cannot fold answers false, and the statement is left alone rather than
|
|
353
|
+
// read half-resolved.
|
|
354
|
+
func stringValue(expr ast.Expr, constants map[string]string) (string, bool) {
|
|
355
|
+
switch value := expr.(type) {
|
|
356
|
+
case *ast.BasicLit:
|
|
357
|
+
if value.Kind != token.STRING {
|
|
358
|
+
return "", false
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return unquote(value.Value), true
|
|
362
|
+
|
|
363
|
+
case *ast.Ident:
|
|
364
|
+
text, known := constants[value.Name]
|
|
365
|
+
|
|
366
|
+
return text, known
|
|
367
|
+
|
|
368
|
+
case *ast.ParenExpr:
|
|
369
|
+
return stringValue(value.X, constants)
|
|
370
|
+
|
|
371
|
+
case *ast.BinaryExpr:
|
|
372
|
+
if value.Op != token.ADD {
|
|
373
|
+
return "", false
|
|
374
|
+
}
|
|
375
|
+
left, ok := stringValue(value.X, constants)
|
|
376
|
+
if !ok {
|
|
377
|
+
return "", false
|
|
378
|
+
}
|
|
379
|
+
right, ok := stringValue(value.Y, constants)
|
|
380
|
+
if !ok {
|
|
381
|
+
return "", false
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return left + right, true
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return "", false
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// placeholderNumbers reads $1, $2 ... out of the values clause, in the order
|
|
391
|
+
// they are written.
|
|
392
|
+
//
|
|
393
|
+
// It exists because the grammar port keeps a placeholder as a node with no
|
|
394
|
+
// number on it. Taking the order of the nodes as the order of the arguments
|
|
395
|
+
// would be right for every statement written in ascending order and wrong,
|
|
396
|
+
// silently, for the one that is not.
|
|
397
|
+
func placeholderNumbers(sql string) []int {
|
|
398
|
+
upper := strings.ToUpper(sql)
|
|
399
|
+
|
|
400
|
+
at := strings.LastIndex(upper, "VALUES")
|
|
401
|
+
if at < 0 {
|
|
402
|
+
return nil
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
clause := sql[at:]
|
|
406
|
+
if end := strings.Index(strings.ToUpper(clause), "RETURNING"); end >= 0 {
|
|
407
|
+
clause = clause[:end]
|
|
408
|
+
}
|
|
409
|
+
if end := strings.Index(strings.ToUpper(clause), "ON CONFLICT"); end >= 0 {
|
|
410
|
+
clause = clause[:end]
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
var out []int
|
|
414
|
+
for _, match := range placeholder.FindAllStringSubmatch(clause, -1) {
|
|
415
|
+
number, err := strconv.Atoi(match[1])
|
|
416
|
+
if err != nil {
|
|
417
|
+
return nil
|
|
418
|
+
}
|
|
419
|
+
out = append(out, number)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return out
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
var placeholder = regexp.MustCompile(`\$(\d+)`)
|
|
426
|
+
|
|
427
|
+
func countParams(values []nodes.Node) int {
|
|
428
|
+
n := 0
|
|
429
|
+
for _, value := range values {
|
|
430
|
+
if _, ok := value.(*nodes.ParamRef); ok {
|
|
431
|
+
n++
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return n
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// insertValues is the single VALUES row of an insert. An insert from a SELECT,
|
|
439
|
+
// or one writing more than one row, is not read: the first has no per-column
|
|
440
|
+
// argument to find and the second would need every row to agree.
|
|
441
|
+
func insertValues(insert *nodes.InsertStmt) []nodes.Node {
|
|
442
|
+
source, ok := insert.SelectStmt.(*nodes.SelectStmt)
|
|
443
|
+
if !ok {
|
|
444
|
+
return nil
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
rows := items(source.ValuesLists)
|
|
448
|
+
if len(rows) != 1 {
|
|
449
|
+
return nil
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
row, ok := rows[0].(*nodes.List)
|
|
453
|
+
if !ok {
|
|
454
|
+
return nil
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return row.Items
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// fieldOf reads the field an argument came from: `u.ID` is ID, and
|
|
461
|
+
// `u.Email.String()` is Email, because what the column carries is the value
|
|
462
|
+
// object, not the conversion applied to it on the way out. An aggregate that
|
|
463
|
+
// keeps its fields unexported hands them out through getters, and `q.ID()` is
|
|
464
|
+
// ID by the same reading: a method with no arguments on the aggregate itself
|
|
465
|
+
// is the field it is named after, in whatever case the domain spells it.
|
|
466
|
+
//
|
|
467
|
+
// The first selector standing on the aggregate wins. Anything with no such
|
|
468
|
+
// selector - a local variable, a function of several fields, a literal -
|
|
469
|
+
// answers empty, and the column stays unmapped.
|
|
470
|
+
func fieldOf(arg ast.Expr, s scope) string {
|
|
471
|
+
for {
|
|
472
|
+
switch expr := arg.(type) {
|
|
473
|
+
case *ast.CallExpr:
|
|
474
|
+
// Three shapes hide the field in different places, and they look
|
|
475
|
+
// alike. `u.Email.String()` and `q.Total().AmountMinor()` read it
|
|
476
|
+
// off their receiver, which is itself a selector or a call on
|
|
477
|
+
// one; `q.ID()` reads it off the aggregate, with nothing to pass;
|
|
478
|
+
// `pgtype.Text(u.Email)` takes it as an argument. What tells the
|
|
479
|
+
// last from the second without a type checker is the file's
|
|
480
|
+
// imports: a call with no arguments on a package name is a
|
|
481
|
+
// function, and on anything else a getter.
|
|
482
|
+
if method, ok := expr.Fun.(*ast.SelectorExpr); ok {
|
|
483
|
+
switch on := method.X.(type) {
|
|
484
|
+
case *ast.SelectorExpr, *ast.CallExpr:
|
|
485
|
+
arg = expr.Fun
|
|
486
|
+
|
|
487
|
+
continue
|
|
488
|
+
case *ast.Ident:
|
|
489
|
+
if len(expr.Args) == 0 && !s.packages[on.Name] {
|
|
490
|
+
arg = expr.Fun
|
|
491
|
+
|
|
492
|
+
continue
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Otherwise the value came through the call, and only an argument
|
|
498
|
+
// of exactly one says which field: a function of two has no single
|
|
499
|
+
// field to name, and one of none has no field at all.
|
|
500
|
+
if len(expr.Args) != 1 {
|
|
501
|
+
return ""
|
|
502
|
+
}
|
|
503
|
+
arg = expr.Args[0]
|
|
504
|
+
case *ast.SelectorExpr:
|
|
505
|
+
if ident, ok := expr.X.(*ast.Ident); ok {
|
|
506
|
+
if over, ok := s.aliases[ident.Name]; ok {
|
|
507
|
+
head := fieldOf(over, s.without(ident.Name))
|
|
508
|
+
if head == "" {
|
|
509
|
+
return ""
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return head + "." + expr.Sel.Name
|
|
513
|
+
}
|
|
514
|
+
if !s.receiver(ident) {
|
|
515
|
+
return ""
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return expr.Sel.Name
|
|
519
|
+
}
|
|
520
|
+
arg = expr.X
|
|
521
|
+
case *ast.UnaryExpr:
|
|
522
|
+
arg = expr.X
|
|
523
|
+
case *ast.ParenExpr:
|
|
524
|
+
arg = expr.X
|
|
525
|
+
default:
|
|
526
|
+
return ""
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// unquote handles both spellings a Go string literal comes in. The statements
|
|
532
|
+
// here are raw-quoted because they span lines, but a short one need not be.
|
|
533
|
+
// title is the type a package is named after: user becomes User,
|
|
534
|
+
// price_list becomes PriceList.
|
|
535
|
+
func title(name string) string {
|
|
536
|
+
var b strings.Builder
|
|
537
|
+
for _, word := range strings.FieldsFunc(name, func(r rune) bool { return r == '_' || r == '-' }) {
|
|
538
|
+
runes := []rune(word)
|
|
539
|
+
if runes[0] >= 'a' && runes[0] <= 'z' {
|
|
540
|
+
runes[0] = runes[0] - 'a' + 'A'
|
|
541
|
+
}
|
|
542
|
+
b.WriteString(string(runes))
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return b.String()
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
func itoa(n int) string { return strconv.Itoa(n) }
|
|
549
|
+
|
|
550
|
+
func unquote(literal string) string {
|
|
551
|
+
if len(literal) >= 2 {
|
|
552
|
+
if literal[0] == '`' || literal[0] == '"' {
|
|
553
|
+
return literal[1 : len(literal)-1]
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return literal
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// slug is the id form of a package name: price_list becomes price-list, the
|
|
561
|
+
// way every extractor spells an aggregate.
|
|
562
|
+
func slug(name string) string {
|
|
563
|
+
return strings.ReplaceAll(name, "_", "-")
|
|
564
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// The same fact read out of a repository written in Java, where JPA already
|
|
4
|
+
// writes it down: an entity says which table it is and each field says which
|
|
5
|
+
// column it is.
|
|
6
|
+
//
|
|
7
|
+
// @Entity
|
|
8
|
+
// @Table(name = "payments")
|
|
9
|
+
// class PaymentEntity {
|
|
10
|
+
// @Column(name = "order_id", nullable = false)
|
|
11
|
+
// private String orderId;
|
|
12
|
+
//
|
|
13
|
+
// So this is the one language where the mapping is not read off the statements
|
|
14
|
+
// that write the rows - there are none to read, Spring Data writes them - but
|
|
15
|
+
// off the annotations that decide what those statements will say. A field with
|
|
16
|
+
// no @Column is still mapped: JPA derives the column from the field name, and
|
|
17
|
+
// the derivation is the same snake_case every Spring Boot project defaults to.
|
|
18
|
+
//
|
|
19
|
+
// The domain object is the entity's own name without the suffix the row shape
|
|
20
|
+
// carries: PostingEntity holds Posting, and a column of the postings table maps
|
|
21
|
+
// to `Posting.account` rather than to the aggregate root it hangs off.
|
|
22
|
+
|
|
23
|
+
import (
|
|
24
|
+
"os"
|
|
25
|
+
"path"
|
|
26
|
+
"path/filepath"
|
|
27
|
+
"regexp"
|
|
28
|
+
"strings"
|
|
29
|
+
|
|
30
|
+
"github.com/shortlink-org/portolan/plugin"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
var (
|
|
34
|
+
javaEntity = regexp.MustCompile(`(?s)@Entity\b.*?@Table\s*\(\s*name\s*=\s*"([^"]+)"[^)]*\).*?(?:class|record)\s+(\w+)`)
|
|
35
|
+
javaColumn = regexp.MustCompile(`@Column\s*\(([^)]*)\)\s*(?:@[\w.]+\s*(?:\([^)]*\)\s*)?)*(?:private|protected|public)?\s*(?:final\s+)?[\w.<>\[\], ]+\s+(\w+)\s*[;=]`)
|
|
36
|
+
javaField = regexp.MustCompile(`(?m)^\s*(?:private|protected|public)\s+(?:final\s+)?[\w.<>\[\], ]+\s+(\w+)\s*;`)
|
|
37
|
+
javaName = regexp.MustCompile(`name\s*=\s*"([^"]+)"`)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
func readMapsJava(root, repositoryDir, aggregate string, b *plugin.Builder) map[string]map[string]string {
|
|
41
|
+
out := map[string]map[string]string{}
|
|
42
|
+
dir := mapSourceDir(root, repositoryDir, aggregate)
|
|
43
|
+
|
|
44
|
+
entries, err := os.ReadDir(filepath.Join(root, filepath.FromSlash(dir)))
|
|
45
|
+
if err != nil {
|
|
46
|
+
return out
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for _, entry := range entries {
|
|
50
|
+
name := entry.Name()
|
|
51
|
+
if entry.IsDir() || !strings.HasSuffix(name, ".java") {
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
source, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(dir), name))
|
|
56
|
+
if err != nil {
|
|
57
|
+
b.Warn(dir, path.Join(dir, name)+" could not be read: "+err.Error())
|
|
58
|
+
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
text := string(source)
|
|
62
|
+
|
|
63
|
+
found := javaEntity.FindStringSubmatch(text)
|
|
64
|
+
if found == nil {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
table, block := found[1], strings.TrimSuffix(found[2], "Entity")
|
|
68
|
+
|
|
69
|
+
columns := map[string]string{}
|
|
70
|
+
// The annotated ones first: what the annotation says wins over what the
|
|
71
|
+
// name would have been derived to.
|
|
72
|
+
annotated := map[string]bool{}
|
|
73
|
+
for _, match := range javaColumn.FindAllStringSubmatch(text, -1) {
|
|
74
|
+
field := match[2]
|
|
75
|
+
column := field
|
|
76
|
+
if named := javaName.FindStringSubmatch(match[1]); named != nil {
|
|
77
|
+
column = named[1]
|
|
78
|
+
} else {
|
|
79
|
+
column = snake(field)
|
|
80
|
+
}
|
|
81
|
+
columns[column] = block + "." + field
|
|
82
|
+
annotated[field] = true
|
|
83
|
+
}
|
|
84
|
+
for _, match := range javaField.FindAllStringSubmatch(text, -1) {
|
|
85
|
+
field := match[1]
|
|
86
|
+
if annotated[field] {
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
columns[snake(field)] = block + "." + field
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if len(columns) > 0 {
|
|
93
|
+
out[table] = columns
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return out
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// snake is the column name JPA derives from a field when nothing says
|
|
101
|
+
// otherwise: orderId becomes order_id.
|
|
102
|
+
func snake(field string) string {
|
|
103
|
+
var b strings.Builder
|
|
104
|
+
for i, r := range field {
|
|
105
|
+
if r >= 'A' && r <= 'Z' {
|
|
106
|
+
if i > 0 {
|
|
107
|
+
b.WriteByte('_')
|
|
108
|
+
}
|
|
109
|
+
b.WriteRune(r - 'A' + 'a')
|
|
110
|
+
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
b.WriteRune(r)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return b.String()
|
|
117
|
+
}
|