350 lines
8.9 KiB
Dart
350 lines
8.9 KiB
Dart
import 'package:swagger_generator_flutter/core/exceptions.dart';
|
||
import 'package:swagger_generator_flutter/utils/logger.dart';
|
||
|
||
/// 命令基类
|
||
/// 实现命令模式,提供统一的命令接口
|
||
abstract class BaseCommand {
|
||
/// 命令名称
|
||
String get name;
|
||
|
||
/// 命令描述
|
||
String get description;
|
||
|
||
/// 命令用法
|
||
String get usage;
|
||
|
||
/// 支持的选项
|
||
List<CommandOption> get options => [];
|
||
|
||
/// 支持的参数
|
||
List<CommandArgument> get arguments => [];
|
||
|
||
/// 执行命令
|
||
Future<int> execute(List<String> args);
|
||
|
||
/// 显示帮助信息
|
||
void showHelp() {
|
||
final buffer = StringBuffer()
|
||
..writeln()
|
||
..writeln('命令: $name')
|
||
..writeln('描述: $description')
|
||
..writeln('用法: $usage');
|
||
|
||
if (arguments.isNotEmpty) {
|
||
buffer
|
||
..writeln()
|
||
..writeln('参数:');
|
||
for (final arg in arguments) {
|
||
final required = arg.required ? '(必填)' : '(可选)';
|
||
buffer.writeln(' ${arg.name} ${arg.description} $required');
|
||
}
|
||
}
|
||
|
||
if (options.isNotEmpty) {
|
||
buffer
|
||
..writeln()
|
||
..writeln('选项:');
|
||
for (final option in options) {
|
||
final short = option.shortName != null ? '-${option.shortName}, ' : '';
|
||
final defaultValue =
|
||
option.defaultValue != null ? ' (默认: ${option.defaultValue})' : '';
|
||
buffer.writeln(
|
||
' $short--${option.name} ${option.description}$defaultValue',
|
||
);
|
||
}
|
||
}
|
||
|
||
buffer.writeln();
|
||
appLogger.info(buffer.toString());
|
||
}
|
||
|
||
/// 解析命令行参数
|
||
ParsedArguments parseArguments(List<String> args) {
|
||
final parser = ArgumentParser(this);
|
||
return parser.parse(args);
|
||
}
|
||
|
||
/// 验证参数
|
||
void validateArguments(ParsedArguments parsedArgs) {
|
||
// 验证必填参数
|
||
for (final arg in arguments.where((a) => a.required)) {
|
||
if (!parsedArgs.hasArgument(arg.name)) {
|
||
throw CommandException('缺少必填参数: ${arg.name}');
|
||
}
|
||
}
|
||
|
||
// 验证必填选项
|
||
for (final option in options.where((o) => o.required)) {
|
||
if (!parsedArgs.hasOption(option.name)) {
|
||
throw CommandException('缺少必填选项: --${option.name}');
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 处理执行前的准备工作
|
||
Future<void> prepare(ParsedArguments parsedArgs) async {
|
||
// 子类可以重写此方法进行准备工作
|
||
}
|
||
|
||
/// 处理执行后的清理工作
|
||
Future<void> cleanup(ParsedArguments parsedArgs) async {
|
||
// 子类可以重写此方法进行清理工作
|
||
}
|
||
|
||
/// 错误处理
|
||
void handleError(dynamic error, StackTrace stackTrace) {
|
||
if (error is CommandException) {
|
||
appLogger.severe(
|
||
'❌ 错误: ${error.message}',
|
||
error.details,
|
||
stackTrace,
|
||
);
|
||
} else if (error is SwaggerException) {
|
||
appLogger.severe(
|
||
'❌ Swagger错误: ${error.message}',
|
||
error.details,
|
||
stackTrace,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 成功消息
|
||
void success(String message) => appLogger.info('✅ $message');
|
||
|
||
/// 信息消息
|
||
void info(String message) => appLogger.info('ℹ️ $message');
|
||
|
||
/// 警告消息
|
||
void warning(String message) => appLogger.warning('⚠️ $message');
|
||
|
||
/// 进度消息
|
||
void progress(String message) => appLogger.info('🔄 $message');
|
||
}
|
||
|
||
/// 命令选项
|
||
class CommandOption {
|
||
const CommandOption({
|
||
required this.name,
|
||
required this.description,
|
||
this.shortName,
|
||
this.required = false,
|
||
this.defaultValue,
|
||
this.type = OptionType.flag,
|
||
});
|
||
final String name;
|
||
final String? shortName;
|
||
final String description;
|
||
final bool required;
|
||
final dynamic defaultValue;
|
||
final OptionType type;
|
||
}
|
||
|
||
/// 命令参数
|
||
class CommandArgument {
|
||
const CommandArgument({
|
||
required this.name,
|
||
required this.description,
|
||
this.required = true,
|
||
this.defaultValue,
|
||
});
|
||
final String name;
|
||
final String description;
|
||
final bool required;
|
||
final dynamic defaultValue;
|
||
}
|
||
|
||
/// 选项类型
|
||
enum OptionType { flag, string, int, double, list }
|
||
|
||
/// 解析后的参数
|
||
class ParsedArguments {
|
||
final Map<String, dynamic> _options = {};
|
||
final Map<String, dynamic> _arguments = {};
|
||
|
||
/// 设置选项值
|
||
void setOption(String name, dynamic value) {
|
||
_options[name] = value;
|
||
}
|
||
|
||
/// 设置参数值
|
||
void setArgument(String name, dynamic value) {
|
||
_arguments[name] = value;
|
||
}
|
||
|
||
/// 获取选项值
|
||
T? getOption<T>(String name) {
|
||
return _options[name] as T?;
|
||
}
|
||
|
||
/// 获取参数值
|
||
T? getArgument<T>(String name) {
|
||
return _arguments[name] as T?;
|
||
}
|
||
|
||
/// 检查是否有选项
|
||
bool hasOption(String name) {
|
||
return _options.containsKey(name);
|
||
}
|
||
|
||
/// 检查是否有参数
|
||
bool hasArgument(String name) {
|
||
return _arguments.containsKey(name);
|
||
}
|
||
|
||
/// 获取所有选项
|
||
Map<String, dynamic> get options => Map.unmodifiable(_options);
|
||
|
||
/// 获取所有参数
|
||
Map<String, dynamic> get arguments => Map.unmodifiable(_arguments);
|
||
}
|
||
|
||
/// 参数解析器
|
||
class ArgumentParser {
|
||
ArgumentParser(this.command);
|
||
final BaseCommand command;
|
||
|
||
/// 解析参数
|
||
ParsedArguments parse(List<String> args) {
|
||
final result = ParsedArguments();
|
||
final argQueue = List<String>.from(args);
|
||
|
||
var argumentIndex = 0;
|
||
|
||
while (argQueue.isNotEmpty) {
|
||
final current = argQueue.removeAt(0);
|
||
|
||
if (current.startsWith('--')) {
|
||
// 长选项
|
||
_parseLongOption(current, argQueue, result);
|
||
} else if (current.startsWith('-') && current.length > 1) {
|
||
// 短选项
|
||
_parseShortOption(current, argQueue, result);
|
||
} else {
|
||
// 位置参数
|
||
if (argumentIndex < command.arguments.length) {
|
||
final arg = command.arguments[argumentIndex];
|
||
result.setArgument(arg.name, current);
|
||
argumentIndex++;
|
||
} else {
|
||
throw CommandException('未知参数: $current');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设置默认值
|
||
_setDefaultValues(result);
|
||
|
||
return result;
|
||
}
|
||
|
||
/// 解析长选项
|
||
void _parseLongOption(
|
||
String current,
|
||
List<String> argQueue,
|
||
ParsedArguments result,
|
||
) {
|
||
String optionName;
|
||
String? optionValue;
|
||
|
||
if (current.contains('=')) {
|
||
final parts = current.split('=');
|
||
optionName = parts[0].substring(2);
|
||
optionValue = parts.sublist(1).join('=');
|
||
} else {
|
||
optionName = current.substring(2);
|
||
}
|
||
|
||
final option = command.options.firstWhere(
|
||
(o) => o.name == optionName,
|
||
orElse: () => throw CommandException('未知选项: --$optionName'),
|
||
);
|
||
|
||
if (option.type == OptionType.flag) {
|
||
result.setOption(optionName, true);
|
||
} else {
|
||
if (optionValue == null) {
|
||
if (argQueue.isEmpty) {
|
||
throw CommandException('选项 --$optionName 需要一个值');
|
||
}
|
||
optionValue = argQueue.removeAt(0);
|
||
}
|
||
|
||
final convertedValue = _convertValue(optionValue, option.type);
|
||
result.setOption(optionName, convertedValue);
|
||
}
|
||
}
|
||
|
||
/// 解析短选项
|
||
void _parseShortOption(
|
||
String current,
|
||
List<String> argQueue,
|
||
ParsedArguments result,
|
||
) {
|
||
final shortName = current.substring(1);
|
||
|
||
final option = command.options.firstWhere(
|
||
(o) => o.shortName == shortName,
|
||
orElse: () => throw CommandException('未知选项: -$shortName'),
|
||
);
|
||
|
||
if (option.type == OptionType.flag) {
|
||
result.setOption(option.name, true);
|
||
} else {
|
||
if (argQueue.isEmpty) {
|
||
throw CommandException('选项 -$shortName 需要一个值');
|
||
}
|
||
|
||
final optionValue = argQueue.removeAt(0);
|
||
final convertedValue = _convertValue(optionValue, option.type);
|
||
result.setOption(option.name, convertedValue);
|
||
}
|
||
}
|
||
|
||
/// 转换值类型
|
||
dynamic _convertValue(String value, OptionType type) {
|
||
switch (type) {
|
||
case OptionType.string:
|
||
return value;
|
||
case OptionType.int:
|
||
return int.tryParse(value) ??
|
||
(throw CommandException('无效的整数值: $value'));
|
||
case OptionType.double:
|
||
return double.tryParse(value) ??
|
||
(throw CommandException('无效的浮点数值: $value'));
|
||
case OptionType.list:
|
||
return value.split(',').map((s) => s.trim()).toList();
|
||
case OptionType.flag:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/// 设置默认值
|
||
void _setDefaultValues(ParsedArguments result) {
|
||
// 设置选项默认值
|
||
for (final option in command.options) {
|
||
if (!result.hasOption(option.name) && option.defaultValue != null) {
|
||
result.setOption(option.name, option.defaultValue);
|
||
}
|
||
}
|
||
|
||
// 设置参数默认值
|
||
for (final argument in command.arguments) {
|
||
if (!result.hasArgument(argument.name) && argument.defaultValue != null) {
|
||
result.setArgument(argument.name, argument.defaultValue);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 命令异常
|
||
class CommandException implements Exception {
|
||
const CommandException(this.message, {this.details});
|
||
final String message;
|
||
final String? details;
|
||
|
||
@override
|
||
String toString() {
|
||
return 'CommandException: $message${details != null ? ' ($details)' : ''}';
|
||
}
|
||
}
|