swagger_generator_flutter/lib/commands/base_command.dart

350 lines
8.9 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)' : ''}';
}
}