Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

136 lines
3.9 KiB
Go

package resolver
import (
"sort"
"github.com/zzet/gortex/internal/graph"
)
// mediatrVia is the Meta["via"] tag the C# extractor stamps on a MediatR
// Send/Publish placeholder.
const mediatrVia = "mediatr-dispatch"
// ResolveMediatRCalls binds .NET MediatR dispatches to their handler's
// Handle method by request type: `_mediator.Send(new CreateOrder())` →
// `CreateOrderHandler.Handle` (one handler), and `_bus.Publish(new
// OrderPlaced())` → every `INotificationHandler<OrderPlaced>` (fan-out).
// Type-keyed via the handler base list, so edges land at the typed
// framework tier.
//
// Returns the number of caller → handler edges synthesized.
func ResolveMediatRCalls(g graph.Store) int {
if g == nil {
return 0
}
// index: kind → request type → handler Handle methods.
index := map[string]map[string][]*graph.Node{"request": {}, "notification": {}}
for _, n := range nodesByKindsOrAll(g, graph.KindMethod, graph.KindFunction) {
if n == nil || n.Meta == nil {
continue
}
reqType, _ := n.Meta["mediatr_request_type"].(string)
kind, _ := n.Meta["mediatr_kind"].(string)
if reqType == "" || index[kind] == nil {
continue
}
index[kind][reqType] = append(index[kind][reqType], n)
}
if len(index["request"]) == 0 && len(index["notification"]) == 0 {
return 0
}
resolved := 0
var reindex []graph.EdgeReindex
var batch []*graph.Edge
for e := range g.EdgesByKind(graph.EdgeCalls) {
if e == nil || e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v != mediatrVia {
continue
}
reqType, _ := e.Meta["mediatr_request_type"].(string)
kind, _ := e.Meta["mediatr_kind"].(string)
if reqType == "" || index[kind] == nil {
continue
}
cands := index[kind][reqType]
if kind == "request" {
// A request has a single handler; never guess on ambiguity.
target := pickStoreAction(g, e, sameBoundaryCandidates(g, e.From, cands))
resolved += mediatrRebind(e, target, &reindex)
continue
}
// A notification fans out to every handler.
handlers := sameBoundaryCandidates(g, e.From, cands)
sort.Slice(handlers, func(i, j int) bool { return handlers[i].ID < handlers[j].ID })
if len(handlers) == 0 {
resolved += mediatrRebind(e, nil, &reindex)
continue
}
resolved += mediatrRebind(e, handlers[0], &reindex)
for _, h := range handlers[1:] {
batch = append(batch, mediatrFanoutEdge(e, h, reqType))
resolved++
}
}
if len(reindex) > 0 {
g.ReindexEdges(reindex)
}
for _, ne := range batch {
g.AddEdge(ne)
}
return resolved
}
// mediatrRebind points the placeholder e at target (typed tier) or
// re-orphans it. Returns 1 when it lands on a real handler, else 0.
func mediatrRebind(e *graph.Edge, target *graph.Node, reindex *[]graph.EdgeReindex) int {
want := "unresolved::*.Handle"
if target != nil {
want = target.ID
}
if e.To == want {
if target != nil {
return 1
}
return 0
}
oldTo := e.To
e.To = want
hit := 0
if target != nil {
e.Origin = graph.OriginASTInferred
e.Confidence = ConfidenceTyped
e.ConfidenceLabel = graph.ConfidenceLabelFor(graph.EdgeCalls, ConfidenceTyped)
StampSynthesizedTyped(e, SynthMediatR)
hit = 1
} else {
e.Origin = graph.OriginASTInferred
e.Confidence = 0
e.ConfidenceLabel = ""
UnstampSynthesized(e)
}
*reindex = append(*reindex, graph.EdgeReindex{Edge: e, OldTo: oldTo})
return hit
}
func mediatrFanoutEdge(e *graph.Edge, handler *graph.Node, reqType string) *graph.Edge {
return &graph.Edge{
From: e.From, To: handler.ID, Kind: graph.EdgeCalls,
FilePath: e.FilePath, Line: e.Line,
Origin: graph.OriginASTInferred,
Confidence: ConfidenceTyped,
ConfidenceLabel: graph.ConfidenceLabelFor(graph.EdgeCalls, ConfidenceTyped),
Meta: map[string]any{
"via": mediatrVia,
"mediatr_request_type": reqType,
"mediatr_kind": "notification",
MetaSynthesizedBy: SynthMediatR,
MetaProvenance: ProvenanceFramework,
},
}
}