"""目录扫描模块。 扫描项目根目录下按约定的子目录结构,识别 ABAP 开发对象。 """ from __future__ import annotations import logging import os from dataclasses import dataclass logger = logging.getLogger("sapcli.scanner") DIRECTORY_TYPE_MAP: dict[str, str] = { "reports": "report", "classes": "class", "interfaces": "interface", "functions": "function", "domains": "domain", "dataelements": "dataelement", "tables": "table", "structures": "structure", "tabletypes": "tabletype", "includes": "include", "cdsviews": "cdsview", "messageclasses": "messageclass", "views": "view", "searchhelps": "searchhelp", "lockobjects": "lockobject", } FUNCTIONS_DIR = "functions" @dataclass class ScannedObject: """扫描到的对象。""" name: str # 大写对象名,function 类型含 / (GROUP/FUNC) type: str # 对象类型 file: str # 相对于项目根目录的路径 def scan_project(project_path: str) -> list[ScannedObject]: """扫描项目目录,返回识别到的所有对象列表。 遍历已知目录名,每个目录下找 .abap 文件。 functions/ 特殊处理:子目录 = 函数组名。 """ results: list[ScannedObject] = [] for dir_name, obj_type in DIRECTORY_TYPE_MAP.items(): dir_path = os.path.join(project_path, dir_name) if not os.path.isdir(dir_path): logger.debug("目录不存在,跳过: %s", dir_path) continue if dir_name == FUNCTIONS_DIR: _scan_functions(dir_path, results) else: _scan_flat_directory(dir_path, dir_name, obj_type, results) logger.info("扫描完成: %d 个对象", len(results)) return results def _scan_flat_directory( dir_path: str, dir_name: str, obj_type: str, results: list[ScannedObject], ) -> None: """扫描普通目录(一对一:文件名 = 对象名)。""" for filename in sorted(os.listdir(dir_path)): if not filename.endswith(".abap"): continue # 隐藏文件跳过 if filename.startswith("."): continue obj_name = filename[:-5].upper() # 去掉 .abap,转大写 rel_path = f"{dir_name}/{filename}" results.append(ScannedObject( name=obj_name, type=obj_type, file=rel_path, )) logger.debug("扫描到: %s (%s) → %s", obj_name, obj_type, rel_path) def _scan_functions(dir_path: str, results: list[ScannedObject]) -> None: """扫描 functions/ 目录(特殊处理:子目录 = 函数组名)。 目录结构: functions/{组名小写}/{模块名小写}.abap 对象名: {组名大写}/{模块名大写} """ for group_dir_name in sorted(os.listdir(dir_path)): group_dir = os.path.join(dir_path, group_dir_name) if not os.path.isdir(group_dir): continue if group_dir_name.startswith("."): continue group_name = group_dir_name.upper() for filename in sorted(os.listdir(group_dir)): if not filename.endswith(".abap"): continue if filename.startswith("."): continue func_name = filename[:-5].upper() obj_name = f"{group_name}/{func_name}" rel_path = f"{FUNCTIONS_DIR}/{group_dir_name}/{filename}" results.append(ScannedObject( name=obj_name, type="function", file=rel_path, )) logger.debug("扫描到: %s (function) → %s", obj_name, rel_path)