diff --git a/dbdoc/dbdoc.go b/dbdoc/dbdoc.go index 586b067..3c478c6 100644 --- a/dbdoc/dbdoc.go +++ b/dbdoc/dbdoc.go @@ -52,7 +52,7 @@ func Run(conf Config) error { } defer f.Close() - err = writeMermaid(f, nodes) + err = WriteMermaid(f, nodes) if err != nil { return fmt.Errorf("failed to write mermaid: %w", err) } diff --git a/dbdoc/funcs.go b/dbdoc/funcs.go index e1ba7a2..239a378 100644 --- a/dbdoc/funcs.go +++ b/dbdoc/funcs.go @@ -10,8 +10,8 @@ import ( "golang.org/x/tools/go/ssa" ) -func BuildFuncs(ctx *Context, pkgs []*packages.Package, ssaProgram *ssa.Program, loopRangeMap LoopRangeMap) ([]function, error) { - var funcs []function +func BuildFuncs(ctx *Context, pkgs []*packages.Package, ssaProgram *ssa.Program, loopRangeMap LoopRangeMap) ([]Function, error) { + var funcs []Function for _, pkg := range pkgs { for _, def := range pkg.TypesInfo.Defs { if def == nil { @@ -46,34 +46,38 @@ func BuildFuncs(ctx *Context, pkgs []*packages.Package, ssaProgram *ssa.Program, continue } - queries := make([]query, 0, len(stringLiterals)) + queries := make([]Query, 0, len(stringLiterals)) for _, strLiteral := range stringLiterals { newQueries := AnalyzeSQL(ctx, strLiteral) queries = append(queries, newQueries...) } loopRanges := loopRangeMap[ssaFunc.Name()] - queriesInLoop := make([]inLoop[query], 0, len(queries)) + queriesInLoop := make([]InLoop[Query], 0, len(queries)) for _, q := range queries { - queriesInLoop = append(queriesInLoop, inLoop[query]{ - value: q, - inLoop: loopRanges.Search(ctx.FileSet, q.pos), + queriesInLoop = append(queriesInLoop, InLoop[Query]{ + Value: q, + InLoop: loopRanges.Search(ctx.FileSet, q.Pos), }) } - callsInLoop := make([]inLoop[string], 0, len(calls)) + callsInLoop := make([]InLoop[Call], 0, len(calls)) for _, call := range calls { - callsInLoop = append(callsInLoop, inLoop[string]{ - value: call.id, - inLoop: loopRanges.Search(ctx.FileSet, call.pos), + callsInLoop = append(callsInLoop, InLoop[Call]{ + Value: Call{ + FunctionID: call.id, + Pos: call.pos, + }, + InLoop: loopRanges.Search(ctx.FileSet, call.pos), }) } - funcs = append(funcs, function{ - id: def.Id(), - name: def.Name(), - queries: queriesInLoop, - calls: callsInLoop, + funcs = append(funcs, Function{ + ID: def.Id(), + Name: def.Name(), + Pos: def.Pos(), + Queries: queriesInLoop, + Calls: callsInLoop, }) } } diff --git a/dbdoc/graph.go b/dbdoc/graph.go index dee065f..03deca6 100644 --- a/dbdoc/graph.go +++ b/dbdoc/graph.go @@ -10,78 +10,78 @@ import ( "github.com/mazrean/isucrud/internal/pkg/analyze" ) -func BuildGraph(funcs []function, ignoreFuncs, ignoreFuncPrefixes []string, ignoreMain, ignoreInitialize bool) []*node { +func BuildGraph(funcs []Function, ignoreFuncs, ignoreFuncPrefixes []string, ignoreMain, ignoreInitialize bool) []*Node { type tmpEdge struct { label string - edgeType edgeType + edgeType EdgeType childID string inLoop bool } type tmpNode struct { - *node + *Node edges []tmpEdge } tmpNodeMap := make(map[string]tmpNode, len(funcs)) FUNC_LOOP: for _, f := range funcs { - if (ignoreMain && f.name == "main") || - (ignoreInitialize && analyze.IsInitializeFuncName(f.name)) { + if (ignoreMain && f.Name == "main") || + (ignoreInitialize && analyze.IsInitializeFuncName(f.Name)) { continue } for _, ignore := range ignoreFuncs { - if f.name == ignore { + if f.Name == ignore { continue FUNC_LOOP } } for _, ignorePrefix := range ignoreFuncPrefixes { - if strings.HasPrefix(f.name, ignorePrefix) { + if strings.HasPrefix(f.Name, ignorePrefix) { continue FUNC_LOOP } } var edges []tmpEdge - for _, q := range f.queries { - id := tableID(q.value.table) + for _, q := range f.Queries { + id := tableID(q.Value.Table) tmpNodeMap[id] = tmpNode{ - node: &node{ - id: id, - label: q.value.table, - nodeType: nodeTypeTable, + Node: &Node{ + ID: id, + Label: q.Value.Table, + NodeType: NodeTypeTable, }, } - var edgeType edgeType - switch q.value.queryType { - case queryTypeSelect: - edgeType = edgeTypeSelect - case queryTypeInsert: - edgeType = edgeTypeInsert - case queryTypeUpdate: - edgeType = edgeTypeUpdate - case queryTypeDelete: - edgeType = edgeTypeDelete + var edgeType EdgeType + switch q.Value.QueryType { + case QueryTypeSelect: + edgeType = EdgeTypeSelect + case QueryTypeInsert: + edgeType = EdgeTypeInsert + case QueryTypeUpdate: + edgeType = EdgeTypeUpdate + case QueryTypeDelete: + edgeType = EdgeTypeDelete default: - log.Printf("unknown query type: %v\n", q.value.queryType) + log.Printf("unknown query type: %v\n", q.Value.QueryType) continue } edges = append(edges, tmpEdge{ label: "", edgeType: edgeType, - childID: tableID(q.value.table), - inLoop: q.inLoop, + childID: tableID(q.Value.Table), + inLoop: q.InLoop, }) } - for _, c := range f.calls { - id := funcID(c.value) + for _, c := range f.Calls { + id := funcID(c.Value.FunctionID) edges = append(edges, tmpEdge{ label: "", - edgeType: edgeTypeCall, + edgeType: EdgeTypeCall, childID: id, - inLoop: c.inLoop, + inLoop: c.InLoop, }) } @@ -97,12 +97,12 @@ FUNC_LOOP: }) edges = slices.Compact(edges) - id := funcID(f.id) + id := funcID(f.ID) tmpNodeMap[id] = tmpNode{ - node: &node{ - id: id, - label: f.name, - nodeType: nodeTypeFunction, + Node: &Node{ + ID: id, + Label: f.Name, + NodeType: NodeTypeFunction, }, edges: edges, } @@ -110,7 +110,7 @@ FUNC_LOOP: type revEdge struct { label string - edgeType edgeType + edgeType EdgeType parentID string inLoop bool } @@ -120,7 +120,7 @@ FUNC_LOOP: revEdgeMap[tmpEdge.childID] = append(revEdgeMap[tmpEdge.childID], revEdge{ label: tmpEdge.label, edgeType: tmpEdge.edgeType, - parentID: tmpNode.id, + parentID: tmpNode.ID, inLoop: tmpEdge.inLoop, }) } @@ -129,7 +129,7 @@ FUNC_LOOP: newNodeMap := make(map[string]tmpNode, len(tmpNodeMap)) nodeQueue := list.New() for id, node := range tmpNodeMap { - if node.nodeType == nodeTypeTable { + if node.NodeType == NodeTypeTable { newNodeMap[id] = node nodeQueue.PushBack(node) delete(tmpNodeMap, id) @@ -141,28 +141,28 @@ FUNC_LOOP: nodeQueue.Remove(element) node := element.Value.(tmpNode) - for _, edge := range revEdgeMap[node.id] { + for _, edge := range revEdgeMap[node.ID] { parent := tmpNodeMap[edge.parentID] newNodeMap[edge.parentID] = parent nodeQueue.PushBack(parent) } - delete(revEdgeMap, node.id) + delete(revEdgeMap, node.ID) } - var nodes []*node + var nodes []*Node for _, tmpNode := range newNodeMap { - node := tmpNode.node + node := tmpNode.Node for _, tmpEdge := range tmpNode.edges { child, ok := newNodeMap[tmpEdge.childID] if !ok { continue } - node.edges = append(node.edges, edge{ - label: tmpEdge.label, - node: child.node, - edgeType: tmpEdge.edgeType, - inLoop: tmpEdge.inLoop, + node.Edges = append(node.Edges, Edge{ + Label: tmpEdge.label, + Node: child.Node, + EdgeType: tmpEdge.edgeType, + InLoop: tmpEdge.inLoop, }) } nodes = append(nodes, node) diff --git a/dbdoc/loopmap.go b/dbdoc/loopmap.go index adf3e7e..2bfa38d 100644 --- a/dbdoc/loopmap.go +++ b/dbdoc/loopmap.go @@ -38,14 +38,14 @@ func (v *loopRangeVisitor) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.ForStmt: v.lr = append(v.lr, LoopRange{ - start: n.Body.Lbrace, - end: n.Body.Rbrace, + Start: n.Body.Lbrace, + End: n.Body.Rbrace, }) return nil case *ast.RangeStmt: v.lr = append(v.lr, LoopRange{ - start: n.Body.Lbrace, - end: n.Body.Rbrace, + Start: n.Body.Lbrace, + End: n.Body.Rbrace, }) return nil } diff --git a/dbdoc/mermaid.go b/dbdoc/mermaid.go index 7625cb7..a65944a 100644 --- a/dbdoc/mermaid.go +++ b/dbdoc/mermaid.go @@ -25,23 +25,23 @@ var ( color string valid bool }{ - nodeTypeTable: {"table", "テーブル", tableNodeColor, true}, - nodeTypeFunction: {"func", "関数", funcNodeColor, true}, + NodeTypeTable: {"table", "テーブル", tableNodeColor, true}, + NodeTypeFunction: {"func", "関数", funcNodeColor, true}, } edgeTypes = []struct { label string color string valid bool }{ - edgeTypeInsert: {"INSERT", insertLinkColor, true}, - edgeTypeUpdate: {"UPDATE", updateLinkColor, true}, - edgeTypeDelete: {"DELETE", deleteLinkColor, true}, - edgeTypeSelect: {"SELECT", selectLinkColor, true}, - edgeTypeCall: {"関数呼び出し", callLinkColor, true}, + EdgeTypeInsert: {"INSERT", insertLinkColor, true}, + EdgeTypeUpdate: {"UPDATE", updateLinkColor, true}, + EdgeTypeDelete: {"DELETE", deleteLinkColor, true}, + EdgeTypeSelect: {"SELECT", selectLinkColor, true}, + EdgeTypeCall: {"関数呼び出し", callLinkColor, true}, } ) -func writeMermaid(w io.StringWriter, nodes []*node) error { +func WriteMermaid(w io.StringWriter, nodes []*Node) error { _, err := w.WriteString("# DB Graph\n") if err != nil { return fmt.Errorf("failed to write header: %w", err) @@ -100,43 +100,43 @@ func writeMermaid(w io.StringWriter, nodes []*node) error { } } - edgeLinksMap := map[edgeType][]string{} + edgeLinksMap := map[EdgeType][]string{} edgeID := 0 for _, node := range nodes { var src string - if nodeType := nodeTypes[node.nodeType]; nodeType.valid { - src = fmt.Sprintf("%s[%s]:::%s", node.id, node.label, nodeType.name) + if nodeType := nodeTypes[node.NodeType]; nodeType.valid { + src = fmt.Sprintf("%s[%s]:::%s", node.ID, node.Label, nodeType.name) } else { - log.Printf("unknown node type: %v\n", node.nodeType) + log.Printf("unknown node type: %v\n", node.NodeType) continue } - for _, edge := range node.edges { + for _, edge := range node.Edges { var dst string - if nodeType := nodeTypes[edge.node.nodeType]; nodeType.valid { - dst = fmt.Sprintf("%s[%s]:::%s", edge.node.id, edge.node.label, nodeType.name) + if nodeType := nodeTypes[edge.Node.NodeType]; nodeType.valid { + dst = fmt.Sprintf("%s[%s]:::%s", edge.Node.ID, edge.Node.Label, nodeType.name) } else { - log.Printf("unknown node type: %v\n", node.nodeType) + log.Printf("unknown node type: %v\n", node.NodeType) continue } line := "--" - if edge.inLoop { + if edge.InLoop { line = "==" } var edgeExpr string - if edge.label == "" { + if edge.Label == "" { edgeExpr = fmt.Sprintf("%s>", line) } else { - edgeExpr = fmt.Sprintf("%s %s %s>", line, edge.label, line) + edgeExpr = fmt.Sprintf("%s %s %s>", line, edge.Label, line) } _, err = w.WriteString(fmt.Sprintf(" %s %s %s\n", src, edgeExpr, dst)) if err != nil { return fmt.Errorf("failed to write edge: %w", err) } - edgeLinksMap[edge.edgeType] = append(edgeLinksMap[edge.edgeType], strconv.Itoa(edgeID)) + edgeLinksMap[edge.EdgeType] = append(edgeLinksMap[edge.EdgeType], strconv.Itoa(edgeID)) edgeID++ } diff --git a/dbdoc/sql.go b/dbdoc/sql.go index 7d16061..fbbb07f 100644 --- a/dbdoc/sql.go +++ b/dbdoc/sql.go @@ -18,16 +18,16 @@ var ( selectKeywords = []string{" where ", " group by ", " having ", " window ", " order by ", "limit ", " for "} ) -func AnalyzeSQL(ctx *Context, sql stringLiteral) []query { +func AnalyzeSQL(ctx *Context, sql stringLiteral) []Query { sqlValue := strings.ToLower(sql.value) strQueries := extractSubQueries(ctx, sqlValue) - var queries []query + var queries []Query for _, sqlValue := range strQueries { newQueries := analyzeSQLWithoutSubQuery(ctx, sqlValue, sql.pos) for _, query := range newQueries { - fmt.Printf("%s(%s): %s\n", query.queryType, query.table, sqlValue) + fmt.Printf("%s(%s): %s\n", query.QueryType, query.Table, sqlValue) } queries = append(queries, newQueries...) } @@ -101,8 +101,8 @@ func extractSubQueries(_ *Context, sql string) []string { return subQueries } -func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []query { - var queries []query +func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []Query { + var queries []Query switch { case strings.HasPrefix(sqlValue, "select"): _, after, found := strings.Cut(sqlValue, " from ") @@ -110,10 +110,10 @@ func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []q tableNames := tableForm(ctx, sqlValue, pos) for _, tableName := range tableNames { - queries = append(queries, query{ - queryType: queryTypeSelect, - table: tableName, - pos: pos, + queries = append(queries, Query{ + QueryType: QueryTypeSelect, + Table: tableName, + Pos: pos, }) } break @@ -140,10 +140,10 @@ func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []q for i, name := range tableRe.SubexpNames() { if name == "Table" { - queries = append(queries, query{ - queryType: queryTypeSelect, - table: matches[i], - pos: pos, + queries = append(queries, Query{ + QueryType: QueryTypeSelect, + Table: matches[i], + Pos: pos, }) } } @@ -153,10 +153,10 @@ func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []q for i, name := range insertRe.SubexpNames() { if name == "Table" { - queries = append(queries, query{ - queryType: queryTypeInsert, - table: matches[i], - pos: pos, + queries = append(queries, Query{ + QueryType: QueryTypeInsert, + Table: matches[i], + Pos: pos, }) } } @@ -181,10 +181,10 @@ func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []q for i, name := range tableRe.SubexpNames() { if name == "Table" { - queries = append(queries, query{ - queryType: queryTypeUpdate, - table: matches[i], - pos: pos, + queries = append(queries, Query{ + QueryType: QueryTypeUpdate, + Table: matches[i], + Pos: pos, }) } } @@ -194,10 +194,10 @@ func analyzeSQLWithoutSubQuery(ctx *Context, sqlValue string, pos token.Pos) []q for i, name := range deleteRe.SubexpNames() { if name == "Table" { - queries = append(queries, query{ - queryType: queryTypeDelete, - table: matches[i], - pos: pos, + queries = append(queries, Query{ + QueryType: QueryTypeDelete, + Table: matches[i], + Pos: pos, }) } } diff --git a/dbdoc/types.go b/dbdoc/types.go index 6f22890..ead0b75 100644 --- a/dbdoc/types.go +++ b/dbdoc/types.go @@ -10,8 +10,8 @@ type Context struct { } type LoopRange struct { - start token.Pos - end token.Pos + Start token.Pos + End token.Pos } type LoopRanges []LoopRange type LoopRangeMap map[string]LoopRanges @@ -19,8 +19,8 @@ type LoopRangeMap map[string]LoopRanges func (lr LoopRanges) Search(fset *token.FileSet, pos token.Pos) bool { position := fset.Position(pos) for _, r := range lr { - start := fset.Position(r.start) - end := fset.Position(r.end) + start := fset.Position(r.Start) + end := fset.Position(r.End) if position.Filename != start.Filename || position.Filename != end.Filename || position.Line < start.Line || position.Line > end.Line || (position.Line == start.Line && position.Column < start.Column) || @@ -34,16 +34,17 @@ func (lr LoopRanges) Search(fset *token.FileSet, pos token.Pos) bool { return false } -type inLoop[T any] struct { - value T - inLoop bool +type InLoop[T any] struct { + Value T + InLoop bool } -type function struct { - id string - name string - queries []inLoop[query] - calls []inLoop[string] +type Function struct { + ID string + Name string + Pos token.Pos + Queries []InLoop[Query] + Calls []InLoop[Call] } type stringLiteral struct { @@ -51,65 +52,70 @@ type stringLiteral struct { pos token.Pos } -type query struct { - queryType queryType - table string - pos token.Pos +type Call struct { + FunctionID string + Pos token.Pos } -type queryType uint8 +type Query struct { + QueryType QueryType + Table string + Pos token.Pos +} + +type QueryType uint8 const ( - queryTypeSelect queryType = iota + 1 - queryTypeInsert - queryTypeUpdate - queryTypeDelete + QueryTypeSelect QueryType = iota + 1 + QueryTypeInsert + QueryTypeUpdate + QueryTypeDelete ) -func (qt queryType) String() string { +func (qt QueryType) String() string { switch qt { - case queryTypeSelect: + case QueryTypeSelect: return "select" - case queryTypeInsert: + case QueryTypeInsert: return "insert" - case queryTypeUpdate: + case QueryTypeUpdate: return "update" - case queryTypeDelete: + case QueryTypeDelete: return "delete" } return "" } -type node struct { - id string - label string - nodeType nodeType - edges []edge +type Node struct { + ID string + Label string + NodeType NodeType + Edges []Edge } -type nodeType uint8 +type NodeType uint8 const ( - nodeTypeUnknown nodeType = iota - nodeTypeTable - nodeTypeFunction + NodeTypeUnknown NodeType = iota + NodeTypeTable + NodeTypeFunction ) -type edge struct { - label string - node *node - edgeType edgeType - inLoop bool +type Edge struct { + Label string + Node *Node + EdgeType EdgeType + InLoop bool } -type edgeType uint8 +type EdgeType uint8 const ( - edgeTypeUnknown edgeType = iota - edgeTypeInsert - edgeTypeUpdate - edgeTypeDelete - edgeTypeSelect - edgeTypeCall + EdgeTypeUnknown EdgeType = iota + EdgeTypeInsert + EdgeTypeUpdate + EdgeTypeDelete + EdgeTypeSelect + EdgeTypeCall )