import re
from pathlib import Path
from typing import List, Tuple, Dict
def add_execute_with_options_old_pattern(content: str) -> Tuple[str, int]:
count = 0
pattern = r'(pub async fn execute\(self\)\s*->\s*SDKResult<([^>]+)>\s*\{)([^}]*?)(let response = Transport::request\([^)]+,\s*&self\.config,\s+)None(\)\.await\?;[^}]*?response\s*\.\s*data\s*\.\s*ok_or_else\([^}]+\))(\s*\})'
def replace_fn(match):
nonlocal count
count += 1
fn_def_start = match.group(1) response_type = match.group(2) fn_body = match.group(3) transport_start = match.group(4) none_part = match.group(5) response_extract = match.group(6) closing_brace = match.group(7)
context_match = re.search(r'"([^"]*)"', response_extract)
context = context_match.group(1) if context_match else "操作"
new_execute = f'''{fn_def_start}{fn_body} self.execute_with_options(openlark_core::req_option::RequestOption::default())
.await
}}
pub async fn execute_with_options(
self,
option: openlark_core::req_option::RequestOption,
) -> SDKResult<{response_type}> {{
{fn_body} let response = Transport::request({transport_start}Some(option){none_part}
extract_response_data(response, "{context}")
}}'''
return new_execute
new_content = re.sub(pattern, replace_fn, content, flags=re.DOTALL)
return new_content, count
def replace_response_data_extraction(content: str) -> Tuple[str, int]:
count = 0
pattern1 = r'response\s*\.\s*data\s*\.\s*ok_or_else\(\s*\|\|\s*openlark_core::error::validation_error\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"\s*\)\s*\)'
def replace1(match):
nonlocal count
count += 1
return f'extract_response_data(response, "{match.group(1)}")'
new_content = re.sub(pattern1, replace1, content, flags=re.DOTALL)
pattern2 = r'response\s*\.\s*data\s*\.\s*ok_or_else\(\s*\|\|\s*validation_error\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"\s*\)\s*\)'
def replace2(match):
nonlocal count
count += 1
return f'extract_response_data(response, "{match.group(1)}")'
new_content = re.sub(pattern2, replace2, new_content, flags=re.DOTALL)
return new_content, count
def replace_serialize_params(content: str) -> Tuple[str, int]:
count = 0
pattern = r'\.body\(serde_json::to_vec\(&([^)]+)\)\?\)'
def replace_fn(match):
nonlocal count
count += 1
param_name = match.group(1)
return f'.body(serialize_params(&{param_name}, "操作")?)'
new_content = re.sub(pattern, replace_fn, content, flags=re.DOTALL)
return new_content, count
def fix_request_option_none(content: str) -> Tuple[str, int]:
count = 0
pattern = r'pub async fn execute_with_options\([^{]+\{\s*(.*?)(\n\s*pub\s+async\s+fn|\n\}\s*$)'
def replace_fn(match):
nonlocal count
method_body = match.group(1)
old_body = method_body
new_body = re.sub(
r'(Transport::request\([^,]+,\s*&[^,]+,\s+)None(\)',
r'\1Some(option)\2',
method_body
)
if new_body != old_body:
count += 1
return f'pub async fn execute_with_options(\n self,\n option: openlark_core::req_option::RequestOption,\n) -> SDKResult<T> {{\n {new_body}\n{match.group(2)}'
new_content = re.sub(pattern, replace_fn, content, flags=re.DOTALL)
return new_content, count
def remove_builder_struct(content: str) -> Tuple[str, List[str]]:
removed_builders = []
builder_pattern = r'/// [^\n]*\n?pub struct (\w+RequestBuilder)\s*\{.*?^\n\}'
def remove_struct(match):
builder_name = match.group(1)
removed_builders.append(builder_name)
return ''
new_content = re.sub(builder_pattern, remove_struct, content, flags=re.MULTILINE | re.DOTALL)
for builder_name in removed_builders:
impl_pattern = rf'\nimpl {builder_name}\s*\{{.*?^\}}\n?'
new_content = re.sub(impl_pattern, '', new_content, flags=re.MULTILINE | re.DOTALL)
return new_content, removed_builders
def add_api_utils_import(content: str) -> Tuple[str, int]:
needs_utils = 'extract_response_data' in content or 'serialize_params' in content
if not needs_utils:
return content, 0
if 'api_utils' in content:
return content, 0
pattern = r'use crate::common::\{([^}]+)\}'
def add_to_import(match):
existing = match.group(1)
if 'api_utils' not in existing:
return f'use crate::common::{{{existing}, api_utils::*}}'
return match.group(0)
new_content, count = re.subn(pattern, add_to_import, content)
if count == 0:
use_pattern = r'\n(use crate::common::[^\n]+\n)'
new_content, count = re.subn(
use_pattern,
r'\nuse crate::common::api_utils::*;\1',
content,
count=1
)
return new_content, count
def process_file(filepath: str, dry_run: bool = False) -> Dict:
print(f"处理文件: {filepath}")
try:
with open(filepath, 'r', encoding='utf-8') as f:
original_content = f.read()
content = original_content
changes = {
'filepath': filepath,
'execute_added': 0,
'response_fixed': 0,
'serialize_fixed': 0,
'builders_removed': [],
'import_added': 0,
'modified': False
}
content, count = add_execute_with_options_old_pattern(content)
changes['execute_added'] = count
if count > 0:
changes['modified'] = True
content, count = replace_response_data_extraction(content)
changes['response_fixed'] = count
if count > 0:
changes['modified'] = True
content, count = replace_serialize_params(content)
changes['serialize_fixed'] = count
if count > 0:
changes['modified'] = True
content, removed = remove_builder_struct(content)
changes['builders_removed'] = removed
if removed:
changes['modified'] = True
content, count = add_api_utils_import(content)
changes['import_added'] = count
if count > 0:
changes['modified'] = True
if changes['modified'] and not dry_run:
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
print(f" ✓ 已修改: execute({changes['execute_added']}) response({changes['response_fixed']}) serialize({changes['serialize_fixed']}) builders({len(changes['builders_removed'])}) import({changes['import_added']})")
elif changes['modified']:
print(f" [预览] 将修改: execute({changes['execute_added']}) response({changes['response_fixed']}) serialize({changes['serialize_fixed']}) builders({len(changes['builders_removed'])}) import({changes['import_added']})")
else:
print(f" - 无需修改")
return changes
except Exception as e:
print(f" ✗ 错误: {e}")
import traceback
traceback.print_exc()
return {
'filepath': filepath,
'error': str(e),
'modified': False
}
def find_files_to_process(base_path: Path) -> List[str]:
files = []
src_path = base_path / "crates/openlark-docs/src"
for rs_file in src_path.rglob("*.rs"):
if "old" in str(rs_file):
continue
files.append(str(rs_file))
return files
def main():
import argparse
parser = argparse.ArgumentParser(description="综合修复 openlark-docs API 代码规范")
parser.add_argument("--dry-run", action="store_true", help="预览模式,不修改文件")
parser.add_argument("--path", default="crates/openlark-docs/src", help="源代码路径")
args = parser.parse_args()
base_path = Path(args.path).parent.parent.parent
print("=" * 80)
print("综合修复 openlark-docs API 代码规范")
print("=" * 80)
print()
files = find_files_to_process(base_path)
print(f"找到 {len(files)} 个 .rs 文件")
print()
results = []
for filepath in files:
result = process_file(filepath, args.dry_run)
results.append(result)
print()
print("=" * 80)
print("修复总结")
print("=" * 80)
modified_count = sum(1 for r in results if r.get('modified', False))
error_count = sum(1 for r in results if 'error' in r)
print(f"总文件数: {len(results)}")
print(f"已修改: {modified_count}")
print(f"错误: {error_count}")
total_execute = sum(r.get('execute_added', 0) for r in results)
total_response = sum(r.get('response_fixed', 0) for r in results)
total_serialize = sum(r.get('serialize_fixed', 0) for r in results)
total_builders = sum(len(r.get('builders_removed', [])) for r in results)
total_import = sum(r.get('import_added', 0) for r in results)
print(f"\nexecute_with_options 添加: {total_execute}")
print(f"响应提取修复: {total_response}")
print(f"序列化修复: {total_serialize}")
print(f"Builder 删除: {total_builders}")
print(f"导入添加: {total_import}")
if error_count > 0:
print("\n错误的文件:")
for r in results:
if 'error' in r:
print(f" - {r['filepath']}: {r['error']}")
if __name__ == '__main__':
main()