-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgraph.go
More file actions
148 lines (134 loc) · 5.15 KB
/
graph.go
File metadata and controls
148 lines (134 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Package reqflow statically traces HTTP request paths through Go codebases.
//
// Given a route like "POST /orders", reqflow finds the handler, follows the
// call chain through services and stores, and shows exactly which methods
// are called at each layer — with file names and line numbers.
//
// # Install the CLI
//
// go install github.com/ShipOrBleed/reqflow/cmd/reqflow@latest
//
// # Trace a request
//
// graph, err := reqflow.Parse(reqflow.ParseOptions{Dir: "."})
// if err != nil {
// log.Fatal(err)
// }
// result := reqflow.Trace("POST /orders", graph)
// for _, node := range result.Chain {
// fmt.Printf("[%s] %s\n", node.Kind, node.Name)
// }
//
// # List all routes
//
// routes := reqflow.ListRoutes(graph)
// for _, r := range routes {
// fmt.Printf("%s %s → %s.%s()\n", r.Method, r.Path, r.HandlerName, r.MethodName)
// }
//
// # Supported frameworks
//
// GoFr, Gin, Echo, Fiber, and net/http handlers are automatically detected.
// Stores are detected by struct field types (*sql.DB, *gorm.DB, *mongo.Client, etc.),
// not by naming conventions.
package reqflow
import (
"fmt"
"os"
)
// Version is set at build time via ldflags.
var Version = "dev"
// Warn prints a warning message to stderr. Used by analysis functions
// that encounter non-fatal errors (e.g., git not available, file not found).
func Warn(format string, args ...any) {
fmt.Fprintf(os.Stderr, " ⚠ "+format+"\n", args...)
}
// NodeKind classifies a graph node into an architectural layer.
type NodeKind string
const (
KindStruct NodeKind = "struct"
KindInterface NodeKind = "interface"
KindFunc NodeKind = "func"
KindHandler NodeKind = "handler" // HTTP handler
KindStore NodeKind = "store" // DB layer / Repository
KindClient NodeKind = "client" // External HTTP/gRPC client
KindModel NodeKind = "model" // DB entity / Model
KindService NodeKind = "service" // Business logic
KindEvent NodeKind = "event" // Event Bus (Kafka/Rabbit)
KindMiddleware NodeKind = "middleware" // HTTP Middleware
KindGRPC NodeKind = "grpc" // gRPC service
KindInfra NodeKind = "infra" // External infrastructure
KindRoute NodeKind = "route" // API endpoint
KindEnvVar NodeKind = "envvar" // Environment variable
KindTable NodeKind = "table" // Database table
KindDep NodeKind = "dependency" // go.mod transitive dependency
KindContainer NodeKind = "container" // Docker/K8s container
KindProtoRPC NodeKind = "proto_rpc" // Proto RPC method
KindProtoMsg NodeKind = "proto_msg" // Proto message type
)
// Node represents a single component in the architecture graph (struct, interface,
// function, handler, service, store, model, etc.). Nodes are uniquely identified
// by their ID (typically "package.TypeName") and carry metadata in the Meta map.
type Node struct {
ID string // "pkg/path.TypeName"
Kind NodeKind
Name string
Package string
Fields []Field
Methods []string
File string
Line int
Meta map[string]string // e.g. "route": "GET /users"
}
// Field represents a struct field with its name, type, and struct tag.
type Field struct{ Name, Type, Tag string }
// EdgeKind classifies the relationship between two nodes.
type EdgeKind string
const (
EdgeEmbeds EdgeKind = "embeds"
EdgeImplements EdgeKind = "implements"
EdgeDepends EdgeKind = "depends"
EdgeCalls EdgeKind = "calls" // function-to-function call
EdgeFlows EdgeKind = "flows" // request data flow (handler→service→store)
EdgeReads EdgeKind = "reads" // reads env var
EdgeMapsTo EdgeKind = "maps_to" // model→table mapping
EdgeTransitive EdgeKind = "transitive" // transitive dependency
EdgePublishes EdgeKind = "publishes" // event publish
EdgeSubscribes EdgeKind = "subscribes" // event subscribe
EdgeRPC EdgeKind = "rpc" // cross-service RPC call
)
// Edge represents a directed relationship between two nodes in the graph.
type Edge struct {
From, To string
Kind EdgeKind
}
// Graph is the core data structure representing the architecture of a Go codebase.
// It contains nodes (components), edges (relationships), and clusters (package groupings).
type Graph struct {
Nodes map[string]*Node
Edges []Edge
Clusters map[string][]string // pkg path → []node IDs
Meta map[string]string // graph-level metadata (repo name, commit SHA, etc.)
MethodCalls MethodCallIndex // method-level call index for trace precision
}
// NewGraph creates an empty, initialized Graph ready for use.
func NewGraph() *Graph {
return &Graph{
Nodes: make(map[string]*Node),
Clusters: make(map[string][]string),
Meta: make(map[string]string),
}
}
// AddNode adds a node to the graph, initializing its Meta map if nil,
// and registering it in the appropriate package cluster.
func (g *Graph) AddNode(n *Node) {
if n.Meta == nil {
n.Meta = make(map[string]string)
}
g.Nodes[n.ID] = n
g.Clusters[n.Package] = append(g.Clusters[n.Package], n.ID)
}
// AddEdge adds a directed edge between two nodes.
func (g *Graph) AddEdge(from, to string, kind EdgeKind) {
g.Edges = append(g.Edges, Edge{From: from, To: to, Kind: kind})
}