Skip to content

refactor: rewrite library#1

Merged
mickamy merged 33 commits into
mainfrom
refactor/rewrite
May 3, 2026
Merged

refactor: rewrite library#1
mickamy merged 33 commits into
mainfrom
refactor/rewrite

Conversation

@mickamy

@mickamy mickamy commented May 3, 2026

Copy link
Copy Markdown
Owner

No description provided.

@mickamy mickamy requested a review from Copilot May 3, 2026 02:49
@gitguardian

gitguardian Bot commented May 3, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
12334303 Triggered Generic Password 68ec9d1 example/name-conflict/domain/user/user_repository.go View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR rewrites injector’s core pipeline, replacing the previous scan/resolve/gen/workspace implementation with a new scan → plan → emit architecture plus a new CLI, and updates docs/examples accordingly.

Changes:

  • Introduces new internal/{scan,plan,emit,packages,ir,diag} packages and removes the legacy workspace/, resolve/, and gen/ implementations.
  • Reworks tag/directive parsing (inject:"..." + //injector:container) and updates examples/README to match the new DSL.
  • Adds CI/lint configuration and updates Makefile targets (unit + e2e regeneration).

Reviewed changes

Copilot reviewed 68 out of 68 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
main.go Switches CLI entry wiring to new cli.New and passes args without argv[0].
internal/workspace/loader.go Removes legacy workspace loader.
internal/scan/tags.go Removes legacy inject tag parser.
internal/scan/tag.go Adds new inject:"..." tag parsing (marker/with/arg/returns).
internal/scan/tag_test.go Adds tests for new tag parsing.
internal/scan/strings.go Removes legacy directive parsing helpers.
internal/scan/providers.go Removes legacy provider collection.
internal/scan/provider.go Adds provider scanning producing ir.Provider.
internal/scan/provider_test.go Adds tests for provider scanning behavior.
internal/scan/directive.go Adds //injector:container directive parser.
internal/scan/directive_test.go Adds tests for directive parsing.
internal/scan/containers.go Removes legacy container scanning.
internal/scan/container.go Adds new container scanning producing ir.Container + diags.
internal/scan/container_test.go Adds tests for container scanning and roles/returns/directive.
internal/resolve/types.go Removes legacy resolve graph types.
internal/resolve/order.go Removes legacy topological ordering.
internal/resolve/graph.go Removes legacy graph resolution logic.
internal/resolve/embedded.go Removes legacy embedded container handling.
internal/resolve/convert.go Removes legacy scan→resolve conversion helpers.
internal/prints/fmt.go Removes legacy print wrappers.
internal/plan/plan.go Adds planner/resolver producing an execution plan from scan+index.
internal/plan/plan_test.go Adds tests for planning (errors, overrides, returns, must modes).
internal/plan/lookup.go Adds provider index for lookup by type and textual ref.
internal/plan/lookup_test.go Adds tests for index lookups and formatting.
internal/packages/packages.go Adds wrapper around x/tools/go/packages (tags/tests/mode).
internal/packages/packages_test.go Adds minimal test for packages loader input validation.
internal/ir/types.go Adds new IR boundary types for scan/plan/emit.
internal/ir/types_test.go Adds test for ProviderRef.HasRef.
internal/gen/emitter.go Removes legacy code emitter implementation.
internal/emit/emit.go Adds new emitter for plan.Plan → formatted Go source.
internal/emit/emit_test.go Adds golden-ish tests for generated output and compilation.
internal/emit/imports.go Adds import tracker / type qualification utilities.
internal/emit/imports_test.go Adds tests for aliasing and type import collection.
internal/diag/diag.go Adds diagnostics model with severity + hints + formatting.
internal/diag/diag_test.go Adds tests for diagnostics formatting and helpers.
internal/config/config.go Removes legacy must/on-error config (panic/fatal strategies).
internal/cli/generate.go Removes legacy subcommand-based CLI implementation.
internal/cli/app.go Removes legacy CLI App implementation.
internal/cli/cli.go Adds new single-command CLI driving scan/plan/emit end-to-end.
internal/cli/cli_test.go Adds tests for CLI help/version/unknown flag/no-args behavior.
example/with-error/main.go Updates example to new with= syntax and must constructor usage.
example/with-error/injector_gen.go Adds MustNewContainer to generated output.
example/with-error/go.mod Removes per-example module file.
example/with-error/config/database_config.go Adds gosec lint suppression comments for example DSNs.
example/simple/main.go Updates example to new with= syntax and main entrypoint.
example/simple/go.mod Removes per-example module file.
example/simple/config/database_config.go Adds gosec lint suppression comments for example DSNs.
example/returns/main.go Updates returns example to new returns usage + directive naming.
example/name-conflict/main.go Updates example to new with= syntax.
example/name-conflict/go.mod Removes per-example module file.
example/name-conflict/domain/user/user_service.go Wraps repository errors with context.
example/name-conflict/domain/task/task_service.go Wraps repository errors with context.
example/name-conflict/config/database_config.go Adds gosec lint suppression comments for example DSNs.
example/embedded/main.go Updates example from embedded container param to explicit arg DB input.
example/embedded/injector_gen.go Updates generated constructor signature/imports to match arg input.
example/embedded/di/infra.go Removes legacy override tag in Infra container.
example/embedded/config/database_config.go Adds gosec lint suppression comments for example DSNs.
example/embedded-error/main.go Updates embedded-error example to MustNew* flow and arg input.
example/embedded-error/injector_gen.go Updates generated constructors + adds MustNewApp.
example/embedded-error/di/injector_gen.go Adds MustNewInfra wrapper.
example/embedded-error/di/infra.go Removes legacy override tag in Infra container.
example/embedded-error/config/database_config.go Adds gosec lint suppression comments for example DSNs.
README.md Rewrites documentation for new tags/directive/CLI and new resolution rules.
Makefile Adds unit vs e2e test targets and example regeneration checks.
.golangci.yaml Adds golangci-lint configuration (formatters + broad linter set).
.github/workflows/ci.yml Adds CI workflow (lint + test).
.github/workflows/release.yml Adds release workflow using goreleaser.
.github/FUNDING.yaml Adds GitHub Sponsors configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/plan/lookup.go Outdated
Comment on lines +21 to +32
// NewIndex builds an Index from the given providers. Each entry is copied
// into the index so that pointer identity is preserved across lookups.
func NewIndex(providers []ir.Provider) *Index {
idx := &Index{
byFull: make(map[string]*ir.Provider, len(providers)),
byShort: make(map[string][]*ir.Provider),
byType: make(map[string][]*ir.Provider),
}
for i := range providers {
p := &providers[i]
idx.all = append(idx.all, p)

Comment thread internal/emit/imports.go
Comment on lines +146 to +192
// collectPkgs walks t and returns each named-type package it references.
func collectPkgs(t types.Type) []*types.Package {
var out []*types.Package
seen := map[*types.Package]struct{}{}

var walk func(types.Type)
walk = func(t types.Type) {
switch tt := t.(type) {
case nil:
return
case *types.Named:
if obj := tt.Obj(); obj != nil {
if pkg := obj.Pkg(); pkg != nil {
if _, ok := seen[pkg]; !ok {
seen[pkg] = struct{}{}
out = append(out, pkg)
}
}
}
if ta := tt.TypeArgs(); ta != nil {
for arg := range ta.Types() {
walk(arg)
}
}
case *types.Pointer:
walk(tt.Elem())
case *types.Slice:
walk(tt.Elem())
case *types.Array:
walk(tt.Elem())
case *types.Map:
walk(tt.Key())
walk(tt.Elem())
case *types.Chan:
walk(tt.Elem())
case *types.Signature:
if params := tt.Params(); params != nil {
for v := range params.Variables() {
walk(v.Type())
}
}
if results := tt.Results(); results != nil {
for v := range results.Variables() {
walk(v.Type())
}
}
}
Comment thread internal/plan/plan.go
Comment on lines +315 to +335
func buildInputs(c ir.Container) ([]Input, []diag.Diag) {
var inputs []Input
var diags []diag.Diag
seen := map[string]token.Position{}
for _, f := range c.Fields {
if f.Role != ir.RoleArg {
continue
}
name := f.ArgName
if name == "" {
name = deriveInputName(f.Type)
}
tk := TypeKey(f.Type)
if prev, ok := seen[tk]; ok {
diags = append(diags, diag.Errorf(f.Pos,
"duplicate input type %s (first declared at %s)", TypeString(f.Type), prev))
continue
}
seen[tk] = f.Pos
inputs = append(inputs, Input{Name: name, Type: f.Type})
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 68 out of 68 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/cli/cli.go Outdated

outDir := filepath.Dir(group.containers[0].Pos.Filename)
outPath := filepath.Join(outDir, outputFile)
if err := os.WriteFile(outPath, out, 0o600); err != nil {

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 68 out of 68 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/emit/emit.go
Comment on lines +87 to +92
func writeContainer(w io.Writer, im *Imports, p plan.Plan) error {
name := p.ConstructorName
structName := p.Container.StructName
paramSig := formatParams(im, p.Inputs)
retSig := im.QualifyType(p.ReturnType)

Comment thread internal/emit/emit.go
Comment on lines +104 to +150
writeStructLiteral(w, p)
if p.ReturnsError {
fmt.Fprint(w, ", nil")
}
fmt.Fprint(w, "\n}\n\n")

if p.EmitMust {
writeMustVariant(w, im, p)
}
return nil
}

func writeSteps(w io.Writer, im *Imports, p plan.Plan) error {
for _, s := range p.Steps {
switch s.Kind {
case plan.StepKindInput:
// The input is already a function parameter — nothing to emit.
case plan.StepKindProvider:
if s.Provider == nil {
return fmt.Errorf("provider step %q has nil Provider", s.VarName)
}
args := make([]string, 0, len(s.ArgSteps))
for _, idx := range s.ArgSteps {
args = append(args, p.Steps[idx].VarName)
}
call := im.QualifyProvider(s.Provider)
if s.Provider.ReturnsError {
fmt.Fprintf(w, "\t%s, err := %s(%s)\n", s.VarName, call, strings.Join(args, ", "))
fmt.Fprint(w, "\tif err != nil {\n")
fmt.Fprint(w, "\t\treturn nil, err\n")
fmt.Fprint(w, "\t}\n")
} else {
fmt.Fprintf(w, "\t%s := %s(%s)\n", s.VarName, call, strings.Join(args, ", "))
}
}
}
return nil
}

func writeStructLiteral(w io.Writer, p plan.Plan) {
fmt.Fprintf(w, "\n\treturn &%s{\n", p.Container.StructName)
for _, o := range p.Outputs {
v := p.Steps[o.StepIndex].VarName
fmt.Fprintf(w, "\t\t%s: %s,\n", o.FieldName, v)
}
fmt.Fprint(w, "\t}")
}
Comment thread internal/emit/emit.go
Comment on lines +130 to +134
if s.Provider.ReturnsError {
fmt.Fprintf(w, "\t%s, err := %s(%s)\n", s.VarName, call, strings.Join(args, ", "))
fmt.Fprint(w, "\tif err != nil {\n")
fmt.Fprint(w, "\t\treturn nil, err\n")
fmt.Fprint(w, "\t}\n")
Comment thread internal/plan/plan.go
Comment on lines +102 to +113
var outputs []Output
for _, f := range c.Fields {
if f.Role != ir.RoleOut {
continue
}
stepIdx, ds := r.resolveField(f)
diags = append(diags, ds...)
if stepIdx < 0 {
continue
}
outputs = append(outputs, Output{FieldName: f.Name, StepIndex: stepIdx})
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 68 out of 68 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +79 to +90
// stripDirectivePrefix matches "injector:container" exactly or followed by
// whitespace, returning the trailing arguments.
func stripDirectivePrefix(body string) (string, bool) {
if body == directiveTag {
return "", true
}
rest, ok := strings.CutPrefix(body, directiveTag+" ")
if !ok {
return "", false
}
return strings.TrimSpace(rest), true
}
Comment thread internal/emit/imports.go
Comment on lines +146 to +203
// collectPkgs walks t and returns each named-type package it references.
//
// The walk descends through the type shapes that may appear in the generated
// source: pointers, slices/arrays/maps/channels, signature parameters and
// results, and named types' type arguments. It deliberately does NOT descend
// into a *types.Named's Underlying form, nor into *types.Interface methods
// or *types.Struct fields — those internals are not written verbatim into
// the generated file (only the named type itself is qualified), so adding
// their packages would cause "imported and not used" errors. If we ever
// inline anonymous interface/struct shapes into the output, those cases
// would need to be added here.
func collectPkgs(t types.Type) []*types.Package {
var out []*types.Package
seen := map[*types.Package]struct{}{}

var walk func(types.Type)
walk = func(t types.Type) {
if t == nil {
return
}
switch tt := t.(type) {
case *types.Named:
if obj := tt.Obj(); obj != nil {
if pkg := obj.Pkg(); pkg != nil {
if _, ok := seen[pkg]; !ok {
seen[pkg] = struct{}{}
out = append(out, pkg)
}
}
}
if ta := tt.TypeArgs(); ta != nil {
for arg := range ta.Types() {
walk(arg)
}
}
case *types.Pointer:
walk(tt.Elem())
case *types.Slice:
walk(tt.Elem())
case *types.Array:
walk(tt.Elem())
case *types.Map:
walk(tt.Key())
walk(tt.Elem())
case *types.Chan:
walk(tt.Elem())
case *types.Signature:
if params := tt.Params(); params != nil {
for v := range params.Variables() {
walk(v.Type())
}
}
if results := tt.Results(); results != nil {
for v := range results.Variables() {
walk(v.Type())
}
}
}
Comment thread Makefile
all: build

build:
@echo "Building $(APP_NAME)..."

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 68 out of 69 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mickamy mickamy merged commit da99a92 into main May 3, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants