clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / tools / depcheck / laneimports.go
laneimports.go 5.5K
1package main23import (4 "golang.org/x/tools/go/analysis"5 "golang.org/x/tools/go/analysis/passes/inspect"6)78// laneImports encodes the dependency rules of issue #5 §3.2 as import edges.9// This analyzer is what depcheck *is*: lane isolation is a property of who10// imports whom, not of a directory taxonomy.11var laneImports = &analysis.Analyzer{12 Name: "laneimports",13 Doc: `check the lane import matrix of issue #5 §3.21415format/ depends on the standard library only. generate/ and enrich/ depend on16format/ and never on each other, app/, workbench/, render/, or analysis/.17app/ depends on format/ and its own packages. workbench/ depends on format/18plus enrich/maturity. Nothing imports render/. Every lane may import19internal/logging, and internal/logging imports no lane back.`,20 Requires: []*analysis.Analyzer{inspect.Analyzer},21 Run: runImportRule(laneImportEdge),22}2324// laneImportEdge is the whole rule as a pure function of two package paths, so25// the matrix can be table-tested without a compiler. It returns the empty26// string when the edge is permitted.27func laneImportEdge(from Lane, fromRel, importPath string) string {28 if from == LaneOutside {29 return "" // the archived pre-rewrite tree is not the subject30 }3132 toRel, local := rel(importPath)33 if !local {34 if from == LaneFormat && !isStdlib(importPath) {35 return contractf(36 "format must depend on the Go standard library only, but imports "+quote(importPath),37 "3.2",38 "format/ is exported at the module root on purpose: a third party who knows nothing of Atlas the app must be able to read a bundle with no dependency but Go itself",39 )40 }41 return ""42 }4344 // The two TypeScript lanes are unimportable from Go by construction; say so45 // in the terms the contract uses rather than letting them fall through to a46 // generic message.47 switch {48 case under(toRel, "render"):49 return contractf(50 "nothing imports render, but this package imports "+quote(importPath),51 "3.2",52 "the seam is deletable: it stands on the /data plane, the scene description and the analysis API, and nothing stands on it",53 )54 case under(toRel, "analysis"):55 return contractf(56 "analysis/ is a TypeScript lane and is not importable from Go, but this package imports "+quote(importPath),57 "3.1",58 "the analysis lane ships as a TypeScript package that Go tooling ignores; its contract crosses into Go only through published artifacts",59 )60 }6162 to := laneOf(toRel)63 if to == LaneOutside {64 return "" // the clean-room analyzer owns imports of the old tree65 }66 if from == LaneFormat && to != LaneFormat {67 return contractf(68 "format must depend on the Go standard library only, but imports "+quote(importPath),69 "3.2",70 "format/ is the center: everything may depend on it and it may depend on nothing of ours",71 )72 }73 if to == from {74 return ""75 }76 if to == LaneFormat {77 return "" // every lane may depend on the center78 }79 if to == LaneLogging {80 // One leveled event stream narrates the whole system (§9), so every81 // lane may speak it. format/ never reaches this line: its82 // stdlib-only rule above has already answered.83 return ""84 }8586 switch from {87 case LaneGenerate, LaneEnrich:88 other := LaneEnrich89 if from == LaneEnrich {90 other = LaneGenerate91 }92 if to == other {93 return contractf(94 string(from)+" must not import "+string(other)+", but imports "+quote(importPath),95 "3.2",96 "the pipeline lanes are isolated — the composed multi-source result is generate ⊕ enrich, joined by the pipeline and checked at the bundle level by the pipeline suite, never by an import edge",97 )98 }99 return contractf(100 string(from)+" must not import "+string(to)+", but imports "+quote(importPath),101 "3.2",102 "each pipeline lane is a library plus CLI subcommands of one atlas binary; the application and the harness depend on the lanes, never the reverse",103 )104105 case LaneApp:106 return contractf(107 "app must not import "+string(to)+", but imports "+quote(importPath),108 "3.2",109 "the application depends on format/ and its own packages; pipeline work is reached through the lane CLIs, and the harness is the oracle around the code, not a dependency of it",110 )111112 case LaneWorkbench:113 if to == LaneEnrich {114 if under(toRel, "internal/enrich/maturity") {115 return ""116 }117 return contractf(118 "workbench may import only enrich/maturity from the enrich lane, but imports "+quote(importPath),119 "3.2",120 "the workbench reads scores and shells out to the lane CLIs for everything else, which is what keeps pipeline operations subprocesses it can stream rather than code it links",121 )122 }123 return contractf(124 "workbench must not import "+string(to)+", but imports "+quote(importPath),125 "3.2",126 "the workbench depends on format/ plus enrich/maturity and shells out to the lane CLIs",127 )128129 case LaneLogging:130 return contractf(131 "logging must not import "+string(to)+", but imports "+quote(importPath),132 "9",133 "the event stream is the one thing every lane may depend on, which it can only stay if it depends on nothing of ours in return",134 )135136 case LaneCLI:137 return "" // the one binary is allowed to wire every lane's subcommands138139 case LaneShell:140 // The desktop shell is host wiring and nothing else: it mounts the141 // app over an OS host and hands it to a window. The rules that142 // matter to it are the ones above this switch — nothing imports143 // render, the two TypeScript lanes are unimportable — and they have144 // already had their say.145 return ""146147 case LaneTests:148 return "" // a test tree may read anything it judges149 }150 return ""151}