/// 字符串工具类 /// /// 提供常用的字符串处理功能,包括命名风格转换、注释生成等。 /// /// # 典型用法示例 /// ```dart /// // snake_case 转 camelCase /// StringUtils.toDartPropertyName('user_id'); // userId /// // 含特殊字符 /// StringUtils.toDartPropertyName('user-id'); // userId /// // 数字开头 /// StringUtils.toDartPropertyName('1st_field'); // n1stField /// // 空字符串 /// StringUtils.toDartPropertyName(''); // property /// ``` /// /// This utility provides string conversion helpers for code generation, such as /// converting snake_case to camelCase, generating Dart class names, and /// cleaning descriptions. /// library; import 'package:swagger_generator_flutter/core/config_loader.dart'; import 'package:swagger_generator_flutter/core/models.dart'; class StringUtils { /// 转换为camelCase static String toCamelCase(String input) { if (input.isEmpty) return input; // 如果输入已经是camelCase格式(首字母小写),直接返回 if (RegExp(r'^[a-z][a-zA-Z0-9]*$').hasMatch(input)) { return input; } // 如果输入是PascalCase格式(首字母大写),转换为camelCase if (RegExp(r'^[A-Z][a-zA-Z0-9]*$').hasMatch(input)) { return input[0].toLowerCase() + input.substring(1); } // 处理下划线分隔的字符串 final parts = input.split('_').where((p) => p.isNotEmpty).toList(); if (parts.isEmpty) return input; var result = parts.first.toLowerCase(); for (var i = 1; i < parts.length; i++) { final part = parts[i]; if (part.isNotEmpty) { result += part[0].toUpperCase() + part.substring(1).toLowerCase(); } } return result.isEmpty ? input : result; } /// 转换为PascalCase static String toPascalCase(String input) { if (input.isEmpty) return input; // 如果输入包含下划线,先按下划线分割并转换每个部分 if (input.contains('_')) { final parts = input.split('_'); var result = ''; for (final part in parts) { if (part.isNotEmpty) { // 保持每个部分的原始大小写,只确保首字母大写 if (part[0].toUpperCase() == part[0]) { // 如果首字母已经是大写,保持整个部分不变 result += part; } else { // 如果首字母是小写,只转换首字母为大写 result += part[0].toUpperCase() + part.substring(1); } } } return result.isEmpty ? input : result; } // 如果输入已经是PascalCase格式(没有下划线且首字母大写),直接返回 if (input[0].toUpperCase() == input[0]) { return input; } // 如果输入是camelCase格式(没有下划线),转换首字母为大写 if (RegExp(r'^[a-zA-Z][a-zA-Z0-9]*$').hasMatch(input)) { return input[0].toUpperCase() + input.substring(1); } return input; } /// 转换为snake_case static String toSnakeCase(String input) { if (input.isEmpty) return input; // 处理PascalCase和camelCase final result = input.replaceAllMapped(RegExp('([A-Z]+)([A-Z][a-z])'), (match) { return '${match[1]!.substring(0, match[1]!.length - 1)}_${match[2]}'; }).replaceAllMapped(RegExp(r'([a-z\d])([A-Z])'), (match) { return '${match[1]}_${match[2]}'; }).toLowerCase(); return result; } /// 转换为符合Dart命名规范的属性名 /// /// - 支持 snake_case、kebab-case、空格、特殊字符自动转为 camelCase /// - 已经是驼峰命名的字符串保持不变 /// - PascalCase(首字母大写)转换为camelCase(首字母小写) /// - 数字开头自动加前缀 n /// - 空字符串返回 'property' /// /// # 示例 /// ```dart /// StringUtils.toDartPropertyName('user_id'); // userId /// StringUtils.toDartPropertyName('user-id'); // userId /// StringUtils.toDartPropertyName('1st_field'); // n1stField /// StringUtils.toDartPropertyName('classCadreId'); // classCadreId /// StringUtils.toDartPropertyName('PageIndex'); // pageIndex /// StringUtils.toDartPropertyName(''); // property /// ``` static String toDartPropertyName(String propName) { // 如果已经是camelCase命名(没有下划线且首字母小写),直接返回 if (RegExp(r'^[a-z][a-zA-Z0-9]*$').hasMatch(propName)) { return propName; } // PascalCase 直接转 camelCase if (RegExp(r'^[A-Z][a-zA-Z0-9]*$').hasMatch(propName)) { return propName[0].toLowerCase() + propName.substring(1); } // 处理特殊字符和数字开头的情况 var result = propName; // 如果以数字开头,添加前缀 if (RegExp('^[0-9]').hasMatch(result)) { result = 'n$result'; } // 替换特殊字符为下划线 result = result.replaceAll(RegExp('[^a-zA-Z0-9_]'), '_'); // 转换为camelCase result = toCamelCase(result); // 确保不为空 if (result.isEmpty) { result = 'property'; } return result; } /// 清理描述文本,移除特殊字符和格式化多行注释 static String cleanDescription(String description) { if (description.isEmpty) return description; // 移除多余的空白字符和换行符 var cleaned = description .replaceAll(RegExp(r'\s+'), ' ') .replaceAll(RegExp(r'[\r\n]+'), ' ') .trim(); // 移除可能引起语法错误的特殊字符 cleaned = cleaned .replaceAll(RegExp(r'[^\w\s\u4e00-\u9fa5,,.。::;;!!??_/\\-]'), '') .replaceAll(RegExp(r'\s+'), ' ') .trim(); // 如果描述过长,截取前200个字符 if (cleaned.length > 200) { cleaned = '${cleaned.substring(0, 200)}...'; } return cleaned; } /// 生成端点名称 static String generateEndpointName(String path, String? operationId) { // 如果有operationId,优先使用 if (operationId != null && operationId.isNotEmpty) { return toCamelCase(operationId); } // 移除路径中的版本前缀 var cleanPath = path.replaceFirst('/api/v1', ''); // 移除开头的斜杠 if (cleanPath.startsWith('/')) { cleanPath = cleanPath.substring(1); } // 将路径转换为camelCase final parts = cleanPath.split('/'); if (parts.length >= 2) { final controller = parts[0]; final action = parts[1]; // 转换为camelCase return toCamelCase('${controller}_$action'); } return toCamelCase(cleanPath.replaceAll('/', '_')); } /// 生成Dart类名 static String generateClassName(String name) { // 确保类名以大写字母开头 final cleanName = name.replaceAll(RegExp('[^a-zA-Z0-9_]'), '_'); return toPascalCase(cleanName); } /// 生成常量名称 (UPPER_SNAKE_CASE) static String generateConstantName(String name) { // 清理特殊字符 final cleanName = name.replaceAll(RegExp('[^a-zA-Z0-9_]'), '_'); // 转换为 snake_case 然后转为大写 return toSnakeCase(cleanName).toUpperCase(); } /// 生成文件名 static String generateFileName(String name) { // 转换为snake_case并添加.dart扩展名 return '${toSnakeCase(name)}.dart'; } /// 验证是否为有效的Dart标识符 static bool isValidDartIdentifier(String identifier) { if (identifier.isEmpty) return false; // Dart标识符规则:以字母或下划线开头,后面可以是字母、数字或下划线 final regex = RegExp(r'^[a-zA-Z_][a-zA-Z0-9_]*$'); return regex.hasMatch(identifier); } /// 生成枚举值名称 static String generateEnumValueName(dynamic value, int index) { if (value is String) { // 尝试从字符串生成合法的枚举名 final cleanValue = value.replaceAll(RegExp('[^a-zA-Z0-9_]'), ''); if (cleanValue.isNotEmpty && isValidDartIdentifier(cleanValue)) { return toCamelCase(cleanValue); } } // 默认使用 value + 索引 return 'value${index + 1}'; } /// 提取控制器名称 static String extractControllerName(ApiPath path) { // 从tags中提取控制器名称 if (path.tags.isNotEmpty) { return path.tags.first; } // 从路径中提取控制器名称 final pathParts = path.path.split('/'); if (pathParts.length > 1) { return toPascalCase(pathParts[1]); } return 'General'; } /// 清理路径,保留版本前缀 static String cleanPath(String path) { // 保留版本前缀,只清理其他不必要的字符 return path; } /// 格式化字节大小 static String formatBytes(int bytes) { if (bytes < 1024) return '${bytes}B'; if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB'; if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB'; } return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB'; } /// 格式化持续时间 static String formatDuration(Duration duration) { if (duration.inMilliseconds >= 1000) { return '${(duration.inMilliseconds / 1000).toStringAsFixed(2)}秒'; } else { return '${duration.inMilliseconds}毫秒'; } } /// 转义字符串中的特殊字符 String escapeString(String input) { return input .replaceAll(r'\', r'\\') .replaceAll('"', r'\"') .replaceAll('\n', r'\n') .replaceAll('\r', r'\r') .replaceAll('\t', r'\t'); } /// 缩进文本 String indent(String text, int spaces) { final indentation = ' ' * spaces; return text.split('\n').map((line) => '$indentation$line').join('\n'); } /// 生成注释块 static String generateComment(String text, {bool isBlock = false}) { if (text.isEmpty) return ''; final cleanText = cleanDescription(text); if (isBlock) { return '/**\n * $cleanText\n */'; } else { return '/// $cleanText'; } } /// pluralize 单词 String pluralize(String word) { if (word.isEmpty) return word; final lowerWord = word.toLowerCase(); // 特殊复数形式 const irregularPlurals = { 'child': 'children', 'person': 'people', 'man': 'men', 'woman': 'women', 'mouse': 'mice', 'goose': 'geese', }; if (irregularPlurals.containsKey(lowerWord)) { return irregularPlurals[lowerWord]!; } // 规则复数形式 if (lowerWord.endsWith('y')) { return '${word.substring(0, word.length - 1)}ies'; } else if (lowerWord.endsWith('s') || lowerWord.endsWith('sh') || lowerWord.endsWith('ch') || lowerWord.endsWith('x') || lowerWord.endsWith('z')) { return '${word}es'; } else { return '${word}s'; } } /// 生成文件头注释 /// [description] 文件描述 /// [source] Swagger 文档源(URL 或文件路径) /// [fileName] 文件名(可选) /// [fileType] 文件类型(可选,如 "API 接口定义"、"模型定义") static String generateFileHeader( String description, String source, { String? fileName, String? fileType, }) { // 尝试从配置读取文件头模板 final template = ConfigLoader.getFileHeaderTemplate(); if (template != null && template.isNotEmpty) { // 使用配置的模板 return _applyFileHeaderTemplate( template, description: description, source: source, fileName: fileName ?? '', fileType: fileType ?? description, generatorName: ConfigLoader.getGeneratorName(), author: ConfigLoader.getAuthor(), copyright: ConfigLoader.getCopyright(), ); } // 使用默认模板(从配置读取生成器信息) final generatorName = ConfigLoader.getGeneratorName(); final author = ConfigLoader.getAuthor(); final copyright = ConfigLoader.getCopyright(); return ''' // $description // 由 $generatorName by $author 生成 // $copyright '''; } /// 应用文件头模板 /// 支持变量: {fileName}, {fileType}, {swaggerUrl}, {generatorName}, {author}, /// {copyright} static String _applyFileHeaderTemplate( String template, { required String description, required String source, required String fileName, required String fileType, required String generatorName, required String author, required String copyright, }) { var result = template; // 替换模板变量 result = result.replaceAll('{fileName}', fileName); result = result.replaceAll('{fileType}', fileType); result = result.replaceAll('{swaggerUrl}', source); result = result.replaceAll('{generatorName}', generatorName); result = result.replaceAll('{author}', author); result = result.replaceAll('{copyright}', copyright); // 如果模板中没有这些变量,使用默认值 if (!result.contains('//')) { // 如果模板格式不正确,添加默认格式 result = '// $description\n$result'; } return result; } }