362 lines
10 KiB
Dart
362 lines
10 KiB
Dart
import '../core/config.dart';
|
||
import '../core/exceptions.dart';
|
||
import '../core/models.dart';
|
||
import '../utils/string_utils.dart';
|
||
|
||
/// 代码生成器基类
|
||
/// 定义通用的接口和功能
|
||
abstract class BaseGenerator {
|
||
/// 生成器类型
|
||
String get generatorType;
|
||
|
||
/// 生成代码
|
||
String generate();
|
||
|
||
/// 生成文件头注释
|
||
String generateFileHeader(String description) {
|
||
return StringUtils.generateFileHeader(
|
||
description,
|
||
SwaggerConfig.swaggerJsonUrl,
|
||
);
|
||
}
|
||
|
||
/// 生成类型安全的代码
|
||
String generateTypeCheckedCode(String code) {
|
||
// 基础类型检查和验证
|
||
try {
|
||
if (code.trim().isEmpty) {
|
||
throw CodeGenerationException(
|
||
'生成的代码不能为空',
|
||
generatorType: generatorType,
|
||
);
|
||
}
|
||
|
||
return code;
|
||
} catch (e) {
|
||
throw CodeGenerationException(
|
||
'代码生成失败',
|
||
details: e.toString(),
|
||
generatorType: generatorType,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 验证生成的代码是否符合Dart语法规范
|
||
bool validateDartCode(String code) {
|
||
// 基础验证规则
|
||
final validationRules = [
|
||
// 检查是否有未闭合的大括号
|
||
(String code) => _countMatches(code, '{') == _countMatches(code, '}'),
|
||
// 检查是否有未闭合的小括号
|
||
(String code) => _countMatches(code, '(') == _countMatches(code, ')'),
|
||
// 检查是否有未闭合的方括号
|
||
(String code) => _countMatches(code, '[') == _countMatches(code, ']'),
|
||
// 检查是否有基本的类声明
|
||
(String code) =>
|
||
code.contains('class ') ||
|
||
code.contains('enum ') ||
|
||
code.contains('const '),
|
||
];
|
||
|
||
for (final rule in validationRules) {
|
||
if (!rule(code)) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// 计算字符出现次数
|
||
int _countMatches(String text, String pattern) {
|
||
return pattern.allMatches(text).length;
|
||
}
|
||
}
|
||
|
||
/// 模型代码生成器基类
|
||
abstract class ModelGenerator extends BaseGenerator {
|
||
final SwaggerDocument document;
|
||
final bool useSimpleModels;
|
||
|
||
ModelGenerator(this.document, {this.useSimpleModels = false});
|
||
|
||
@override
|
||
String get generatorType => 'ModelGenerator';
|
||
|
||
/// 生成单个模型的代码
|
||
String generateModelCode(ApiModel model);
|
||
|
||
/// 生成枚举代码
|
||
String generateEnumCode(ApiModel model) {
|
||
if (!model.isEnum) {
|
||
throw CodeGenerationException('模型不是枚举类型', generatorType: generatorType);
|
||
}
|
||
|
||
final className = StringUtils.generateClassName(model.name);
|
||
final enumType = model.enumType?.value ?? 'string';
|
||
final buffer = StringBuffer();
|
||
|
||
// 生成文件头
|
||
buffer.writeln(generateFileHeader('${model.name} 枚举定义'));
|
||
buffer.writeln('');
|
||
|
||
// 生成枚举类
|
||
if (model.description.isNotEmpty) {
|
||
buffer.writeln(StringUtils.generateComment(model.description));
|
||
}
|
||
|
||
buffer.writeln('enum $className {');
|
||
|
||
// 生成枚举值
|
||
for (int i = 0; i < model.enumValues.length; i++) {
|
||
final value = model.enumValues[i];
|
||
final enumName = StringUtils.generateEnumValueName(value, i);
|
||
|
||
if (enumType == 'integer' || enumType == 'number') {
|
||
buffer.writeln(' $enumName($value),');
|
||
} else {
|
||
buffer.writeln(' $enumName(\'$value\'),');
|
||
}
|
||
}
|
||
|
||
// 移除最后一个逗号
|
||
final content = buffer.toString().trimRight();
|
||
buffer.clear();
|
||
buffer.writeln(content.substring(0, content.lastIndexOf(',')));
|
||
buffer.writeln(';');
|
||
buffer.writeln('');
|
||
|
||
// 生成构造函数和方法
|
||
buffer.writeln(' const $className(this.value);');
|
||
buffer.writeln(
|
||
' final ${enumType == 'integer' || enumType == 'number' ? 'int' : 'String'} value;',
|
||
);
|
||
buffer.writeln('');
|
||
|
||
// 生成 fromValue 方法
|
||
buffer.writeln(' static $className fromValue(dynamic value) {');
|
||
buffer.writeln(' for (final enumValue in $className.values) {');
|
||
buffer.writeln(' if (enumValue.value == value) {');
|
||
buffer.writeln(' return enumValue;');
|
||
buffer.writeln(' }');
|
||
buffer.writeln(' }');
|
||
buffer.writeln(' throw ArgumentError(\'Unknown enum value: \$value\');');
|
||
buffer.writeln(' }');
|
||
buffer.writeln('');
|
||
|
||
// 生成 fromJson 方法
|
||
buffer.writeln(' factory $className.fromJson(dynamic json) {');
|
||
buffer.writeln(' return fromValue(json);');
|
||
buffer.writeln(' }');
|
||
buffer.writeln('');
|
||
|
||
// 生成 toJson 方法
|
||
buffer.writeln(' dynamic toJson() => value;');
|
||
buffer.writeln('');
|
||
|
||
buffer.writeln('}');
|
||
|
||
return generateTypeCheckedCode(buffer.toString());
|
||
}
|
||
|
||
// 已移动到 StringUtils.generateEnumValueName
|
||
|
||
/// 获取导入的类型列表
|
||
Set<String> getImportedTypes(ApiModel model) {
|
||
final importedTypes = <String>{};
|
||
|
||
model.properties.forEach((_, property) {
|
||
if (property.type == PropertyType.reference &&
|
||
property.reference != null) {
|
||
importedTypes.add(property.reference!);
|
||
} else if (property.type == PropertyType.array) {
|
||
// 处理数组类型的引用
|
||
if (property.items != null) {
|
||
final itemType = _getItemType(property.items!);
|
||
// 如果是引用类型(不是基本类型),则添加到导入列表
|
||
if (itemType != 'String' &&
|
||
itemType != 'int' &&
|
||
itemType != 'double' &&
|
||
itemType != 'bool' &&
|
||
itemType != 'dynamic') {
|
||
importedTypes.add(property.items!.name);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
return importedTypes;
|
||
}
|
||
|
||
/// 生成属性的Dart类型
|
||
String getDartPropertyType(ApiProperty property) {
|
||
switch (property.type) {
|
||
case PropertyType.string:
|
||
switch (property.format) {
|
||
case 'date':
|
||
case 'date-time':
|
||
return 'DateTime';
|
||
default:
|
||
return 'String';
|
||
}
|
||
case PropertyType.integer:
|
||
return 'int';
|
||
case PropertyType.number:
|
||
return 'double';
|
||
case PropertyType.boolean:
|
||
return 'bool';
|
||
case PropertyType.array:
|
||
// 根据数组元素类型推导具体类型
|
||
if (property.items != null) {
|
||
final itemType = _getItemType(property.items!);
|
||
return 'List<$itemType>';
|
||
}
|
||
return 'List<dynamic>';
|
||
case PropertyType.object:
|
||
return 'Map<String, dynamic>';
|
||
case PropertyType.reference:
|
||
return property.reference != null
|
||
? StringUtils.generateClassName(property.reference!)
|
||
: 'dynamic';
|
||
default:
|
||
return 'dynamic';
|
||
}
|
||
}
|
||
|
||
/// 获取数组元素的类型
|
||
String _getItemType(ApiModel items) {
|
||
// 如果是引用类型,直接返回类名
|
||
if (items.name != 'string' &&
|
||
items.name != 'integer' &&
|
||
items.name != 'number' &&
|
||
items.name != 'boolean') {
|
||
return StringUtils.generateClassName(items.name);
|
||
}
|
||
|
||
// 如果是基本类型,转换为对应的Dart类型
|
||
switch (items.name) {
|
||
case 'string':
|
||
return 'String';
|
||
case 'integer':
|
||
return 'int';
|
||
case 'number':
|
||
return 'double';
|
||
case 'boolean':
|
||
return 'bool';
|
||
default:
|
||
return 'dynamic';
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 选项配置类
|
||
class GeneratorOptions {
|
||
final bool generateEndpoints;
|
||
final bool generateModels;
|
||
final bool generateDocs;
|
||
final bool useSimpleModels;
|
||
final bool separateModelFiles;
|
||
final String modelsDirectory;
|
||
final String outputDirectory;
|
||
final String endpointsFileName;
|
||
final String docsFileName;
|
||
|
||
const GeneratorOptions({
|
||
this.generateEndpoints = true,
|
||
this.generateModels = true,
|
||
this.generateDocs = true,
|
||
this.useSimpleModels = false,
|
||
this.separateModelFiles = true,
|
||
this.modelsDirectory = 'models',
|
||
this.outputDirectory = 'generator',
|
||
this.endpointsFileName = 'api_paths.dart',
|
||
this.docsFileName = 'api_documentation.md',
|
||
});
|
||
|
||
/// 从命令行参数创建选项
|
||
factory GeneratorOptions.fromArgs(List<String> args) {
|
||
bool generateEndpoints = false;
|
||
bool generateModels = false;
|
||
bool generateDocs = false;
|
||
bool useSimpleModels = false;
|
||
const bool separateModelFiles = true;
|
||
String modelsDirectory = 'models';
|
||
String outputDirectory = 'generator';
|
||
String endpointsFileName = 'api_paths.dart';
|
||
String docsFileName = 'api_documentation.md';
|
||
|
||
bool hasSpecificOption = false;
|
||
|
||
for (int i = 0; i < args.length; i++) {
|
||
final arg = args[i];
|
||
|
||
switch (arg) {
|
||
case '--endpoints':
|
||
generateEndpoints = true;
|
||
hasSpecificOption = true;
|
||
break;
|
||
case '--models':
|
||
generateModels = true;
|
||
hasSpecificOption = true;
|
||
break;
|
||
case '--docs':
|
||
generateDocs = true;
|
||
hasSpecificOption = true;
|
||
break;
|
||
case '--all':
|
||
generateEndpoints = true;
|
||
generateModels = true;
|
||
generateDocs = true;
|
||
hasSpecificOption = true;
|
||
break;
|
||
case '--simple':
|
||
useSimpleModels = true;
|
||
break;
|
||
case '--models-dir':
|
||
if (i + 1 < args.length) {
|
||
modelsDirectory = args[i + 1];
|
||
i++; // 跳过下一个参数
|
||
}
|
||
break;
|
||
case '--output-dir':
|
||
if (i + 1 < args.length) {
|
||
outputDirectory = args[i + 1];
|
||
i++; // 跳过下一个参数
|
||
}
|
||
break;
|
||
case '--endpoints-file':
|
||
if (i + 1 < args.length) {
|
||
endpointsFileName = args[i + 1];
|
||
i++; // 跳过下一个参数
|
||
}
|
||
break;
|
||
case '--docs-file':
|
||
if (i + 1 < args.length) {
|
||
docsFileName = args[i + 1];
|
||
i++; // 跳过下一个参数
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有指定特定选项,默认生成所有文件
|
||
if (!hasSpecificOption) {
|
||
generateEndpoints = true;
|
||
generateModels = true;
|
||
generateDocs = true;
|
||
}
|
||
|
||
return GeneratorOptions(
|
||
generateEndpoints: generateEndpoints,
|
||
generateModels: generateModels,
|
||
generateDocs: generateDocs,
|
||
useSimpleModels: useSimpleModels,
|
||
separateModelFiles: separateModelFiles,
|
||
modelsDirectory: modelsDirectory,
|
||
outputDirectory: outputDirectory,
|
||
endpointsFileName: endpointsFileName,
|
||
docsFileName: docsFileName,
|
||
);
|
||
}
|
||
}
|