Files
gochat/scripts/scan_route_conflicts.py
T
2026-06-04 15:44:48 +08:00

92 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""
Scan router.go for Gin radix tree param name conflicts.
Gin requires all routes at the same URL position to use the same param name.
Strategy: Build a prefix tree, find all positions where multiple param names are used.
"""
import re
with open('/home/yanghao05/Workspace/gochat/internal/router/router.go') as f:
lines = f.readlines()
# Track Group nesting to build full paths
group_stack = [] # list of (var_name, prefix_path)
routes = []
# Parse the file line by line
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith('//') or not stripped:
continue
# Detect Group definition: varName := parent.Group("/path")
gmatch = re.match(r'(\w+)\s*:?=\s*(\w+)\.Group\("([^"]+)"\)', stripped)
if gmatch:
var_name = gmatch.group(1)
parent = gmatch.group(2)
path = gmatch.group(3)
# Find parent prefix
parent_prefix = ''
for name, prefix in group_stack:
if name == parent:
parent_prefix = prefix
break
full_prefix = parent_prefix + path
group_stack.append((var_name, full_prefix))
continue
# Detect route registration: var.Method("/path", handler)
for method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']:
pattern = rf'^(\w+)\.{method}\("([^"]+)"\s*,'
rmatch = re.match(pattern, stripped)
if rmatch:
var_name = rmatch.group(1)
path = rmatch.group(2)
# Find prefix for this group variable
prefix = ''
for name, p in group_stack:
if name == var_name:
prefix = p
break
full_path = prefix + path
routes.append((method, full_path, i+1))
break
# Now build a radix-tree-like structure to find conflicts
# At each path segment position, track all param names seen
tree = {} # key: tuple of segments up to a position -> set of param names at that position
for method, path, linenum in routes:
segments = [s for s in path.split('/') if s] # skip empty segments
for pos, seg in enumerate(segments):
if seg.startswith(':'):
param_name = seg[1:]
prefix_key = '/'.join(segments[:pos])
if prefix_key not in tree:
tree[prefix_key] = {}
if pos not in tree[prefix_key]:
tree[prefix_key][pos] = {}
if param_name not in tree[prefix_key][pos]:
tree[prefix_key][pos][param_name] = []
tree[prefix_key][pos][param_name].append((method, path, linenum))
# Find conflicts
print("=== Gin路由参数名冲突检测 ===\n")
conflicts_found = 0
for prefix, positions in sorted(tree.items()):
for pos, names_dict in positions.items():
if len(names_dict) > 1:
names = sorted(names_dict.keys())
conflicts_found += 1
print(f"冲突 #{conflicts_found}: 前缀 '{prefix}' 位置 {pos}")
print(f" 参数名: {names}")
for name, refs in names_dict.items():
print(f" :{name} 的使用:")
for method, path, linenum in refs[:3]:
print(f" Line {linenum}: {method} {path}")
if len(refs) > 3:
print(f" ... (还有 {len(refs)-3} 条)")
print()
print(f"总计冲突: {conflicts_found}")