@shortlink-org/portolan 0.1.0 → 0.2.1

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.
Files changed (381) hide show
  1. package/README.md +69 -205
  2. package/cli/init.mjs +285 -0
  3. package/cli/init.test.mjs +158 -0
  4. package/cli/portolan.mjs +37 -73
  5. package/index.html +19 -0
  6. package/package.json +5 -10
  7. package/plugins/README.md +66 -27
  8. package/plugins/portolan-go.wasm +0 -0
  9. package/portolan.json +52 -73
  10. package/public/landing/cats/bug-hunter.webp +0 -0
  11. package/public/landing/cats/compass-nap.webp +0 -0
  12. package/public/landing/cats/diagrammer.webp +0 -0
  13. package/public/landing/cats/docs-reader.webp +0 -0
  14. package/public/landing/cats/laptop.webp +0 -0
  15. package/public/landing/cats/map-inspector.webp +0 -0
  16. package/public/landing/cats/server-break.webp +0 -0
  17. package/public/landing/cats/star-mapper.webp +0 -0
  18. package/public/landing/cats/system-builder.webp +0 -0
  19. package/public/landing/cats/thread-tangle.webp +0 -0
  20. package/public/og.png +0 -0
  21. package/public/readme-header.webp +0 -0
  22. package/schema/portolan.schema.json +180 -0
  23. package/scripts/builtin-plugins.mjs +14 -0
  24. package/scripts/delivery-presets.mjs +353 -0
  25. package/scripts/gen.mjs +37 -4
  26. package/scripts/history.mjs +126 -0
  27. package/scripts/history.test.mjs +110 -0
  28. package/scripts/host-plugins/fetch-bsr.mjs +351 -0
  29. package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
  30. package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
  31. package/scripts/host-plugins/fetch-csr.mjs +386 -0
  32. package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
  33. package/scripts/host-plugins/fetch-git.mjs +379 -0
  34. package/scripts/host-plugins/fetch-git.test.mjs +210 -0
  35. package/scripts/local-api.mjs +27 -10
  36. package/scripts/local-api.test.mjs +85 -2
  37. package/scripts/manifest.mjs +3 -2
  38. package/scripts/package-smoke.mjs +24 -3
  39. package/scripts/plugin-host.mjs +77 -18
  40. package/scripts/plugin-host.test.mjs +50 -1
  41. package/scripts/plugin-wasm-worker.mjs +6 -3
  42. package/scripts/run-builtin.mjs +6 -26
  43. package/scripts/schema.mjs +6 -1
  44. package/src/app/App.tsx +24 -429
  45. package/src/app/Breadcrumbs.test.ts +7 -0
  46. package/src/app/Breadcrumbs.tsx +15 -2
  47. package/src/app/CatalogApp.tsx +436 -0
  48. package/src/app/Sidebar.tsx +0 -1
  49. package/src/chat/ChatPanel.tsx +98 -41
  50. package/src/chat/Composer.tsx +4 -2
  51. package/src/chat/Conversation.tsx +79 -15
  52. package/src/chat/Header.tsx +43 -9
  53. package/src/chat/Starter.tsx +64 -3
  54. package/src/chat/Steps.tsx +1 -1
  55. package/src/chat/flags.test.ts +51 -14
  56. package/src/chat/flags.ts +8 -2
  57. package/src/chat/page-context.test.ts +51 -0
  58. package/src/chat/page-context.ts +128 -0
  59. package/src/chat/prompt.test.ts +49 -1
  60. package/src/chat/prompt.ts +42 -1
  61. package/src/chat/tools.ts +7 -2
  62. package/src/chat/transport.ts +16 -4
  63. package/src/components/ProblemRow.tsx +182 -0
  64. package/src/data.ts +20 -5
  65. package/src/er/ErCanvas.tsx +1 -1
  66. package/src/graph/DependencyGraph.tsx +10 -1
  67. package/src/graph/FocusedEventGraph.tsx +1 -1
  68. package/src/graph/GraphToolbar.tsx +2 -4
  69. package/src/index.css +220 -2
  70. package/src/landing/DraggableReveal.tsx +168 -0
  71. package/src/landing/EstateGraph.tsx +28 -0
  72. package/src/landing/FlowPlayback.tsx +282 -0
  73. package/src/landing/HeroMap.tsx +128 -0
  74. package/src/landing/LandingPage.tsx +757 -0
  75. package/src/landing/ProductFrame.tsx +68 -0
  76. package/src/landing/ProductTour.tsx +371 -0
  77. package/src/landing/catalog.ts +25 -0
  78. package/src/landing/motion.tsx +52 -0
  79. package/src/lib/all-problems.ts +28 -0
  80. package/src/lib/local-api.ts +35 -1
  81. package/src/lib/setup-info.test.ts +8 -0
  82. package/src/lib/setup-info.ts +3 -2
  83. package/src/lib/source-code.ts +2 -1
  84. package/src/likec4/C4View.tsx +5 -0
  85. package/src/likec4/InteractiveView.tsx +5 -1
  86. package/src/map/ContextMapGraph.tsx +47 -20
  87. package/src/pages/ContextMap.tsx +5 -1
  88. package/src/pages/FlowDetail.tsx +24 -7
  89. package/src/pages/Overview.tsx +68 -12
  90. package/src/pages/Problems.tsx +6 -193
  91. package/src/pages/Settings.tsx +133 -108
  92. package/src/pages/settings/DeliverySettings.tsx +229 -0
  93. package/src/pages/settings/PreferencesSettings.tsx +101 -0
  94. package/src/routes.test.ts +20 -0
  95. package/src/routes.ts +13 -1
  96. package/vite.config.ts +17 -9
  97. package/catalog/enum_test.go +0 -46
  98. package/catalog/model.go +0 -932
  99. package/catalog/roundtrip_test.go +0 -185
  100. package/catalog/via_test.go +0 -38
  101. package/go.mod +0 -14
  102. package/go.sum +0 -14
  103. package/internal/commands/cargo.go +0 -154
  104. package/internal/commands/commands.go +0 -143
  105. package/internal/commands/commands_test.go +0 -323
  106. package/internal/commands/gradle.go +0 -77
  107. package/internal/commands/justfile.go +0 -82
  108. package/internal/commands/makefile.go +0 -106
  109. package/internal/commands/maven.go +0 -93
  110. package/internal/commands/packagejson.go +0 -159
  111. package/internal/commands/pyproject.go +0 -169
  112. package/internal/commands/taskfile.go +0 -101
  113. package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
  114. package/internal/commands/testdata/estate/Makefile +0 -33
  115. package/internal/commands/testdata/estate/Taskfile.yml +0 -21
  116. package/internal/commands/testdata/estate/build.gradle.kts +0 -21
  117. package/internal/commands/testdata/estate/justfile +0 -20
  118. package/internal/commands/testdata/estate/package.json +0 -13
  119. package/internal/commands/testdata/estate/pom.xml +0 -29
  120. package/internal/commands/testdata/estate/pyproject.toml +0 -27
  121. package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
  122. package/internal/commands/testdata/golden/commands.json +0 -305
  123. package/internal/gohttp/analyze.go +0 -2500
  124. package/internal/gohttp/endpoints.go +0 -1067
  125. package/internal/gohttp/roots.go +0 -320
  126. package/internal/gohttp/typed.go +0 -143
  127. package/internal/goscan/constants.go +0 -85
  128. package/internal/goscan/goscan_test.go +0 -227
  129. package/internal/goscan/names.go +0 -52
  130. package/internal/goscan/parse_test.go +0 -11
  131. package/internal/goscan/source.go +0 -36
  132. package/internal/goscan/tree.go +0 -155
  133. package/internal/goscan/types.go +0 -99
  134. package/internal/wsdl/ids.go +0 -127
  135. package/internal/wsdl/ids_test.go +0 -21
  136. package/internal/wsdl/model.go +0 -62
  137. package/internal/wsdl/parse.go +0 -920
  138. package/internal/wsdl/parse_test.go +0 -133
  139. package/plugin/describe.go +0 -107
  140. package/plugin/describe_test.go +0 -114
  141. package/plugin/protocol.go +0 -117
  142. package/plugin/schematest/schematest.go +0 -126
  143. package/plugins/extract-adr/describe.go +0 -19
  144. package/plugins/extract-adr/describe_test.go +0 -11
  145. package/plugins/extract-adr/extract.go +0 -153
  146. package/plugins/extract-adr/extract_test.go +0 -350
  147. package/plugins/extract-adr/history.go +0 -99
  148. package/plugins/extract-adr/main.go +0 -65
  149. package/plugins/extract-adr/parse.go +0 -642
  150. package/plugins/extract-adr/parse_test.go +0 -405
  151. package/plugins/extract-asyncapi/describe.go +0 -19
  152. package/plugins/extract-asyncapi/describe_test.go +0 -11
  153. package/plugins/extract-asyncapi/extract.go +0 -315
  154. package/plugins/extract-asyncapi/extract_test.go +0 -197
  155. package/plugins/extract-asyncapi/main.go +0 -48
  156. package/plugins/extract-asyncapi/spec.go +0 -151
  157. package/plugins/extract-commands/describe.go +0 -19
  158. package/plugins/extract-commands/describe_test.go +0 -11
  159. package/plugins/extract-commands/extract.go +0 -79
  160. package/plugins/extract-commands/extract_test.go +0 -92
  161. package/plugins/extract-commands/main.go +0 -55
  162. package/plugins/extract-csr/avro.go +0 -258
  163. package/plugins/extract-csr/describe.go +0 -19
  164. package/plugins/extract-csr/describe_test.go +0 -11
  165. package/plugins/extract-csr/extract.go +0 -338
  166. package/plugins/extract-csr/extract_test.go +0 -374
  167. package/plugins/extract-csr/jsonschema.go +0 -343
  168. package/plugins/extract-csr/lock.go +0 -25
  169. package/plugins/extract-csr/main.go +0 -84
  170. package/plugins/extract-csr/subject.go +0 -102
  171. package/plugins/extract-flows/describe.go +0 -19
  172. package/plugins/extract-flows/describe_test.go +0 -11
  173. package/plugins/extract-flows/extract.go +0 -91
  174. package/plugins/extract-flows/main.go +0 -45
  175. package/plugins/extract-flows/parse.go +0 -593
  176. package/plugins/extract-flows/parse_test.go +0 -204
  177. package/plugins/extract-glossary/describe.go +0 -19
  178. package/plugins/extract-glossary/describe_test.go +0 -11
  179. package/plugins/extract-glossary/extract.go +0 -115
  180. package/plugins/extract-glossary/extract_test.go +0 -220
  181. package/plugins/extract-glossary/main.go +0 -59
  182. package/plugins/extract-glossary/parse.go +0 -214
  183. package/plugins/extract-glossary/parse_test.go +0 -203
  184. package/plugins/extract-go/aggregate.go +0 -214
  185. package/plugins/extract-go/client.go +0 -409
  186. package/plugins/extract-go/client_test.go +0 -266
  187. package/plugins/extract-go/describe.go +0 -19
  188. package/plugins/extract-go/describe_test.go +0 -11
  189. package/plugins/extract-go/enum.go +0 -195
  190. package/plugins/extract-go/enum_test.go +0 -82
  191. package/plugins/extract-go/event.go +0 -99
  192. package/plugins/extract-go/extract.go +0 -191
  193. package/plugins/extract-go/extract_test.go +0 -261
  194. package/plugins/extract-go/flow.go +0 -1441
  195. package/plugins/extract-go/flow_test.go +0 -609
  196. package/plugins/extract-go/httpclient.go +0 -174
  197. package/plugins/extract-go/httpclient_test.go +0 -296
  198. package/plugins/extract-go/ids.go +0 -92
  199. package/plugins/extract-go/layout.go +0 -225
  200. package/plugins/extract-go/layout_test.go +0 -125
  201. package/plugins/extract-go/lifecycle.go +0 -324
  202. package/plugins/extract-go/lifecycle_test.go +0 -108
  203. package/plugins/extract-go/main.go +0 -79
  204. package/plugins/extract-go/operation.go +0 -157
  205. package/plugins/extract-go/source.go +0 -316
  206. package/plugins/extract-go/transport.go +0 -321
  207. package/plugins/extract-go/transport_test.go +0 -145
  208. package/plugins/extract-go/wiring.go +0 -434
  209. package/plugins/extract-go-nats/describe.go +0 -19
  210. package/plugins/extract-go-nats/describe_test.go +0 -11
  211. package/plugins/extract-go-nats/extract.go +0 -177
  212. package/plugins/extract-go-nats/extract_test.go +0 -389
  213. package/plugins/extract-go-nats/index.go +0 -379
  214. package/plugins/extract-go-nats/main.go +0 -42
  215. package/plugins/extract-go-nats/resolve.go +0 -161
  216. package/plugins/extract-go-nats/sites.go +0 -224
  217. package/plugins/extract-graphql/describe.go +0 -19
  218. package/plugins/extract-graphql/describe_test.go +0 -11
  219. package/plugins/extract-graphql/extract.go +0 -433
  220. package/plugins/extract-graphql/extract_test.go +0 -256
  221. package/plugins/extract-graphql/ids.go +0 -49
  222. package/plugins/extract-graphql/lex.go +0 -237
  223. package/plugins/extract-graphql/main.go +0 -51
  224. package/plugins/extract-graphql/parse.go +0 -621
  225. package/plugins/extract-graphql/parse_test.go +0 -122
  226. package/plugins/extract-http-clients/describe.go +0 -19
  227. package/plugins/extract-http-clients/describe_test.go +0 -11
  228. package/plugins/extract-http-clients/extract.go +0 -705
  229. package/plugins/extract-http-clients/extract_test.go +0 -1263
  230. package/plugins/extract-http-clients/main.go +0 -42
  231. package/plugins/extract-openapi/describe.go +0 -19
  232. package/plugins/extract-openapi/describe_test.go +0 -11
  233. package/plugins/extract-openapi/discover.go +0 -243
  234. package/plugins/extract-openapi/extract.go +0 -526
  235. package/plugins/extract-openapi/extract_test.go +0 -545
  236. package/plugins/extract-openapi/main.go +0 -75
  237. package/plugins/extract-openapi/spec.go +0 -350
  238. package/plugins/extract-project/describe.go +0 -19
  239. package/plugins/extract-project/describe_test.go +0 -11
  240. package/plugins/extract-project/extract.go +0 -221
  241. package/plugins/extract-project/extract_test.go +0 -109
  242. package/plugins/extract-project/main.go +0 -41
  243. package/plugins/extract-proto/ast.go +0 -125
  244. package/plugins/extract-proto/consumes.go +0 -77
  245. package/plugins/extract-proto/describe.go +0 -19
  246. package/plugins/extract-proto/extract.go +0 -293
  247. package/plugins/extract-proto/extract_test.go +0 -459
  248. package/plugins/extract-proto/ids.go +0 -89
  249. package/plugins/extract-proto/ids_test.go +0 -57
  250. package/plugins/extract-proto/lex.go +0 -285
  251. package/plugins/extract-proto/main.go +0 -100
  252. package/plugins/extract-proto/module.go +0 -120
  253. package/plugins/extract-proto/parse.go +0 -720
  254. package/plugins/extract-proto/parse_test.go +0 -307
  255. package/plugins/extract-proto/provides.go +0 -236
  256. package/plugins/extract-proto/resolve.go +0 -222
  257. package/plugins/extract-proto/resolve_test.go +0 -119
  258. package/plugins/extract-redis/describe.go +0 -19
  259. package/plugins/extract-redis/describe_test.go +0 -11
  260. package/plugins/extract-redis/extract.go +0 -183
  261. package/plugins/extract-redis/extract_test.go +0 -168
  262. package/plugins/extract-redis/keyspaces.go +0 -469
  263. package/plugins/extract-redis/main.go +0 -44
  264. package/plugins/extract-river/describe.go +0 -19
  265. package/plugins/extract-river/describe_test.go +0 -11
  266. package/plugins/extract-river/extract.go +0 -507
  267. package/plugins/extract-river/extract_test.go +0 -132
  268. package/plugins/extract-river/main.go +0 -42
  269. package/plugins/extract-sql/ddl.go +0 -893
  270. package/plugins/extract-sql/ddl_test.go +0 -401
  271. package/plugins/extract-sql/describe.go +0 -19
  272. package/plugins/extract-sql/describe_test.go +0 -11
  273. package/plugins/extract-sql/layout.go +0 -221
  274. package/plugins/extract-sql/layout_test.go +0 -55
  275. package/plugins/extract-sql/lineage.go +0 -107
  276. package/plugins/extract-sql/main.go +0 -141
  277. package/plugins/extract-sql/maps.go +0 -564
  278. package/plugins/extract-sql/maps_java.go +0 -117
  279. package/plugins/extract-sql/maps_rust.go +0 -333
  280. package/plugins/extract-sql/maps_rust_test.go +0 -70
  281. package/plugins/extract-sql/maps_test.go +0 -204
  282. package/plugins/extract-sql/maps_ts.go +0 -398
  283. package/plugins/extract-sql/maps_ts_test.go +0 -136
  284. package/plugins/extract-sql/projection.go +0 -70
  285. package/plugins/extract-sql/projection_test.go +0 -50
  286. package/plugins/extract-sql/store.go +0 -420
  287. package/plugins/extract-sql/store_test.go +0 -233
  288. package/plugins/extract-sql/view.go +0 -295
  289. package/plugins/extract-watermill/describe.go +0 -19
  290. package/plugins/extract-watermill/describe_test.go +0 -11
  291. package/plugins/extract-watermill/extract.go +0 -1228
  292. package/plugins/extract-watermill/extract_test.go +0 -234
  293. package/plugins/extract-watermill/main.go +0 -41
  294. package/plugins/extract-wsdl/describe.go +0 -19
  295. package/plugins/extract-wsdl/describe_test.go +0 -11
  296. package/plugins/extract-wsdl/extract.go +0 -148
  297. package/plugins/extract-wsdl/extract_test.go +0 -45
  298. package/plugins/extract-wsdl/main.go +0 -53
  299. package/plugins/fetch-bsr/auth.go +0 -114
  300. package/plugins/fetch-bsr/bsr.go +0 -240
  301. package/plugins/fetch-bsr/cache.go +0 -66
  302. package/plugins/fetch-bsr/describe.go +0 -19
  303. package/plugins/fetch-bsr/fetch.go +0 -192
  304. package/plugins/fetch-bsr/fetch_test.go +0 -465
  305. package/plugins/fetch-bsr/lock.go +0 -71
  306. package/plugins/fetch-bsr/main.go +0 -117
  307. package/plugins/fetch-csr/auth.go +0 -95
  308. package/plugins/fetch-csr/cache.go +0 -69
  309. package/plugins/fetch-csr/describe.go +0 -19
  310. package/plugins/fetch-csr/describe_test.go +0 -11
  311. package/plugins/fetch-csr/fetch.go +0 -254
  312. package/plugins/fetch-csr/fetch_test.go +0 -494
  313. package/plugins/fetch-csr/lock.go +0 -95
  314. package/plugins/fetch-csr/main.go +0 -122
  315. package/plugins/fetch-csr/registry.go +0 -209
  316. package/plugins/fetch-git/cache.go +0 -65
  317. package/plugins/fetch-git/describe.go +0 -19
  318. package/plugins/fetch-git/describe_test.go +0 -11
  319. package/plugins/fetch-git/fetch.go +0 -181
  320. package/plugins/fetch-git/fetch_test.go +0 -332
  321. package/plugins/fetch-git/git.go +0 -169
  322. package/plugins/fetch-git/lock.go +0 -72
  323. package/plugins/fetch-git/main.go +0 -127
  324. package/plugins/fetch-git/offline.go +0 -39
  325. package/plugins/fetch-git/pin.go +0 -90
  326. package/plugins/fetch-git/pin_test.go +0 -74
  327. package/plugins/gen-backstage/describe.go +0 -17
  328. package/plugins/gen-backstage/main.go +0 -22
  329. package/plugins/gen-backstage/plugin.go +0 -473
  330. package/plugins/gen-backstage/plugin_test.go +0 -145
  331. package/plugins/gen-backstage.wasm +0 -0
  332. package/plugins/gen-markdown/adr.go +0 -162
  333. package/plugins/gen-markdown/aggregate.go +0 -362
  334. package/plugins/gen-markdown/canonical.go +0 -93
  335. package/plugins/gen-markdown/context.go +0 -90
  336. package/plugins/gen-markdown/coverage_test.go +0 -89
  337. package/plugins/gen-markdown/describe.go +0 -19
  338. package/plugins/gen-markdown/describe_test.go +0 -11
  339. package/plugins/gen-markdown/external.go +0 -71
  340. package/plugins/gen-markdown/flow.go +0 -277
  341. package/plugins/gen-markdown/glossary.go +0 -76
  342. package/plugins/gen-markdown/glossary_test.go +0 -148
  343. package/plugins/gen-markdown/llms.go +0 -302
  344. package/plugins/gen-markdown/main.go +0 -22
  345. package/plugins/gen-markdown/markdown.go +0 -254
  346. package/plugins/gen-markdown/markdown_test.go +0 -100
  347. package/plugins/gen-markdown/module.go +0 -107
  348. package/plugins/gen-markdown/plugin.go +0 -39
  349. package/plugins/gen-markdown/quality_test.go +0 -176
  350. package/plugins/gen-markdown/redis_test.go +0 -38
  351. package/plugins/gen-markdown/render.go +0 -344
  352. package/plugins/gen-markdown/render_test.go +0 -186
  353. package/plugins/gen-markdown/service.go +0 -456
  354. package/plugins/gen-markdown/source.go +0 -177
  355. package/plugins/gen-markdown/store.go +0 -201
  356. package/plugins/gen-markdown.wasm +0 -0
  357. package/plugins/gen-mermaid/describe.go +0 -17
  358. package/plugins/gen-mermaid/main.go +0 -22
  359. package/plugins/gen-mermaid/plugin.go +0 -104
  360. package/plugins/gen-mermaid/plugin_test.go +0 -33
  361. package/plugins/gen-mermaid.wasm +0 -0
  362. package/plugins/openapi/ids.go +0 -261
  363. package/plugins/openapi/ids_test.go +0 -98
  364. package/plugins/verify-codeowners/describe.go +0 -19
  365. package/plugins/verify-codeowners/describe_test.go +0 -11
  366. package/plugins/verify-codeowners/main.go +0 -75
  367. package/plugins/verify-codeowners/match.go +0 -85
  368. package/plugins/verify-codeowners/match_test.go +0 -47
  369. package/plugins/verify-codeowners/owners.go +0 -164
  370. package/plugins/verify-codeowners/owners_test.go +0 -225
  371. package/plugins/verify-codeowners/parse.go +0 -90
  372. package/plugins/verify-codeowners/parse_test.go +0 -62
  373. package/plugins/verify-otel/describe.go +0 -19
  374. package/plugins/verify-otel/describe_test.go +0 -11
  375. package/plugins/verify-otel/main.go +0 -53
  376. package/plugins/verify-otel/match.go +0 -336
  377. package/plugins/verify-otel/otlp.go +0 -200
  378. package/plugins/verify-otel/verify.go +0 -734
  379. package/plugins/verify-otel/verify_test.go +0 -460
  380. /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
  381. /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
@@ -1,720 +0,0 @@
1
- package main
2
-
3
- // A tolerant recursive-descent parser for .proto.
4
- //
5
- // TOLERANT is the whole design, not a shortcut. docs/adr/org.0001.md has
6
- // consumers keeping NARROWED vendored copies - "a consumer may narrow a message
7
- // to the fields it actually uses" - and a narrowed copy routinely imports a file
8
- // that was not vendored beside it. A compiler refuses to produce anything for
9
- // that input; the point of reading vendored copies is to describe files that do
10
- // not build standalone. So an unrecognised construct is skipped to the end of
11
- // its block and NAMED in a Note, never dropped and never fatal.
12
- //
13
- // The two things that are fatal are a file that cannot be tokenised and a file
14
- // that ends mid-declaration, because past either of those the parser is not
15
- // reading proto any more, it is guessing.
16
-
17
- import (
18
- "fmt"
19
- "strconv"
20
- "strings"
21
- )
22
-
23
- type parser struct {
24
- toks []token
25
- at int
26
- path string
27
- notes []Note
28
- }
29
-
30
- // Parse reads one .proto. The returned notes name every construct it declined
31
- // to model; an error means the file could not be read at all.
32
- func Parse(path, src string) (*File, []Note, error) {
33
- out, err := lex(src)
34
- if err != nil {
35
- return nil, nil, fmt.Errorf("%s: %w", path, err)
36
- }
37
-
38
- p := &parser{toks: out.tokens, path: path}
39
- file := &File{Path: path, Doc: p.peek().doc}
40
-
41
- for !p.done() {
42
- if p.peek().is(";") { // a stray semicolon between declarations is legal
43
- p.take()
44
-
45
- continue
46
- }
47
-
48
- tok := p.peek()
49
- if tok.kind != tokIdent {
50
- p.note(tok.line, fmt.Sprintf("unexpected %q at the top level; skipped", tok.text))
51
- p.skipDeclaration()
52
-
53
- continue
54
- }
55
-
56
- switch tok.text {
57
- case "syntax", "edition":
58
- p.take()
59
- if !p.expect("=") {
60
- continue
61
- }
62
- if v := p.take(); v.kind == tokString {
63
- file.Syntax = v.text
64
- }
65
- p.expect(";")
66
- case "package":
67
- p.take()
68
- file.Package = p.qualified()
69
- p.expect(";")
70
- case "import":
71
- file.Imports = append(file.Imports, p.parseImport())
72
- case "option":
73
- if o, ok := p.parseOption(); ok {
74
- file.Options = append(file.Options, o)
75
- }
76
- case "message":
77
- if m := p.parseMessage(); m != nil {
78
- file.Messages = append(file.Messages, m)
79
- }
80
- case "enum":
81
- if e := p.parseEnum(); e != nil {
82
- file.Enums = append(file.Enums, e)
83
- }
84
- case "service":
85
- if s := p.parseService(); s != nil {
86
- file.Services = append(file.Services, s)
87
- }
88
- case "extend":
89
- // Modelled by nothing in the catalog: an extension adds fields to
90
- // someone else's message, and the catalog has no way to say that.
91
- p.note(tok.line, "extend is not read; the fields it adds are not listed")
92
- p.take()
93
- p.qualified()
94
- p.skipBlock()
95
- default:
96
- p.note(tok.line, fmt.Sprintf("%q is not a declaration this parser reads; skipped", tok.text))
97
- p.skipDeclaration()
98
- }
99
- }
100
-
101
- // Trailing comments are attached last, by line, so a field documented to
102
- // its right reads the same as one documented above.
103
- attachTrailing(file, out.trailing)
104
-
105
- return file, p.notes, nil
106
- }
107
-
108
- func (p *parser) parseImport() Import {
109
- tok := p.take() // "import"
110
- imp := Import{Line: tok.line}
111
- switch {
112
- case p.peek().is("public"):
113
- p.take()
114
- imp.Public = true
115
- case p.peek().is("weak"):
116
- p.take()
117
- imp.Weak = true
118
- }
119
- if v := p.peek(); v.kind == tokString {
120
- imp.Path = p.take().text
121
- }
122
- p.expect(";")
123
-
124
- return imp
125
- }
126
-
127
- func (p *parser) parseOption() (Option, bool) {
128
- tok := p.take() // "option"
129
- name := p.optionName()
130
- if !p.expect("=") {
131
- return Option{}, false
132
- }
133
- value := p.optionValue()
134
- p.expect(";")
135
-
136
- return Option{Name: name, Value: value, Line: tok.line}, true
137
- }
138
-
139
- // optionName reads a name that may be parenthesised and dotted:
140
- // `go_package`, `(google.api.http).post`.
141
- func (p *parser) optionName() string {
142
- var b strings.Builder
143
- for {
144
- switch {
145
- case p.peek().is("("):
146
- p.take()
147
- b.WriteString("(")
148
- b.WriteString(p.qualified())
149
- if p.peek().is(")") {
150
- p.take()
151
- }
152
- b.WriteString(")")
153
- case p.peek().kind == tokIdent:
154
- b.WriteString(p.take().text)
155
- case p.peek().is("."):
156
- p.take()
157
- b.WriteString(".")
158
- default:
159
- return b.String()
160
- }
161
- if !p.peek().is(".") {
162
- return b.String()
163
- }
164
- }
165
- }
166
-
167
- // optionValue reads a scalar value, or skips an aggregate `{ ... }` body.
168
- func (p *parser) optionValue() string {
169
- tok := p.peek()
170
- if tok.is("{") {
171
- // A custom option body is arbitrary and modelled by nothing here.
172
- p.note(tok.line, "an aggregate option value is not read")
173
- p.skipBlock()
174
-
175
- return ""
176
- }
177
- if tok.is("[") {
178
- p.skipBrackets()
179
-
180
- return ""
181
- }
182
-
183
- var b strings.Builder
184
- for !p.done() && !p.peek().is(";") && !p.peek().is(",") && !p.peek().is("]") {
185
- t := p.take()
186
- if t.kind == tokString {
187
- b.WriteString(t.text)
188
-
189
- continue
190
- }
191
- b.WriteString(t.text)
192
- }
193
-
194
- return b.String()
195
- }
196
-
197
- func (p *parser) parseMessage() *Message {
198
- tok := p.take() // "message"
199
- name := p.take()
200
- if name.kind != tokIdent {
201
- p.note(tok.line, "message has no name; skipped")
202
- p.skipDeclaration()
203
-
204
- return nil
205
- }
206
-
207
- msg := &Message{Name: name.text, Doc: tok.doc, Line: tok.line}
208
- if !p.expect("{") {
209
- return msg
210
- }
211
- p.messageBody(msg, "")
212
-
213
- return msg
214
- }
215
-
216
- // messageBody reads declarations up to the closing brace, which it consumes.
217
- // oneof is the name of the oneof enclosing these fields, empty at the top.
218
- func (p *parser) messageBody(msg *Message, oneof string) {
219
- for !p.done() && !p.peek().is("}") {
220
- tok := p.peek()
221
-
222
- if tok.is(";") {
223
- p.take()
224
-
225
- continue
226
- }
227
-
228
- if tok.kind != tokIdent {
229
- p.note(tok.line, fmt.Sprintf("unexpected %q in message %q; skipped", tok.text, msg.Name))
230
- p.skipDeclaration()
231
-
232
- continue
233
- }
234
-
235
- switch tok.text {
236
- case "message":
237
- if nested := p.parseMessage(); nested != nil {
238
- msg.Messages = append(msg.Messages, nested)
239
- }
240
- case "enum":
241
- if e := p.parseEnum(); e != nil {
242
- msg.Enums = append(msg.Enums, e)
243
- }
244
- case "option":
245
- p.parseOption()
246
- case "reserved":
247
- msg.Reserved = append(msg.Reserved, p.parseReserved()...)
248
- case "extensions":
249
- p.note(tok.line, "extensions ranges are not read")
250
- p.skipDeclaration()
251
- case "extend":
252
- p.note(tok.line, "extend is not read; the fields it adds are not listed")
253
- p.take()
254
- p.qualified()
255
- p.skipBlock()
256
- case "oneof":
257
- p.parseOneof(msg)
258
- case "group":
259
- p.note(tok.line, "a proto2 group is not read; its fields are not listed")
260
- p.skipDeclaration()
261
- default:
262
- if f := p.parseField(oneof); f != nil {
263
- msg.Fields = append(msg.Fields, f)
264
- }
265
- }
266
- }
267
- p.expect("}")
268
- }
269
-
270
- func (p *parser) parseOneof(msg *Message) {
271
- tok := p.take() // "oneof"
272
- name := p.take()
273
- if name.kind != tokIdent {
274
- p.note(tok.line, "oneof has no name; skipped")
275
- p.skipDeclaration()
276
-
277
- return
278
- }
279
- one := &Oneof{Name: name.text, Doc: tok.doc, Line: tok.line}
280
- if !p.expect("{") {
281
- return
282
- }
283
-
284
- // A oneof body is a message body with one field kind excluded, and reusing
285
- // the same reader is what keeps a oneof member and a plain field the same
286
- // shape - which is the whole reason `Message.Fields` holds both.
287
- before := len(msg.Fields)
288
- p.messageBody(msg, one.Name)
289
- one.Fields = append(one.Fields, msg.Fields[before:]...)
290
- msg.Oneofs = append(msg.Oneofs, one)
291
- }
292
-
293
- func (p *parser) parseField(oneof string) *Field {
294
- tok := p.peek()
295
- f := &Field{Doc: tok.doc, Line: tok.line, Oneof: oneof}
296
-
297
- switch tok.text {
298
- case "repeated", "optional", "required":
299
- f.Label = p.take().text
300
- }
301
-
302
- if p.peek().is("group") {
303
- p.note(p.peek().line, "a proto2 group is not read; its fields are not listed")
304
- p.skipDeclaration()
305
-
306
- return nil
307
- }
308
-
309
- f.Type = p.typeName()
310
- if f.Type == "" {
311
- p.note(tok.line, "a field with no type; skipped")
312
- p.skipDeclaration()
313
-
314
- return nil
315
- }
316
-
317
- name := p.take()
318
- if name.kind != tokIdent {
319
- p.note(tok.line, fmt.Sprintf("a %s field with no name; skipped", f.Type))
320
- p.skipDeclaration()
321
-
322
- return nil
323
- }
324
- f.Name = name.text
325
-
326
- if p.expect("=") {
327
- if n := p.take(); n.kind == tokNumber {
328
- f.Number, _ = strconv.Atoi(n.text)
329
- }
330
- }
331
-
332
- if p.peek().is("[") {
333
- p.fieldOptions(f)
334
- }
335
- p.expect(";")
336
-
337
- return f
338
- }
339
-
340
- // fieldOptions reads `[deprecated = true, default = "x"]`. Only the two the
341
- // catalog can say anything about are kept; the rest are read past so they
342
- // cannot desynchronise the parser.
343
- func (p *parser) fieldOptions(f *Field) {
344
- p.take() // "["
345
- for !p.done() && !p.peek().is("]") {
346
- name := p.optionName()
347
- if !p.expect("=") {
348
- break
349
- }
350
- value := p.optionValue()
351
- switch name {
352
- case "deprecated":
353
- f.Deprecated = value == "true"
354
- case "default":
355
- f.Default = value
356
- }
357
- if p.peek().is(",") {
358
- p.take()
359
- }
360
- }
361
- p.expect("]")
362
- }
363
-
364
- func (p *parser) parseReserved() []string {
365
- p.take() // "reserved"
366
- var out []string
367
- for !p.done() && !p.peek().is(";") {
368
- t := p.take()
369
- if t.kind == tokString || t.kind == tokNumber {
370
- out = append(out, t.text)
371
- }
372
- }
373
- p.expect(";")
374
-
375
- return out
376
- }
377
-
378
- func (p *parser) parseEnum() *Enum {
379
- tok := p.take() // "enum"
380
- name := p.take()
381
- if name.kind != tokIdent {
382
- p.note(tok.line, "enum has no name; skipped")
383
- p.skipDeclaration()
384
-
385
- return nil
386
- }
387
-
388
- e := &Enum{Name: name.text, Doc: tok.doc, Line: tok.line}
389
- if !p.expect("{") {
390
- return e
391
- }
392
-
393
- for !p.done() && !p.peek().is("}") {
394
- t := p.peek()
395
- if t.is(";") {
396
- p.take()
397
-
398
- continue
399
- }
400
- if t.is("option") {
401
- p.parseOption()
402
-
403
- continue
404
- }
405
- if t.is("reserved") {
406
- p.parseReserved()
407
-
408
- continue
409
- }
410
- if t.kind != tokIdent {
411
- p.note(t.line, fmt.Sprintf("unexpected %q in enum %q; skipped", t.text, e.Name))
412
- p.skipDeclaration()
413
-
414
- continue
415
- }
416
-
417
- value := &EnumValue{Name: p.take().text, Doc: t.doc, Line: t.line}
418
- if p.expect("=") {
419
- if n := p.take(); n.kind == tokNumber {
420
- value.Number, _ = strconv.Atoi(n.text)
421
- }
422
- }
423
- if p.peek().is("[") {
424
- p.skipBrackets()
425
- }
426
- p.expect(";")
427
- e.Values = append(e.Values, value)
428
- }
429
- p.expect("}")
430
-
431
- return e
432
- }
433
-
434
- func (p *parser) parseService() *Service {
435
- tok := p.take() // "service"
436
- name := p.take()
437
- if name.kind != tokIdent {
438
- p.note(tok.line, "service has no name; skipped")
439
- p.skipDeclaration()
440
-
441
- return nil
442
- }
443
-
444
- svc := &Service{Name: name.text, Doc: tok.doc, Line: tok.line}
445
- if !p.expect("{") {
446
- return svc
447
- }
448
-
449
- for !p.done() && !p.peek().is("}") {
450
- t := p.peek()
451
- switch {
452
- case t.is(";"):
453
- p.take()
454
- case t.is("option"):
455
- p.parseOption()
456
- case t.is("rpc"):
457
- if m := p.parseMethod(); m != nil {
458
- svc.Methods = append(svc.Methods, m)
459
- }
460
- default:
461
- p.note(t.line, fmt.Sprintf("unexpected %q in service %q; skipped", t.text, svc.Name))
462
- p.skipDeclaration()
463
- }
464
- }
465
- p.expect("}")
466
-
467
- return svc
468
- }
469
-
470
- func (p *parser) parseMethod() *Method {
471
- tok := p.take() // "rpc"
472
- name := p.take()
473
- if name.kind != tokIdent {
474
- p.note(tok.line, "rpc has no name; skipped")
475
- p.skipDeclaration()
476
-
477
- return nil
478
- }
479
-
480
- m := &Method{Name: name.text, Doc: tok.doc, Line: tok.line}
481
- m.ClientStreaming, m.Request = p.methodType()
482
- if !p.peek().is("returns") {
483
- p.note(tok.line, fmt.Sprintf("rpc %q has no returns clause; response unknown", m.Name))
484
- } else {
485
- p.take()
486
- }
487
- m.ServerStreaming, m.Response = p.methodType()
488
-
489
- // The body, when there is one, holds method options - `deprecated` being
490
- // the only one the catalog can carry.
491
- if p.peek().is("{") {
492
- p.take()
493
- for !p.done() && !p.peek().is("}") {
494
- if p.peek().is("option") {
495
- o, ok := p.parseOption()
496
- if ok && o.Name == "deprecated" && o.Value == "true" {
497
- m.Deprecated = true
498
- }
499
-
500
- continue
501
- }
502
- p.take()
503
- }
504
- p.expect("}")
505
- } else {
506
- p.expect(";")
507
- }
508
-
509
- return m
510
- }
511
-
512
- // methodType reads `(stream shop.v1.Req)` and says whether it streamed.
513
- func (p *parser) methodType() (bool, string) {
514
- if !p.expect("(") {
515
- return false, ""
516
- }
517
- stream := false
518
- if p.peek().is("stream") {
519
- p.take()
520
- stream = true
521
- }
522
- name := p.qualified()
523
- p.expect(")")
524
-
525
- return stream, name
526
- }
527
-
528
- // typeName reads a field's type as written, including a map's two arguments.
529
- func (p *parser) typeName() string {
530
- if p.peek().is("map") {
531
- p.take()
532
- if !p.peek().is("<") {
533
- return "map"
534
- }
535
- p.take()
536
- key := p.qualified()
537
- p.expect(",")
538
- value := p.typeName()
539
- p.expect(">")
540
-
541
- return "map<" + key + ", " + value + ">"
542
- }
543
-
544
- return p.qualified()
545
- }
546
-
547
- // qualified reads `shop.v1.Money`, or `.shop.v1.Money` with its leading dot
548
- // kept - proto's spelling for "this is absolute, do not search enclosing
549
- // scopes", which the resolver needs and a reader recognises.
550
- func (p *parser) qualified() string {
551
- var b strings.Builder
552
- if p.peek().is(".") {
553
- p.take()
554
- b.WriteString(".")
555
- }
556
- if p.peek().kind != tokIdent {
557
- return b.String()
558
- }
559
- b.WriteString(p.take().text)
560
- for p.peek().is(".") {
561
- p.take()
562
- b.WriteString(".")
563
- if p.peek().kind != tokIdent {
564
- break
565
- }
566
- b.WriteString(p.take().text)
567
- }
568
-
569
- return b.String()
570
- }
571
-
572
- // --- the tolerance machinery -----------------------------------------------
573
-
574
- func (p *parser) done() bool { return p.peek().kind == tokEOF }
575
-
576
- func (p *parser) peek() token {
577
- if p.at >= len(p.toks) {
578
- return token{kind: tokEOF}
579
- }
580
-
581
- return p.toks[p.at]
582
- }
583
-
584
- func (p *parser) take() token {
585
- t := p.peek()
586
- if p.at < len(p.toks) {
587
- p.at++
588
- }
589
-
590
- return t
591
- }
592
-
593
- func (p *parser) expect(text string) bool {
594
- if p.peek().is(text) {
595
- p.take()
596
-
597
- return true
598
- }
599
- p.note(p.peek().line, fmt.Sprintf("expected %q, found %q", text, p.peek().text))
600
-
601
- return false
602
- }
603
-
604
- // skipDeclaration reads past whatever is here: to the semicolon that ends it,
605
- // or past the balanced block it opens, whichever comes first.
606
- func (p *parser) skipDeclaration() {
607
- for !p.done() {
608
- switch {
609
- case p.peek().is(";"):
610
- p.take()
611
-
612
- return
613
- case p.peek().is("{"):
614
- p.skipBlock()
615
-
616
- return
617
- case p.peek().is("}"):
618
- return
619
- default:
620
- p.take()
621
- }
622
- }
623
- }
624
-
625
- func (p *parser) skipBlock() {
626
- if !p.peek().is("{") {
627
- return
628
- }
629
- depth := 0
630
- for !p.done() {
631
- switch {
632
- case p.peek().is("{"):
633
- depth++
634
- case p.peek().is("}"):
635
- depth--
636
- }
637
- p.take()
638
- if depth == 0 {
639
- return
640
- }
641
- }
642
- }
643
-
644
- func (p *parser) skipBrackets() {
645
- if !p.peek().is("[") {
646
- return
647
- }
648
- depth := 0
649
- for !p.done() {
650
- switch {
651
- case p.peek().is("["):
652
- depth++
653
- case p.peek().is("]"):
654
- depth--
655
- }
656
- p.take()
657
- if depth == 0 {
658
- return
659
- }
660
- }
661
- }
662
-
663
- func (p *parser) note(line int, message string) {
664
- p.notes = append(p.notes, Note{Line: line, Message: message})
665
- }
666
-
667
- // attachTrailing gives a declaration the comment sitting at the end of its
668
- // line, when nothing was written above it. Above wins: an author who wrote
669
- // both meant the block as the documentation and the trailing note as an aside.
670
- func attachTrailing(file *File, trailing map[int]string) {
671
- if len(trailing) == 0 {
672
- return
673
- }
674
-
675
- var doMessage func(m *Message)
676
- doMessage = func(m *Message) {
677
- if m.Doc == "" {
678
- m.Doc = trailing[m.Line]
679
- }
680
- for _, f := range m.Fields {
681
- if f.Doc == "" {
682
- f.Doc = trailing[f.Line]
683
- }
684
- }
685
- for _, e := range m.Enums {
686
- doEnum(e, trailing)
687
- }
688
- for _, nested := range m.Messages {
689
- doMessage(nested)
690
- }
691
- }
692
-
693
- for _, m := range file.Messages {
694
- doMessage(m)
695
- }
696
- for _, e := range file.Enums {
697
- doEnum(e, trailing)
698
- }
699
- for _, s := range file.Services {
700
- if s.Doc == "" {
701
- s.Doc = trailing[s.Line]
702
- }
703
- for _, m := range s.Methods {
704
- if m.Doc == "" {
705
- m.Doc = trailing[m.Line]
706
- }
707
- }
708
- }
709
- }
710
-
711
- func doEnum(e *Enum, trailing map[int]string) {
712
- if e.Doc == "" {
713
- e.Doc = trailing[e.Line]
714
- }
715
- for _, v := range e.Values {
716
- if v.Doc == "" {
717
- v.Doc = trailing[v.Line]
718
- }
719
- }
720
- }