Documentation
¶
Overview ¶
Package depsurface reads what a component KNOWS through its Deps (docs/design/composition-graph.md §2, "least knowledge"): for every Deps field, the type it declares and the part of that type the package actually uses through it. go/types resolves every `s.deps.X` selection to the field it names, however the Deps value was reached, so a method called through the field counts and a same-named method on anything else does not.
Three rules read it (internal/linter/forgeconv/least_knowledge.go):
- deps-surface: an interface field declares methods the package never needs through it — never calls, and never passes the value on as an interface that declares them;
- no-container-deps: a field holds a container — the composition root's types, the command tree's Deps, a locator, `any`. (The root config joins the list with K3, when the scaffold gives each component a config block of its own: until then the scaffold itself writes `Config *config.Config`, and a lint that flags forge's own output on every fresh service is noise);
- deps-no-downcast: a type assertion turns a field back into a concrete type, undoing the capability its interface granted.
and `forge fix narrow-deps` (Narrow) writes the interface the observed calls describe.
Index ¶
Constants ¶
const ( DepsFileName = "deps.go" DepsFileMarker = "// forge:deps — consumer-declared interfaces for this package's Deps." )
DepsFileName is where Narrow declares the interfaces of a package that has no contract.go; DepsFileMarker is its first line, which `forge generate` reads to keep the file's mocks (mock_gen.go) in step with it.
Variables ¶
This section is empty.
Functions ¶
func FindComponentDirs ¶
FindComponentDirs lists the project-relative directories under internal/ whose non-test files declare `type Deps struct`.
func HasDepsMarker ¶
HasDepsMarker reports whether dir holds a deps.go Narrow wrote.
Types ¶
type Bloated ¶
type Bloated struct {
Interface string
// Methods counts what interfacebloat counts: every method written out and
// every embedded interface.
Methods int
Limit int
LimitSource string
// Suppressed: narrow-deps wrote a reasoned //nolint:interfacebloat above
// the type, because the project's lint runs interfacebloat.
Suppressed bool
}
Bloated is a narrowed interface wider than the project's interfacebloat limit.
type EnvRead ¶
type EnvRead struct {
File string
Line int
// Call is the function read through ("os.Getenv").
Call string
}
EnvRead is one read of the process environment.
type Field ¶
type Field struct {
Name string
File string // project-relative
Line int
// Type is the field's type as the consumer would write it.
Type string
// Interface is true for an interface-typed field; Methods are its
// exported methods, sorted.
Interface bool
Methods []string
// Foreign is true for a named interface declared in another package
// (the consumer did not declare what it needs).
Foreign bool
// Used are the methods the package needs through the field: those it
// calls, plus every method of each Role.
Used map[string]bool
// Roles are the interfaces the field's value is passed on as: a
// parameter, a struct field (assigned or in a literal) or a conversion
// whose declared type is an interface with methods. Whatever the
// receiver does with the value, it does through that interface, so its
// method set — as the receiver declares it — bounds what the receiver
// needs, and is in Used.
Roles []Role
// Escapes is true when the value also leaves through something that
// bounds nothing — `any`, a concrete type, a return value, a channel, a
// map or slice element — so even Used is not the whole story.
// EscapeAt is where it first does ("file:line").
Escapes bool
EscapeAt string
// Downcast is true when the package type-asserts the field to a
// concrete type (Package.Downcasts says where).
Downcast bool
// Container names the container the field holds ("" for none).
Container string
// RootConfig is true for a field holding the project's root config
// (pkg/config.Config) rather than the component's own block.
RootConfig bool
// contains filtered or unexported fields
}
Field is one Deps field.
type NarrowResult ¶
type NarrowResult struct {
Dir string
// File is the project-relative file the interfaces were written to:
// the package's contract.go, or deps.go.
File string
Narrowed []Narrowed
Skipped []Skipped
// Bloated are the declared interfaces wider than the project's
// interfacebloat limit.
Bloated []Bloated
}
NarrowResult is what Narrow did to one package.
func Narrow ¶
func Narrow(projectDir, dir string) (*NarrowResult, error)
Narrow rewrites every foreign interface field of dir's Deps that declares methods the package never needs to an interface the package declares itself, holding exactly the methods it needs (design §2, K1): those it calls, with each interface it passes the value on as (Field.Roles) embedded. The composition still fills the field with the same value: Go satisfies the narrower interface structurally, so compose.go needs no edit, and every call that passes the value on still type-checks.
A field whose value also leaves through something that bounds nothing (Escapes: `any`, a concrete type, a return value) is left alone — what it needs is not all visible here — as is a field the package never uses at all (delete it instead), and an interface the package already declares (edit it in place).
type Narrowed ¶
type Narrowed struct {
Field, From, To string
// Methods are every method the new interface holds; Embeds are the
// interfaces it embeds for the ones the package passes the value on as.
Methods []string
Embeds []string
}
Narrowed is one field Narrow retyped.
type Package ¶
type Package struct {
// Dir is the package directory, project-relative and slash-separated.
Dir string
// Path is the import path.
Path string
// DepsFile is the project-relative file declaring Deps.
DepsFile string
Fields []Field
// Downcasts are type assertions from a Deps field to a concrete type.
Downcasts []Downcast
// EnvReads are the package's own reads of the process environment
// (os.Getenv, os.LookupEnv, os.Environ) in non-test files: configuration
// the composition never handed it, and a root's config reach cannot see.
EnvReads []EnvRead
// contains filtered or unexported fields
}
Package is one component package's Deps, as used.
type Role ¶
type Role struct {
// Type is the interface as the consumer would write it
// ("threads.Repository"); for an interface literal or a type
// parameter's constraint, its methods in braces.
Type string
// Methods are its exported methods, sorted.
Methods []string
// Embeddable is true when the consumer can name the interface (a named
// interface it may refer to), so a narrowed interface can embed it.
Embeddable bool
// contains filtered or unexported fields
}
Role is one interface a Deps field's value is passed on as.