Compare commits
10 commits
a4beca5a4c
...
1007479792
Author | SHA1 | Date | |
---|---|---|---|
|
1007479792 | ||
|
7bf27e2e59 | ||
|
23c9e2b107 | ||
|
16c1047bfe | ||
|
83fd901968 | ||
|
601a5e6210 | ||
|
cb16079d14 | ||
|
a0d5f68fad | ||
|
b42e1daa89 | ||
|
df19fbea36 |
103 changed files with 5185 additions and 416 deletions
|
@ -3,6 +3,7 @@ repository: https://github.com/protevus/platform
|
|||
packages:
|
||||
- apps/**
|
||||
- packages/**
|
||||
- helpers/tools/**
|
||||
- examples/**
|
||||
|
||||
command:
|
||||
|
|
16
devbox.json
16
devbox.json
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.11.0/.schema/devbox.schema.json",
|
||||
"packages": [
|
||||
"dart@latest"
|
||||
],
|
||||
"shell": {
|
||||
"init_hook": [
|
||||
"echo 'Welcome to Protevus!' > /dev/null"
|
||||
],
|
||||
"scripts": {
|
||||
"test": [
|
||||
"echo \"Error: no test specified\" && exit 1"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
53
devbox.lock
53
devbox.lock
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"lockfile_version": "1",
|
||||
"packages": {
|
||||
"dart@latest": {
|
||||
"last_modified": "2024-06-03T07:19:07Z",
|
||||
"resolved": "github:NixOS/nixpkgs/4a4ecb0ab415c9fccfb005567a215e6a9564cdf5#dart",
|
||||
"source": "devbox-search",
|
||||
"version": "3.4.2",
|
||||
"systems": {
|
||||
"aarch64-darwin": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/9piqr817cdsgmz31m8q723lxhcpgqsa4-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/9piqr817cdsgmz31m8q723lxhcpgqsa4-dart-3.4.2"
|
||||
},
|
||||
"aarch64-linux": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/1j3h5yqxvgzakv5gir1ssg7wggwxhmsd-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/1j3h5yqxvgzakv5gir1ssg7wggwxhmsd-dart-3.4.2"
|
||||
},
|
||||
"x86_64-darwin": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/k8a6gkss3s19p5dhbzgbdqqk5b8qzd7d-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/k8a6gkss3s19p5dhbzgbdqqk5b8qzd7d-dart-3.4.2"
|
||||
},
|
||||
"x86_64-linux": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/wbj1csi5fk2w99aiglwgg1mv406pw4pn-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/wbj1csi5fk2w99aiglwgg1mv406pw4pn-dart-3.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
helpers/install_code_extensions.sh
Executable file
53
helpers/install_code_extensions.sh
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Array of extensions to install
|
||||
extensions=(
|
||||
"nash.awesome-flutter-snippets" # Dart Data Class Generator
|
||||
"robert-brunhage.flutter-riverpod-snippets" # Flutter Riverpod Snippets
|
||||
"usernamehw.errorlens" # Error Lens
|
||||
"aaron-bond.better-comments" # Better Comments
|
||||
"plibither8.remove-comments" # Remove Comments
|
||||
"patbenatar.advanced-new-file" # Advanced New File
|
||||
"GitHub.copilot" # GitHub Copilot
|
||||
"dracula-theme.theme-dracula" # Dracula Theme (optional)
|
||||
"jsayol.firebase-explorer" # Firebase Explorer
|
||||
"pflannery.vscode-versionlens" # Version Lens
|
||||
"esentis.flutter-find-unused-assets-and-dart-files" # Find Unused Assets & Dart Files
|
||||
"humao.rest-client" # REST Client
|
||||
"rangav.vscode-thunder-client" # Thunder Client
|
||||
"ritwickdey.liveserver" # Live Server
|
||||
"Dart-Code.dart-code" # Dart SDK
|
||||
"Dart-Code.flutter" # Flutter SDK
|
||||
"ms-vscode.cpptools" # C/C++
|
||||
"ms-vscode.cpptools-extension-pack" # C/C++ Extension Pack
|
||||
"ms-vscode.cpptools-themes" # C/C++ Themes
|
||||
"twxs.cmake " # CMake
|
||||
"ms-vscode.cmake-tools" # CMake Tools
|
||||
"ms-vscode.makefile-tools" # Makefile Tools
|
||||
"saoudrizwan.claude-dev" # Claude Dev
|
||||
"Continue.continue" # Continue
|
||||
"DEVSENSE.phptools-vscode" # PHP Tools
|
||||
"DEVSENSE.composer-php-vscode" # Composer PHP
|
||||
"DEVSENSE.profiler-php-vscode" # Profiler PHP
|
||||
"ms-vscode.remote-explorer" # Remote - Containers
|
||||
"ms-vscode-remote.remote-ssh" # Remote - SSH
|
||||
"ms-vscode-remote.remote-ssh-edit" # Remote - SSH: Edit
|
||||
"ms-vscode-remote.remote-containers" # Remote - Containers
|
||||
"eamodio.gitlens" # GitLens
|
||||
"DEVSENSE.intelli-php-vscode" # IntelliPHP
|
||||
"blaugold.melos-code" # Melos
|
||||
"vscode-icons-team.vscode-icons" # VSCode Icons
|
||||
"redhat.vscode-yaml" # YAML
|
||||
"GitHub.vscode-github-actions" # GitHub Actions
|
||||
"ms-azuretools.vscode-docker" # Docker
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools" # Kubernetes
|
||||
)
|
||||
|
||||
# Install each extension
|
||||
echo "Installing VSCode extensions..."
|
||||
for extension in "${extensions[@]}"; do
|
||||
code --install-extension "$extension" --force
|
||||
echo "Installed: $extension"
|
||||
done
|
||||
|
||||
echo "All extensions have been installed successfully."
|
156
helpers/tools/converter/bin/extract_contracts.dart
Normal file
156
helpers/tools/converter/bin/extract_contracts.dart
Normal file
|
@ -0,0 +1,156 @@
|
|||
import 'dart:io';
|
||||
import 'package:args/args.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:converter/src/extractors/base_extractor.dart';
|
||||
import 'package:converter/src/extractors/php_extractor.dart';
|
||||
|
||||
/// Factory for creating language-specific extractors
|
||||
class ExtractorFactory {
|
||||
/// Create an appropriate extractor based on file extension
|
||||
static LanguageExtractor? createExtractor(String extension) {
|
||||
switch (extension.toLowerCase()) {
|
||||
case '.php':
|
||||
return PhpExtractor();
|
||||
// TODO: Add more extractors as they're implemented
|
||||
// case '.py':
|
||||
// return PythonExtractor();
|
||||
// case '.ts':
|
||||
// case '.js':
|
||||
// return TypeScriptExtractor();
|
||||
// case '.java':
|
||||
// return JavaExtractor();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Main contract extractor CLI
|
||||
class ContractExtractorCLI {
|
||||
final String sourcePath;
|
||||
final String outputPath;
|
||||
final bool verbose;
|
||||
|
||||
ContractExtractorCLI({
|
||||
required this.sourcePath,
|
||||
required this.outputPath,
|
||||
this.verbose = false,
|
||||
});
|
||||
|
||||
/// Run the extraction process
|
||||
Future<void> run() async {
|
||||
try {
|
||||
if (await FileSystemEntity.isDirectory(sourcePath)) {
|
||||
await _processDirectory(sourcePath);
|
||||
} else if (await FileSystemEntity.isFile(sourcePath)) {
|
||||
await _processFile(sourcePath);
|
||||
} else {
|
||||
throw Exception('Source path does not exist: $sourcePath');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a directory recursively
|
||||
Future<void> _processDirectory(String dirPath) async {
|
||||
final dir = Directory(dirPath);
|
||||
await for (final entity in dir.list(recursive: true)) {
|
||||
if (entity is File) {
|
||||
await _processFile(entity.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a single file
|
||||
Future<void> _processFile(String filePath) async {
|
||||
final extension = path.extension(filePath);
|
||||
final extractor = ExtractorFactory.createExtractor(extension);
|
||||
|
||||
if (extractor == null) {
|
||||
if (verbose) {
|
||||
print('Skipping unsupported file type: $filePath');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Calculate relative path to maintain directory structure
|
||||
final relativePath = path.relative(filePath, from: sourcePath);
|
||||
final destDir = path.join(outputPath, path.dirname(relativePath));
|
||||
|
||||
// Create destination directory
|
||||
await Directory(destDir).create(recursive: true);
|
||||
|
||||
// Extract contract
|
||||
final contract = await extractor.parseFile(filePath);
|
||||
final yamlContent = extractor.convertToYaml(contract);
|
||||
|
||||
// Write YAML contract
|
||||
final yamlFile = File(path.join(
|
||||
destDir,
|
||||
'${path.basenameWithoutExtension(filePath)}.yaml',
|
||||
));
|
||||
await yamlFile.writeAsString(yamlContent);
|
||||
|
||||
if (verbose) {
|
||||
print('Processed: $filePath');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error processing $filePath: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption(
|
||||
'source',
|
||||
abbr: 's',
|
||||
help: 'Source file or directory path',
|
||||
mandatory: true,
|
||||
)
|
||||
..addOption(
|
||||
'output',
|
||||
abbr: 'o',
|
||||
help: 'Output directory for YAML contracts',
|
||||
mandatory: true,
|
||||
)
|
||||
..addFlag(
|
||||
'verbose',
|
||||
abbr: 'v',
|
||||
help: 'Enable verbose output',
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'help',
|
||||
abbr: 'h',
|
||||
help: 'Show this help message',
|
||||
negatable: false,
|
||||
);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
|
||||
if (results['help'] as bool) {
|
||||
print('Usage: dart extract_contracts.dart [options]');
|
||||
print(parser.usage);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
final cli = ContractExtractorCLI(
|
||||
sourcePath: results['source'] as String,
|
||||
outputPath: results['output'] as String,
|
||||
verbose: results['verbose'] as bool,
|
||||
);
|
||||
|
||||
await cli.run();
|
||||
print('Contract extraction completed successfully.');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print('\nUsage: dart extract_contracts.dart [options]');
|
||||
print(parser.usage);
|
||||
exit(1);
|
||||
}
|
||||
}
|
108
helpers/tools/converter/example/extract_contracts_example.dart
Normal file
108
helpers/tools/converter/example/extract_contracts_example.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
import 'dart:io';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
void main() async {
|
||||
// Create a sample PHP file
|
||||
final samplePhp = '''
|
||||
<?php
|
||||
|
||||
namespace App\\Models;
|
||||
|
||||
use Illuminate\\Database\\Eloquent\\Model;
|
||||
use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
|
||||
use App\\Interfaces\\UserInterface;
|
||||
|
||||
/**
|
||||
* User model class.
|
||||
* Represents a user in the system.
|
||||
*/
|
||||
class User extends Model implements UserInterface {
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array \$fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array \$hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user's full name.
|
||||
*
|
||||
* @param string \$title Optional title prefix
|
||||
* @return string
|
||||
*/
|
||||
public function getFullName(string \$title = ''): string {
|
||||
return trim(\$title . ' ' . \$this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's password.
|
||||
*
|
||||
* @param string \$value
|
||||
* @return void
|
||||
*/
|
||||
public function setPasswordAttribute(string \$value): void {
|
||||
\$this->attributes['password'] = bcrypt(\$value);
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
// Create temporary directories
|
||||
final tempDir = Directory.systemTemp.createTempSync('contract_example');
|
||||
final sourceDir = Directory(path.join(tempDir.path, 'source'))..createSync();
|
||||
final outputDir = Directory(path.join(tempDir.path, 'output'))..createSync();
|
||||
|
||||
// Write sample PHP file
|
||||
final phpFile = File(path.join(sourceDir.path, 'User.php'));
|
||||
await phpFile.writeAsString(samplePhp);
|
||||
|
||||
// Run the contract extractor
|
||||
print('Extracting contracts from ${sourceDir.path}');
|
||||
print('Output directory: ${outputDir.path}');
|
||||
|
||||
final result = await Process.run(
|
||||
'dart',
|
||||
[
|
||||
'run',
|
||||
'bin/extract_contracts.dart',
|
||||
'--source',
|
||||
sourceDir.path,
|
||||
'--output',
|
||||
outputDir.path,
|
||||
'--verbose',
|
||||
],
|
||||
);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
print('Error: ${result.stderr}');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Read and display the generated YAML
|
||||
final yamlFile = File(path.join(outputDir.path, 'User.yaml'));
|
||||
if (await yamlFile.exists()) {
|
||||
print('\nGenerated YAML contract:');
|
||||
print('------------------------');
|
||||
print(await yamlFile.readAsString());
|
||||
} else {
|
||||
print('Error: YAML file was not generated');
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
122
helpers/tools/converter/lib/src/extractors/base_extractor.dart
Normal file
122
helpers/tools/converter/lib/src/extractors/base_extractor.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
import 'dart:io';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'yaml_formatter.dart';
|
||||
|
||||
/// Base class for all language extractors
|
||||
abstract class LanguageExtractor {
|
||||
/// File extension this extractor handles (e.g., '.php', '.py')
|
||||
String get fileExtension;
|
||||
|
||||
/// Parse a source file and extract its components
|
||||
Future<Map<String, dynamic>> parseFile(String filePath);
|
||||
|
||||
/// Extract class-level documentation
|
||||
String? extractClassComment(String content);
|
||||
|
||||
/// Extract dependencies (imports, use statements, etc.)
|
||||
List<Map<String, String>> extractDependencies(String content);
|
||||
|
||||
/// Extract class properties/fields
|
||||
List<Map<String, dynamic>> extractProperties(String content);
|
||||
|
||||
/// Extract class methods
|
||||
List<Map<String, dynamic>> extractMethods(String content);
|
||||
|
||||
/// Extract implemented interfaces
|
||||
List<String> extractInterfaces(String content);
|
||||
|
||||
/// Extract used traits/mixins
|
||||
List<String> extractTraits(String content);
|
||||
|
||||
/// Convert extracted data to YAML format
|
||||
String convertToYaml(Map<String, dynamic> data) {
|
||||
return YamlFormatter.toYaml(data);
|
||||
}
|
||||
|
||||
/// Process a directory of source files
|
||||
Future<void> processDirectory(String sourceDir, String destDir) async {
|
||||
final sourceDirectory = Directory(sourceDir);
|
||||
|
||||
await for (final entity in sourceDirectory.list(recursive: true)) {
|
||||
if (entity is! File || !entity.path.endsWith(fileExtension)) continue;
|
||||
|
||||
final relativePath = path.relative(entity.path, from: sourceDir);
|
||||
final destPath = path.join(destDir, path.dirname(relativePath));
|
||||
|
||||
await Directory(destPath).create(recursive: true);
|
||||
|
||||
final data = await parseFile(entity.path);
|
||||
final yamlContent = convertToYaml(data);
|
||||
|
||||
final yamlFile = File(path.join(
|
||||
destPath, '${path.basenameWithoutExtension(entity.path)}.yaml'));
|
||||
|
||||
await yamlFile.writeAsString(yamlContent);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse method parameters from a parameter string
|
||||
List<Map<String, String>> parseParameters(String paramsStr) {
|
||||
final params = <Map<String, String>>[];
|
||||
if (paramsStr.trim().isEmpty) return params;
|
||||
|
||||
for (final param in paramsStr.split(',')) {
|
||||
final parts = param.trim().split('=');
|
||||
final paramInfo = <String, String>{
|
||||
'name': parts[0].trim(),
|
||||
};
|
||||
|
||||
if (parts.length > 1) {
|
||||
paramInfo['default'] = parts[1].trim();
|
||||
}
|
||||
|
||||
params.add(paramInfo);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/// Format a comment by removing common comment markers and whitespace
|
||||
String? formatComment(String? comment) {
|
||||
if (comment == null || comment.isEmpty) return null;
|
||||
|
||||
return comment
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.map((line) {
|
||||
// Remove common comment markers
|
||||
line = line.replaceAll(RegExp(r'^/\*+|\*+/$'), '');
|
||||
line = line.replaceAll(RegExp(r'^\s*\*\s*'), '');
|
||||
line = line.replaceAll(RegExp(r'^//\s*'), '');
|
||||
return line.trim();
|
||||
})
|
||||
.where((line) => line.isNotEmpty)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/// Extract type information from a type string
|
||||
Map<String, dynamic> extractTypeInfo(String typeStr) {
|
||||
// Handle nullable types
|
||||
final isNullable = typeStr.endsWith('?');
|
||||
if (isNullable) {
|
||||
typeStr = typeStr.substring(0, typeStr.length - 1);
|
||||
}
|
||||
|
||||
// Handle generics
|
||||
final genericMatch = RegExp(r'^([\w\d_]+)<(.+)>$').firstMatch(typeStr);
|
||||
if (genericMatch != null) {
|
||||
return {
|
||||
'base_type': genericMatch.group(1),
|
||||
'generic_params':
|
||||
genericMatch.group(2)!.split(',').map((t) => t.trim()).toList(),
|
||||
'nullable': isNullable,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
'type': typeStr,
|
||||
'nullable': isNullable,
|
||||
};
|
||||
}
|
||||
}
|
157
helpers/tools/converter/lib/src/extractors/php_extractor.dart
Normal file
157
helpers/tools/converter/lib/src/extractors/php_extractor.dart
Normal file
|
@ -0,0 +1,157 @@
|
|||
import 'dart:io';
|
||||
import 'base_extractor.dart';
|
||||
|
||||
/// Extracts contract information from PHP source files
|
||||
class PhpExtractor extends LanguageExtractor {
|
||||
@override
|
||||
String get fileExtension => '.php';
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> parseFile(String filePath) async {
|
||||
final file = File(filePath);
|
||||
final content = await file.readAsString();
|
||||
|
||||
return {
|
||||
'name': filePath.split('/').last.split('.').first,
|
||||
'class_comment': extractClassComment(content),
|
||||
'dependencies': extractDependencies(content),
|
||||
'properties': extractProperties(content),
|
||||
'methods': extractMethods(content),
|
||||
'traits': extractTraits(content),
|
||||
'interfaces': extractInterfaces(content),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String? extractClassComment(String content) {
|
||||
final regex =
|
||||
RegExp(r'/\*\*(.*?)\*/\s*class', multiLine: true, dotAll: true);
|
||||
final match = regex.firstMatch(content);
|
||||
return formatComment(match?.group(1));
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, String>> extractDependencies(String content) {
|
||||
final regex = RegExp(r'use\s+([\w\\]+)(?:\s+as\s+(\w+))?;');
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
final fullName = match.group(1)!;
|
||||
final alias = match.group(2);
|
||||
return {
|
||||
'name': alias ?? fullName.split('\\').last,
|
||||
'type': 'class', // Assuming class for now
|
||||
'source': fullName,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, dynamic>> extractProperties(String content) {
|
||||
final regex = RegExp(
|
||||
r'(?:/\*\*(.*?)\*/\s*)?(public|protected|private)\s+(?:readonly\s+)?(?:static\s+)?(?:[\w|]+\s+)?\$(\w+)(?:\s*=\s*[^;]+)?;',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
);
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
return {
|
||||
'name': match.group(3), // Property name without $
|
||||
'visibility': match.group(2),
|
||||
'comment': formatComment(match.group(1)),
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, dynamic>> extractMethods(String content) {
|
||||
final regex = RegExp(
|
||||
r'(?:/\*\*(.*?)\*/\s*)?(public|protected|private)\s+(?:static\s+)?function\s+(\w+)\s*\((.*?)\)(?:\s*:\s*(?:[\w|\\]+))?\s*{',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
);
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
return {
|
||||
'name': match.group(3),
|
||||
'visibility': match.group(2),
|
||||
'parameters': _parseMethodParameters(match.group(4) ?? ''),
|
||||
'comment': formatComment(match.group(1)),
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<Map<String, String>> _parseMethodParameters(String params) {
|
||||
if (params.trim().isEmpty) return [];
|
||||
|
||||
final parameters = <Map<String, String>>[];
|
||||
final paramList = params.split(',');
|
||||
|
||||
for (var param in paramList) {
|
||||
param = param.trim();
|
||||
if (param.isEmpty) continue;
|
||||
|
||||
final paramInfo = <String, String>{};
|
||||
|
||||
// Handle type declaration and parameter name
|
||||
final typeAndName = param.split(RegExp(r'\$'));
|
||||
if (typeAndName.length > 1) {
|
||||
// Has type declaration
|
||||
final type = typeAndName[0].trim();
|
||||
if (type.isNotEmpty) {
|
||||
paramInfo['type'] = type;
|
||||
}
|
||||
|
||||
// Handle parameter name and default value
|
||||
final nameAndDefault = typeAndName[1].split('=');
|
||||
paramInfo['name'] = nameAndDefault[0].trim();
|
||||
|
||||
if (nameAndDefault.length > 1) {
|
||||
paramInfo['default'] = nameAndDefault[1].trim();
|
||||
}
|
||||
} else {
|
||||
// No type declaration, just name and possibly default value
|
||||
final nameAndDefault = param.replaceAll(r'$', '').split('=');
|
||||
paramInfo['name'] = nameAndDefault[0].trim();
|
||||
|
||||
if (nameAndDefault.length > 1) {
|
||||
paramInfo['default'] = nameAndDefault[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
parameters.add(paramInfo);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> extractTraits(String content) {
|
||||
final regex = RegExp(r'use\s+([\w\\]+(?:\s*,\s*[\w\\]+)*)\s*;');
|
||||
final matches = regex.allMatches(content);
|
||||
final traits = <String>[];
|
||||
|
||||
for (final match in matches) {
|
||||
final traitList = match.group(1)!.split(',');
|
||||
traits.addAll(traitList.map((t) => t.trim()));
|
||||
}
|
||||
|
||||
return traits;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> extractInterfaces(String content) {
|
||||
final regex = RegExp(r'implements\s+([\w\\]+(?:\s*,\s*[\w\\]+)*)');
|
||||
final matches = regex.allMatches(content);
|
||||
final interfaces = <String>[];
|
||||
|
||||
for (final match in matches) {
|
||||
final interfaceList = match.group(1)!.split(',');
|
||||
interfaces.addAll(interfaceList.map((i) => i.trim()));
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
}
|
223
helpers/tools/converter/lib/src/extractors/yaml_formatter.dart
Normal file
223
helpers/tools/converter/lib/src/extractors/yaml_formatter.dart
Normal file
|
@ -0,0 +1,223 @@
|
|||
/// Handles YAML formatting with proper comment preservation
|
||||
class YamlFormatter {
|
||||
/// Format a value for YAML output
|
||||
static String format(dynamic value, {int indent = 0}) {
|
||||
if (value == null) return 'null';
|
||||
|
||||
final indentStr = ' ' * indent;
|
||||
|
||||
if (value is String) {
|
||||
if (value.startsWith('#')) {
|
||||
// Handle comments - preserve only actual comment content
|
||||
return value
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.map((line) => '$indentStr$line')
|
||||
.join('\n');
|
||||
}
|
||||
// Escape special characters and handle multiline strings
|
||||
if (value.contains('\n') || value.contains('"')) {
|
||||
return '|\n${value.split('\n').map((line) => '$indentStr ${line.trim()}').join('\n')}';
|
||||
}
|
||||
return value.contains(' ') ? '"$value"' : value;
|
||||
}
|
||||
|
||||
if (value is num || value is bool) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (value is List) {
|
||||
if (value.isEmpty) return '[]';
|
||||
final buffer = StringBuffer('\n');
|
||||
for (final item in value) {
|
||||
buffer.writeln(
|
||||
'$indentStr- ${format(item, indent: indent + 2).trimLeft()}');
|
||||
}
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
if (value is Map) {
|
||||
if (value.isEmpty) return '{}';
|
||||
final buffer = StringBuffer('\n');
|
||||
value.forEach((key, val) {
|
||||
if (val != null) {
|
||||
final formattedValue = format(val, indent: indent + 2);
|
||||
if (formattedValue.contains('\n')) {
|
||||
buffer.writeln('$indentStr$key:$formattedValue');
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: $formattedValue');
|
||||
}
|
||||
// Add extra newline between top-level sections
|
||||
if (indent == 0) {
|
||||
buffer.writeln();
|
||||
}
|
||||
}
|
||||
});
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/// Extract the actual documentation from a comment block
|
||||
static String _extractDocumentation(String comment) {
|
||||
return comment
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.where(
|
||||
(line) => !line.contains('class ') && !line.contains('function '))
|
||||
.where((line) => !line.startsWith('@'))
|
||||
.where((line) => !line.contains('use ') && !line.contains('protected '))
|
||||
.where((line) => !line.contains('];') && !line.contains('['))
|
||||
.where((line) => !line.contains("'"))
|
||||
.where(
|
||||
(line) => !line.contains('private ') && !line.contains('public '))
|
||||
.where((line) => !line.contains('\$'))
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/// Format method documentation
|
||||
static String formatMethodDoc(Map<String, dynamic> method) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Add main comment
|
||||
if (method['comment'] != null) {
|
||||
final mainComment = _extractDocumentation(method['comment'].toString());
|
||||
if (mainComment.isNotEmpty) {
|
||||
buffer.writeln(
|
||||
mainComment.split('\n').map((line) => '# $line').join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
// Add parameter documentation
|
||||
final params = method['parameters'] as List<Map<String, String>>?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
buffer.writeln('# Parameters:');
|
||||
for (final param in params) {
|
||||
final name = param['name'];
|
||||
final type = param['type'] ?? 'mixed';
|
||||
final defaultValue = param['default'];
|
||||
if (defaultValue != null) {
|
||||
buffer.writeln('# $name ($type = $defaultValue)');
|
||||
} else {
|
||||
buffer.writeln('# $name ($type)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
/// Format property documentation
|
||||
static String formatPropertyDoc(Map<String, dynamic> property) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Add main comment
|
||||
if (property['comment'] != null) {
|
||||
final mainComment = _extractDocumentation(property['comment'].toString());
|
||||
if (mainComment.isNotEmpty) {
|
||||
buffer.writeln(
|
||||
mainComment.split('\n').map((line) => '# $line').join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
// Add visibility
|
||||
if (property['visibility'] != null) {
|
||||
buffer.writeln('# Visibility: ${property["visibility"]}');
|
||||
}
|
||||
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
/// Convert a contract to YAML format
|
||||
static String toYaml(Map<String, dynamic> contract) {
|
||||
final formatted = <String, dynamic>{};
|
||||
|
||||
// Format class documentation
|
||||
if (contract['class_comment'] != null) {
|
||||
final doc = _extractDocumentation(contract['class_comment'] as String);
|
||||
if (doc.isNotEmpty) {
|
||||
formatted['documentation'] =
|
||||
doc.split('\n').map((line) => '# $line').join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
// Format dependencies (remove duplicates)
|
||||
if (contract['dependencies'] != null) {
|
||||
final deps = contract['dependencies'] as List;
|
||||
final uniqueDeps = <String, Map<String, String>>{};
|
||||
for (final dep in deps) {
|
||||
final source = dep['source'] as String;
|
||||
if (!uniqueDeps.containsKey(source)) {
|
||||
uniqueDeps[source] = dep as Map<String, String>;
|
||||
}
|
||||
}
|
||||
formatted['dependencies'] = uniqueDeps.values.toList();
|
||||
}
|
||||
|
||||
// Format properties with documentation
|
||||
if (contract['properties'] != null) {
|
||||
formatted['properties'] = (contract['properties'] as List).map((prop) {
|
||||
final doc = formatPropertyDoc(prop as Map<String, dynamic>);
|
||||
return {
|
||||
'name': prop['name'],
|
||||
'visibility': prop['visibility'],
|
||||
'documentation': doc,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Format methods with documentation
|
||||
if (contract['methods'] != null) {
|
||||
formatted['methods'] = (contract['methods'] as List).map((method) {
|
||||
final doc = formatMethodDoc(method as Map<String, dynamic>);
|
||||
return {
|
||||
'name': method['name'],
|
||||
'visibility': method['visibility'],
|
||||
'parameters': method['parameters'],
|
||||
'documentation': doc,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Format interfaces (remove duplicates)
|
||||
if (contract['interfaces'] != null) {
|
||||
formatted['interfaces'] =
|
||||
(contract['interfaces'] as List).toSet().toList();
|
||||
}
|
||||
|
||||
// Format traits (remove duplicates and filter out interfaces)
|
||||
if (contract['traits'] != null) {
|
||||
final traits = (contract['traits'] as List)
|
||||
.where((t) {
|
||||
// Filter out duplicates from dependencies
|
||||
if (contract['dependencies'] != null) {
|
||||
final deps = contract['dependencies'] as List;
|
||||
if (deps.any((d) => d['source'] == t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Filter out interfaces
|
||||
if (formatted['interfaces'] != null) {
|
||||
final interfaces = formatted['interfaces'] as List;
|
||||
if (interfaces.contains(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.toSet()
|
||||
.toList();
|
||||
|
||||
if (traits.isNotEmpty) {
|
||||
formatted['traits'] = traits;
|
||||
}
|
||||
}
|
||||
|
||||
return format(formatted);
|
||||
}
|
||||
}
|
168
helpers/tools/converter/lib/src/utils/class_generator_utils.dart
Normal file
168
helpers/tools/converter/lib/src/utils/class_generator_utils.dart
Normal file
|
@ -0,0 +1,168 @@
|
|||
import 'name_utils.dart';
|
||||
import 'type_conversion_utils.dart';
|
||||
|
||||
/// Utility class for generating Dart class code
|
||||
class ClassGeneratorUtils {
|
||||
/// Generate a constructor for a class
|
||||
static String generateConstructor(
|
||||
String className, List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Constructor signature
|
||||
buffer.writeln(' $className({');
|
||||
final params = <String>[];
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
params.add(' $propType? $propName,');
|
||||
} else {
|
||||
params.add(' required $propType $propName,');
|
||||
}
|
||||
}
|
||||
buffer.writeln(params.join('\n'));
|
||||
buffer.writeln(' }) {');
|
||||
|
||||
// Initialize properties in constructor body
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
final defaultValue = TypeConversionUtils.getDefaultValue(propType);
|
||||
buffer.writeln(' _$propName = $propName ?? $defaultValue;');
|
||||
} else {
|
||||
buffer.writeln(' _$propName = $propName;');
|
||||
}
|
||||
}
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate property declarations and accessors
|
||||
static String generateProperties(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
buffer.writeln(' late $propType _$propName;');
|
||||
|
||||
// Generate getter
|
||||
buffer.writeln(' $propType get $propName => _$propName;');
|
||||
|
||||
// Generate setter if not readonly
|
||||
final isReadonly = prop['is_readonly'];
|
||||
if (isReadonly != null && !isReadonly) {
|
||||
buffer.writeln(' set $propName($propType value) {');
|
||||
buffer.writeln(' _$propName = value;');
|
||||
buffer.writeln(' }');
|
||||
}
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate a method implementation
|
||||
static String generateMethod(Map<String, dynamic> method) {
|
||||
if (method['name'] == '__init__') return '';
|
||||
|
||||
final buffer = StringBuffer();
|
||||
final methodName = NameUtils.toDartName(method['name'] as String);
|
||||
final returnType =
|
||||
TypeConversionUtils.pythonToDartType(method['return_type'] as String);
|
||||
final methodDoc = method['docstring'] as String?;
|
||||
final isAsync = method['is_async'] == true;
|
||||
|
||||
if (methodDoc != null) {
|
||||
buffer.writeln(' /// ${methodDoc.replaceAll('\n', '\n /// ')}');
|
||||
}
|
||||
|
||||
// Method signature
|
||||
if (isAsync) {
|
||||
buffer.write(' Future<$returnType> $methodName(');
|
||||
} else {
|
||||
buffer.write(' $returnType $methodName(');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
final params = method['arguments'] as List?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
final paramStrings = <String>[];
|
||||
|
||||
for (final param in params) {
|
||||
final paramName = NameUtils.toDartName(param['name'] as String);
|
||||
final paramType =
|
||||
TypeConversionUtils.pythonToDartType(param['type'] as String);
|
||||
final isOptional = param['is_optional'] == true;
|
||||
|
||||
if (isOptional) {
|
||||
paramStrings.add('[$paramType $paramName]');
|
||||
} else {
|
||||
paramStrings.add('$paramType $paramName');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.write(paramStrings.join(', '));
|
||||
}
|
||||
|
||||
buffer.write(')');
|
||||
if (isAsync) buffer.write(' async');
|
||||
buffer.writeln(' {');
|
||||
buffer.writeln(' // TODO: Implement $methodName');
|
||||
if (returnType == 'void') {
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
} else {
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
}
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate required interface implementations
|
||||
static String generateRequiredImplementations(
|
||||
List<String> bases, Map<String, dynamic> classContract) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Generate BaseChain implementations
|
||||
if (bases.contains('BaseChain')) {
|
||||
buffer.writeln(' late Map<String, dynamic>? _memory;');
|
||||
buffer.writeln(' Map<String, dynamic>? get memory => _memory;');
|
||||
buffer.writeln();
|
||||
buffer.writeln(' late bool _verbose;');
|
||||
buffer.writeln(' bool get verbose => _verbose;');
|
||||
buffer.writeln();
|
||||
|
||||
// Constructor with required properties
|
||||
buffer.writeln(' ${classContract['name']}({');
|
||||
buffer.writeln(' Map<String, dynamic>? memory,');
|
||||
buffer.writeln(' bool? verbose,');
|
||||
buffer.writeln(' }) {');
|
||||
buffer.writeln(' _memory = memory ?? {};');
|
||||
buffer.writeln(' _verbose = verbose ?? false;');
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
// Required methods
|
||||
buffer.writeln(' @override');
|
||||
buffer.writeln(' void setMemory(Map<String, dynamic> memory) {');
|
||||
buffer.writeln(' // TODO: Implement setMemory');
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
40
helpers/tools/converter/lib/src/utils/constructor_utils.dart
Normal file
40
helpers/tools/converter/lib/src/utils/constructor_utils.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
/// Utility functions for constructor generation
|
||||
class ConstructorUtils {
|
||||
/// Generate constructor parameter declarations
|
||||
static String generateParameters(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
for (final prop in properties) {
|
||||
final propName = prop['name'] as String;
|
||||
final propType = prop['type'] as String;
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
buffer.writeln(' $propType? $propName,');
|
||||
} else {
|
||||
buffer.writeln(' required $propType $propName,');
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate constructor initialization statements
|
||||
static String generateInitializers(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
for (final prop in properties) {
|
||||
final propName = prop['name'] as String;
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
if (hasDefault) {
|
||||
buffer.writeln(
|
||||
' _$propName = $propName ?? false;'); // TODO: Better default values
|
||||
} else {
|
||||
buffer.writeln(' _$propName = $propName;');
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Check if a method is a constructor (__init__)
|
||||
static bool isConstructor(Map<String, dynamic> method) {
|
||||
return method['name'] == '__init__';
|
||||
}
|
||||
}
|
23
helpers/tools/converter/lib/src/utils/name_utils.dart
Normal file
23
helpers/tools/converter/lib/src/utils/name_utils.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
/// Utility functions for name conversions
|
||||
class NameUtils {
|
||||
/// Convert Python method/property name to Dart style
|
||||
static String toDartName(String pythonName) {
|
||||
// Handle special Python method names
|
||||
if (pythonName.startsWith('__') && pythonName.endsWith('__')) {
|
||||
final name = pythonName.substring(2, pythonName.length - 2);
|
||||
if (name == 'init') return 'new';
|
||||
return name;
|
||||
}
|
||||
|
||||
// Convert snake_case to camelCase
|
||||
final parts = pythonName.split('_');
|
||||
if (parts.isEmpty) return pythonName;
|
||||
|
||||
return parts.first +
|
||||
parts
|
||||
.skip(1)
|
||||
.where((p) => p.isNotEmpty)
|
||||
.map((p) => p[0].toUpperCase() + p.substring(1))
|
||||
.join('');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/// Utility class for converting Python types to Dart types
|
||||
class TypeConversionUtils {
|
||||
/// Convert a Python type string to its Dart equivalent
|
||||
static String pythonToDartType(String pythonType) {
|
||||
final typeMap = {
|
||||
'str': 'String',
|
||||
'int': 'int',
|
||||
'float': 'double',
|
||||
'bool': 'bool',
|
||||
'List': 'List',
|
||||
'Dict': 'Map',
|
||||
'dict': 'Map<String, dynamic>',
|
||||
'Any': 'dynamic',
|
||||
'None': 'void',
|
||||
'Optional': 'dynamic',
|
||||
'Union': 'dynamic',
|
||||
'Callable': 'Function',
|
||||
};
|
||||
|
||||
// Handle generic types
|
||||
if (pythonType.contains('[')) {
|
||||
final match = RegExp(r'(\w+)\[(.*)\]').firstMatch(pythonType);
|
||||
if (match != null) {
|
||||
final baseType = match.group(1)!;
|
||||
final genericType = match.group(2)!;
|
||||
|
||||
if (baseType == 'List') {
|
||||
return 'List<${pythonToDartType(genericType)}>';
|
||||
} else if (baseType == 'Dict' || baseType == 'dict') {
|
||||
final types = genericType.split(',');
|
||||
if (types.length == 2) {
|
||||
return 'Map<${pythonToDartType(types[0].trim())}, ${pythonToDartType(types[1].trim())}>';
|
||||
}
|
||||
return 'Map<String, dynamic>';
|
||||
} else if (baseType == 'Optional') {
|
||||
final innerType = pythonToDartType(genericType);
|
||||
if (innerType == 'Map<String, dynamic>') {
|
||||
return 'Map<String, dynamic>?';
|
||||
}
|
||||
return '${innerType}?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle raw types
|
||||
if (pythonType == 'dict') {
|
||||
return 'Map<String, dynamic>';
|
||||
} else if (pythonType == 'None') {
|
||||
return 'void';
|
||||
}
|
||||
|
||||
return typeMap[pythonType] ?? pythonType;
|
||||
}
|
||||
|
||||
/// Get a default value for a Dart type
|
||||
static String getDefaultValue(String dartType) {
|
||||
switch (dartType) {
|
||||
case 'bool':
|
||||
case 'bool?':
|
||||
return 'false';
|
||||
case 'int':
|
||||
case 'int?':
|
||||
return '0';
|
||||
case 'double':
|
||||
case 'double?':
|
||||
return '0.0';
|
||||
case 'String':
|
||||
case 'String?':
|
||||
return "''";
|
||||
case 'Map<String, dynamic>':
|
||||
case 'Map<String, dynamic>?':
|
||||
return '{}';
|
||||
case 'List':
|
||||
case 'List?':
|
||||
return '[]';
|
||||
default:
|
||||
if (dartType.startsWith('List<')) {
|
||||
return '[]';
|
||||
} else if (dartType.startsWith('Map<')) {
|
||||
return '{}';
|
||||
} else if (dartType.endsWith('?')) {
|
||||
return 'null';
|
||||
}
|
||||
return 'null';
|
||||
}
|
||||
}
|
||||
}
|
20
helpers/tools/converter/lib/src/utils/type_utils.dart
Normal file
20
helpers/tools/converter/lib/src/utils/type_utils.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
/// Utility functions for type casting
|
||||
class TypeUtils {
|
||||
/// Cast a List<dynamic> to List<Map<String, dynamic>>
|
||||
static List<Map<String, dynamic>> castToMapList(List<dynamic>? list) {
|
||||
if (list == null) return [];
|
||||
return list.map((item) => item as Map<String, dynamic>).toList();
|
||||
}
|
||||
|
||||
/// Cast a dynamic value to Map<String, dynamic>
|
||||
static Map<String, dynamic> castToMap(dynamic value) {
|
||||
if (value == null) return {};
|
||||
return value as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
/// Cast a List<dynamic> to List<String>
|
||||
static List<String> castToStringList(List<dynamic>? list) {
|
||||
if (list == null) return [];
|
||||
return list.map((item) => item.toString()).toList();
|
||||
}
|
||||
}
|
36
helpers/tools/converter/lib/src/utils/yaml_utils.dart
Normal file
36
helpers/tools/converter/lib/src/utils/yaml_utils.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
import 'package:yaml/yaml.dart';
|
||||
|
||||
/// Utility class for handling YAML conversions
|
||||
class YamlUtils {
|
||||
/// Convert YamlMap to regular Map recursively
|
||||
static Map<String, dynamic> convertYamlToMap(YamlMap yamlMap) {
|
||||
return Map<String, dynamic>.fromEntries(
|
||||
yamlMap.entries.map((entry) {
|
||||
if (entry.value is YamlMap) {
|
||||
return MapEntry(
|
||||
entry.key.toString(),
|
||||
convertYamlToMap(entry.value as YamlMap),
|
||||
);
|
||||
} else if (entry.value is YamlList) {
|
||||
return MapEntry(
|
||||
entry.key.toString(),
|
||||
convertYamlList(entry.value as YamlList),
|
||||
);
|
||||
}
|
||||
return MapEntry(entry.key.toString(), entry.value);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert YamlList to regular List recursively
|
||||
static List<dynamic> convertYamlList(YamlList yamlList) {
|
||||
return yamlList.map((item) {
|
||||
if (item is YamlMap) {
|
||||
return convertYamlToMap(item);
|
||||
} else if (item is YamlList) {
|
||||
return convertYamlList(item);
|
||||
}
|
||||
return item;
|
||||
}).toList();
|
||||
}
|
||||
}
|
402
helpers/tools/converter/pubspec.lock
Normal file
402
helpers/tools/converter/pubspec.lock
Normal file
|
@ -0,0 +1,402 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "73.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.8.0"
|
||||
args:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: args
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.12"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.8"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.6"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
15
helpers/tools/converter/pubspec.yaml
Normal file
15
helpers/tools/converter/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: converter
|
||||
description: A Dart implementation of LangChain, providing tools and utilities for building applications powered by large language models (LLMs).
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
yaml: ^3.1.2
|
||||
path: ^1.8.3
|
||||
args: ^2.4.2
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.1.1
|
||||
test: ^1.24.6
|
95
helpers/tools/converter/test/code_generator_class_test.dart
Normal file
95
helpers/tools/converter/test/code_generator_class_test.dart
Normal file
|
@ -0,0 +1,95 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Class Generation', () {
|
||||
test('generates class with interface implementations', () {
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'bases': ['BaseChain'],
|
||||
'methods': [
|
||||
{
|
||||
'name': 'run',
|
||||
'return_type': 'dict',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'inputs',
|
||||
'type': 'dict',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Execute the chain logic.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Should include BaseChain implementations
|
||||
expect(code, contains('late Map<String, dynamic>? _memory;'));
|
||||
expect(code, contains('Map<String, dynamic>? get memory => _memory;'));
|
||||
expect(code, contains('late bool _verbose;'));
|
||||
expect(code, contains('bool get verbose => _verbose;'));
|
||||
|
||||
// Should include constructor with required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
expect(code, contains('_memory = memory ?? {};'));
|
||||
expect(code, contains('_verbose = verbose ?? false;'));
|
||||
|
||||
// Should include required method implementations
|
||||
expect(code, contains('@override'));
|
||||
expect(code, contains('void setMemory(Map<String, dynamic> memory)'));
|
||||
|
||||
// Should include additional methods
|
||||
expect(code, contains('Map<String, dynamic> run('));
|
||||
expect(code, contains('Map<String, dynamic> inputs'));
|
||||
expect(code, contains('/// Execute the chain logic.'));
|
||||
});
|
||||
|
||||
test('generates class with own properties and methods', () {
|
||||
final classContract = {
|
||||
'name': 'CustomClass',
|
||||
'docstring': 'A custom class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'methods': [
|
||||
{
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Should include properties
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
|
||||
// Should include constructor
|
||||
expect(code, contains('CustomClass({'));
|
||||
expect(code, contains('required String modelName'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
|
||||
// Should include methods
|
||||
expect(code, contains('String process(String input)'));
|
||||
expect(code, contains('/// Process input.'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
import '../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator Integration', () {
|
||||
test('generates complete class with properties and methods', () {
|
||||
final classContract = {
|
||||
'name': 'TestModel',
|
||||
'docstring': 'A test model implementation.',
|
||||
'bases': ['BaseModel'],
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_loaded',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
'methods': [
|
||||
{
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Initialize the model.',
|
||||
},
|
||||
{
|
||||
'name': 'process_input',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input_text',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input text.',
|
||||
'is_async': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Class definition
|
||||
expect(code, contains('class TestModel implements BaseModel {'));
|
||||
expect(code, contains('/// A test model implementation.'));
|
||||
|
||||
// Properties
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
expect(code, contains('late bool _isLoaded;'));
|
||||
expect(code, contains('bool get isLoaded => _isLoaded;'));
|
||||
|
||||
// Constructor
|
||||
expect(code, contains('TestModel({'));
|
||||
expect(code, contains('required String modelName,'));
|
||||
expect(code, contains('bool? isLoaded,'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
expect(code, contains('_isLoaded = isLoaded ?? false;'));
|
||||
|
||||
// Methods
|
||||
expect(code,
|
||||
contains('Future<String> processInput(String inputText) async {'));
|
||||
expect(code, contains('/// Process input text.'));
|
||||
|
||||
// No __init__ method
|
||||
expect(code, isNot(contains('__init__')));
|
||||
expect(code, isNot(contains('void new(')));
|
||||
});
|
||||
});
|
||||
}
|
63
helpers/tools/converter/test/code_generator_name_test.dart
Normal file
63
helpers/tools/converter/test/code_generator_name_test.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator Name Handling', () {
|
||||
test('converts Python method names to Dart style in interfaces', () {
|
||||
final interface = {
|
||||
'name': 'TestInterface',
|
||||
'docstring': 'Test interface.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'get_model_name',
|
||||
'return_type': 'str',
|
||||
'arguments': [],
|
||||
'docstring': 'Get model name.',
|
||||
},
|
||||
{
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'model_path',
|
||||
'type': 'str',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Initialize.',
|
||||
}
|
||||
],
|
||||
'properties': [],
|
||||
};
|
||||
|
||||
final code = generateInterface(interface);
|
||||
expect(code, contains('String getModelName();'));
|
||||
expect(code, contains('void new(String modelPath);'));
|
||||
});
|
||||
|
||||
test('converts Python property names to Dart style in classes', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'str',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_initialized',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('String get modelName'));
|
||||
expect(code, contains('bool get isInitialized'));
|
||||
});
|
||||
});
|
||||
}
|
83
helpers/tools/converter/test/code_generator_test.dart
Normal file
83
helpers/tools/converter/test/code_generator_test.dart
Normal file
|
@ -0,0 +1,83 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator', () {
|
||||
test('generates constructor correctly', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('TestClass({'));
|
||||
expect(code, contains('required String name,'));
|
||||
expect(code, contains('bool? isReady,'));
|
||||
expect(code, contains('_name = name;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
|
||||
test('handles async methods correctly', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input.',
|
||||
'is_async': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('Future<String> process(String input) async {'));
|
||||
expect(code, contains('/// Process input.'));
|
||||
});
|
||||
|
||||
test('initializes properties in constructor', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('late String _name;'));
|
||||
expect(code, contains('late bool _isReady;'));
|
||||
expect(code, contains('_name = name;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
});
|
||||
}
|
181
helpers/tools/converter/test/extractors/php_extractor_test.dart
Normal file
181
helpers/tools/converter/test/extractors/php_extractor_test.dart
Normal file
|
@ -0,0 +1,181 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/extractors/php_extractor.dart';
|
||||
|
||||
void main() {
|
||||
group('PhpExtractor', () {
|
||||
final extractor = PhpExtractor();
|
||||
|
||||
test('extracts class comment', () {
|
||||
const phpCode = '''
|
||||
/**
|
||||
* User entity class.
|
||||
* Represents a user in the system.
|
||||
*/
|
||||
class User {
|
||||
}
|
||||
''';
|
||||
final comment = extractor.extractClassComment(phpCode);
|
||||
expect(comment, contains('User entity class'));
|
||||
expect(comment, contains('Represents a user in the system'));
|
||||
});
|
||||
|
||||
test('extracts dependencies', () {
|
||||
const phpCode = '''
|
||||
use App\\Models\\User;
|
||||
use Illuminate\\Support\\Str as StringHelper;
|
||||
use App\\Interfaces\\UserInterface;
|
||||
''';
|
||||
final deps = extractor.extractDependencies(phpCode);
|
||||
expect(deps, hasLength(3));
|
||||
expect(deps[0]['name'], equals('User'));
|
||||
expect(deps[1]['name'], equals('StringHelper'));
|
||||
expect(deps[2]['name'], equals('UserInterface'));
|
||||
expect(deps[1]['source'], equals('Illuminate\\Support\\Str'));
|
||||
});
|
||||
|
||||
test('extracts properties', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
/**
|
||||
* The user's name.
|
||||
*/
|
||||
private string \$name;
|
||||
|
||||
/**
|
||||
* The user's email.
|
||||
*/
|
||||
protected string \$email;
|
||||
|
||||
/**
|
||||
* Is the user active?
|
||||
*/
|
||||
public bool \$isActive = false;
|
||||
}
|
||||
''';
|
||||
final props = extractor.extractProperties(phpCode);
|
||||
expect(props, hasLength(3));
|
||||
|
||||
expect(props[0]['name'], equals('name'));
|
||||
expect(props[0]['visibility'], equals('private'));
|
||||
expect(props[0]['comment'], contains("The user's name"));
|
||||
|
||||
expect(props[1]['name'], equals('email'));
|
||||
expect(props[1]['visibility'], equals('protected'));
|
||||
|
||||
expect(props[2]['name'], equals('isActive'));
|
||||
expect(props[2]['visibility'], equals('public'));
|
||||
});
|
||||
|
||||
test('extracts methods', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
/**
|
||||
* Get the user's full name.
|
||||
* @param string \$title Optional title
|
||||
* @return string
|
||||
*/
|
||||
public function getFullName(string \$title = '') {
|
||||
return \$title . ' ' . \$this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's email address.
|
||||
*/
|
||||
protected function setEmail(string \$email) {
|
||||
\$this->email = \$email;
|
||||
}
|
||||
}
|
||||
''';
|
||||
final methods = extractor.extractMethods(phpCode);
|
||||
expect(methods, hasLength(2));
|
||||
|
||||
expect(methods[0]['name'], equals('getFullName'));
|
||||
expect(methods[0]['visibility'], equals('public'));
|
||||
expect(methods[0]['parameters'], hasLength(1));
|
||||
expect(methods[0]['parameters'][0]['name'], equals('title'));
|
||||
expect(methods[0]['parameters'][0]['default'], equals("''"));
|
||||
expect(methods[0]['comment'], contains("Get the user's full name"));
|
||||
|
||||
expect(methods[1]['name'], equals('setEmail'));
|
||||
expect(methods[1]['visibility'], equals('protected'));
|
||||
expect(methods[1]['parameters'], hasLength(1));
|
||||
expect(methods[1]['parameters'][0]['name'], equals('email'));
|
||||
});
|
||||
|
||||
test('extracts interfaces', () {
|
||||
const phpCode = '''
|
||||
class User implements UserInterface, Authenticatable {
|
||||
}
|
||||
''';
|
||||
final interfaces = extractor.extractInterfaces(phpCode);
|
||||
expect(interfaces, hasLength(2));
|
||||
expect(interfaces[0], equals('UserInterface'));
|
||||
expect(interfaces[1], equals('Authenticatable'));
|
||||
});
|
||||
|
||||
test('extracts traits', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
use HasFactory, Notifiable;
|
||||
}
|
||||
''';
|
||||
final traits = extractor.extractTraits(phpCode);
|
||||
expect(traits, hasLength(2));
|
||||
expect(traits[0], equals('HasFactory'));
|
||||
expect(traits[1], equals('Notifiable'));
|
||||
});
|
||||
|
||||
test('generates valid YAML output', () {
|
||||
const phpCode = '''
|
||||
/**
|
||||
* User entity class.
|
||||
*/
|
||||
class User implements UserInterface {
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The user's name.
|
||||
*/
|
||||
private string \$name;
|
||||
|
||||
/**
|
||||
* Get the user's name.
|
||||
*/
|
||||
public function getName(): string {
|
||||
return \$this->name;
|
||||
}
|
||||
}
|
||||
''';
|
||||
final contract = {
|
||||
'name': 'User',
|
||||
'class_comment': extractor.extractClassComment(phpCode),
|
||||
'dependencies': extractor.extractDependencies(phpCode),
|
||||
'properties': extractor.extractProperties(phpCode),
|
||||
'methods': extractor.extractMethods(phpCode),
|
||||
'traits': extractor.extractTraits(phpCode),
|
||||
'interfaces': extractor.extractInterfaces(phpCode),
|
||||
};
|
||||
|
||||
final yaml = extractor.convertToYaml(contract);
|
||||
|
||||
// Check required sections
|
||||
expect(yaml, contains('documentation:'));
|
||||
expect(yaml, contains('properties:'));
|
||||
expect(yaml, contains('methods:'));
|
||||
expect(yaml, contains('interfaces:'));
|
||||
|
||||
// Check content
|
||||
expect(yaml, contains('User entity class'));
|
||||
expect(yaml, contains('name: name'));
|
||||
expect(yaml, contains('visibility: private'));
|
||||
expect(yaml, contains('name: getName'));
|
||||
expect(yaml, contains('visibility: public'));
|
||||
expect(yaml, contains('UserInterface'));
|
||||
|
||||
// Verify formatting
|
||||
expect(yaml, isNot(contains('class User')));
|
||||
expect(yaml, isNot(contains('function')));
|
||||
expect(yaml, isNot(contains('private string')));
|
||||
});
|
||||
});
|
||||
}
|
112
helpers/tools/converter/test/fixtures/contracts.yaml
vendored
Normal file
112
helpers/tools/converter/test/fixtures/contracts.yaml
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
interfaces:
|
||||
-
|
||||
name: "LLMProtocol"
|
||||
bases:
|
||||
- Protocol
|
||||
methods:
|
||||
-
|
||||
name: "generate"
|
||||
arguments:
|
||||
-
|
||||
name: "prompts"
|
||||
type: "List[str]"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "List[str]"
|
||||
docstring: "Generate completions for the prompts."
|
||||
decorators:
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
-
|
||||
name: "model_name"
|
||||
arguments:
|
||||
return_type: "str"
|
||||
docstring: "Get the model name."
|
||||
decorators:
|
||||
-
|
||||
name: "property"
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
properties:
|
||||
docstring: "Protocol for language models."
|
||||
decorators:
|
||||
is_interface: true
|
||||
classes:
|
||||
-
|
||||
name: "BaseChain"
|
||||
bases:
|
||||
- ABC
|
||||
methods:
|
||||
-
|
||||
name: "run"
|
||||
arguments:
|
||||
-
|
||||
name: "inputs"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "dict"
|
||||
docstring: "Run the chain on the inputs."
|
||||
decorators:
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
-
|
||||
name: "set_memory"
|
||||
arguments:
|
||||
-
|
||||
name: "memory"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "None"
|
||||
docstring: "Set the memory for the chain."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
properties:
|
||||
-
|
||||
name: "memory"
|
||||
type: "Optional[dict]"
|
||||
has_default: true
|
||||
-
|
||||
name: "verbose"
|
||||
type: "bool"
|
||||
has_default: true
|
||||
docstring: "Base class for chains."
|
||||
decorators:
|
||||
is_interface: false
|
||||
-
|
||||
name: "SimpleChain"
|
||||
bases:
|
||||
- BaseChain
|
||||
methods:
|
||||
-
|
||||
name: "__init__"
|
||||
arguments:
|
||||
-
|
||||
name: "llm"
|
||||
type: "LLMProtocol"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "None"
|
||||
docstring: "Initialize the chain."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
-
|
||||
name: "run"
|
||||
arguments:
|
||||
-
|
||||
name: "inputs"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "dict"
|
||||
docstring: "Execute the chain logic."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
properties:
|
||||
docstring: "A simple implementation of a chain."
|
||||
decorators:
|
||||
is_interface: false
|
46
helpers/tools/converter/test/fixtures/sample.py
vendored
Normal file
46
helpers/tools/converter/test/fixtures/sample.py
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
from typing import List, Optional, Protocol
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class LLMProtocol(Protocol):
|
||||
"""Protocol for language models."""
|
||||
|
||||
@abstractmethod
|
||||
async def generate(self, prompts: List[str], **kwargs) -> List[str]:
|
||||
"""Generate completions for the prompts."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def model_name(self) -> str:
|
||||
"""Get the model name."""
|
||||
pass
|
||||
|
||||
class BaseChain(ABC):
|
||||
"""Base class for chains."""
|
||||
|
||||
memory: Optional[dict] = None
|
||||
verbose: bool = False
|
||||
|
||||
@abstractmethod
|
||||
async def run(self, inputs: dict) -> dict:
|
||||
"""Run the chain on the inputs."""
|
||||
pass
|
||||
|
||||
def set_memory(self, memory: dict) -> None:
|
||||
"""Set the memory for the chain."""
|
||||
self.memory = memory
|
||||
|
||||
class SimpleChain(BaseChain):
|
||||
"""A simple implementation of a chain."""
|
||||
|
||||
def __init__(self, llm: LLMProtocol):
|
||||
"""Initialize the chain."""
|
||||
self.llm = llm
|
||||
self.history: List[str] = []
|
||||
|
||||
async def run(self, inputs: dict) -> dict:
|
||||
"""Execute the chain logic."""
|
||||
prompt = inputs.get("prompt", "")
|
||||
result = await self.llm.generate([prompt])
|
||||
self.history.append(result[0])
|
||||
return {"output": result[0]}
|
91
helpers/tools/converter/test/python_parser_test.dart
Normal file
91
helpers/tools/converter/test/python_parser_test.dart
Normal file
|
@ -0,0 +1,91 @@
|
|||
import 'dart:io';
|
||||
import 'package:test/test.dart';
|
||||
import '../tools/python_parser.dart';
|
||||
|
||||
void main() {
|
||||
group('PythonParser', () {
|
||||
test('parses interface correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final interface = classes.firstWhere((c) => c.isInterface);
|
||||
expect(interface.name, equals('LLMProtocol'));
|
||||
expect(interface.docstring, equals('Protocol for language models.'));
|
||||
|
||||
// Test generate method
|
||||
final generateMethod =
|
||||
interface.methods.firstWhere((m) => m.name == 'generate');
|
||||
expect(generateMethod.isAsync, isTrue);
|
||||
expect(generateMethod.isAbstract, isTrue);
|
||||
expect(generateMethod.docstring,
|
||||
equals('Generate completions for the prompts.'));
|
||||
expect(generateMethod.parameters.length, equals(1));
|
||||
expect(generateMethod.parameters.first.name, equals('prompts'));
|
||||
expect(generateMethod.parameters.first.type, equals('List[str]'));
|
||||
expect(generateMethod.returnType, equals('List[str]'));
|
||||
|
||||
// Test model_name property
|
||||
final modelNameMethod =
|
||||
interface.methods.firstWhere((m) => m.name == 'model_name');
|
||||
expect(modelNameMethod.isProperty, isTrue);
|
||||
expect(modelNameMethod.isAbstract, isTrue);
|
||||
expect(modelNameMethod.docstring, equals('Get the model name.'));
|
||||
expect(modelNameMethod.parameters.isEmpty, isTrue);
|
||||
expect(modelNameMethod.returnType, equals('str'));
|
||||
});
|
||||
|
||||
test('parses abstract class correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final abstractClass = classes.firstWhere((c) => c.name == 'BaseChain');
|
||||
expect(abstractClass.docstring, equals('Base class for chains.'));
|
||||
|
||||
// Test properties
|
||||
expect(abstractClass.properties.length, equals(2));
|
||||
final memoryProp =
|
||||
abstractClass.properties.firstWhere((p) => p.name == 'memory');
|
||||
expect(memoryProp.type, equals('Optional[dict]'));
|
||||
expect(memoryProp.hasDefault, isTrue);
|
||||
|
||||
// Test run method
|
||||
final runMethod =
|
||||
abstractClass.methods.firstWhere((m) => m.name == 'run');
|
||||
expect(runMethod.isAsync, isTrue);
|
||||
expect(runMethod.isAbstract, isTrue);
|
||||
expect(runMethod.docstring, equals('Run the chain on the inputs.'));
|
||||
expect(runMethod.parameters.length, equals(1));
|
||||
expect(runMethod.parameters.first.name, equals('inputs'));
|
||||
expect(runMethod.parameters.first.type, equals('dict'));
|
||||
expect(runMethod.returnType, equals('dict'));
|
||||
});
|
||||
|
||||
test('parses concrete class correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final concreteClass = classes.firstWhere((c) => c.name == 'SimpleChain');
|
||||
expect(concreteClass.docstring,
|
||||
equals('A simple implementation of a chain.'));
|
||||
|
||||
// Test constructor
|
||||
final constructor =
|
||||
concreteClass.methods.firstWhere((m) => m.name == '__init__');
|
||||
expect(constructor.docstring, equals('Initialize the chain.'));
|
||||
expect(constructor.parameters.length, equals(1));
|
||||
expect(constructor.parameters.first.name, equals('llm'));
|
||||
expect(constructor.parameters.first.type, equals('LLMProtocol'));
|
||||
|
||||
// Test run method
|
||||
final runMethod =
|
||||
concreteClass.methods.firstWhere((m) => m.name == 'run');
|
||||
expect(runMethod.isAsync, isTrue);
|
||||
expect(runMethod.isAbstract, isFalse);
|
||||
expect(runMethod.docstring, equals('Execute the chain logic.'));
|
||||
expect(runMethod.parameters.length, equals(1));
|
||||
expect(runMethod.parameters.first.name, equals('inputs'));
|
||||
expect(runMethod.parameters.first.type, equals('dict'));
|
||||
expect(runMethod.returnType, equals('dict'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('ClassGeneratorUtils', () {
|
||||
test('generates constructor with property initialization', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final code =
|
||||
ClassGeneratorUtils.generateConstructor('TestClass', properties);
|
||||
expect(code, contains('TestClass({'));
|
||||
expect(code, contains('required String modelName,'));
|
||||
expect(code, contains('bool? isReady,'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
|
||||
test('generates properties with getters and setters', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'is_readonly': true,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'is_readonly': false,
|
||||
}
|
||||
];
|
||||
|
||||
final code = ClassGeneratorUtils.generateProperties(properties);
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
expect(code, contains('late bool _isReady;'));
|
||||
expect(code, contains('bool get isReady => _isReady;'));
|
||||
expect(code, contains('set isReady(bool value)'));
|
||||
expect(code, isNot(contains('set modelName')));
|
||||
});
|
||||
|
||||
test('generates async method correctly', () {
|
||||
final method = {
|
||||
'name': 'process_input',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process the input.',
|
||||
'is_async': true,
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateMethod(method);
|
||||
expect(code, contains('Future<String> processInput('));
|
||||
expect(code, contains('String input'));
|
||||
expect(code, contains('async {'));
|
||||
expect(code, contains('/// Process the input.'));
|
||||
});
|
||||
|
||||
test('skips generating constructor method', () {
|
||||
final method = {
|
||||
'name': '__init__',
|
||||
'return_type': 'void',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateMethod(method);
|
||||
expect(code, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/constructor_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('ConstructorUtils', () {
|
||||
test('generates required parameters for non-default properties', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
}
|
||||
];
|
||||
|
||||
final params = ConstructorUtils.generateParameters(properties);
|
||||
expect(params, contains('required String model_name'));
|
||||
});
|
||||
|
||||
test('generates optional parameters for default properties', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final params = ConstructorUtils.generateParameters(properties);
|
||||
expect(params, contains('bool? is_ready'));
|
||||
});
|
||||
|
||||
test('generates property initializers', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final inits = ConstructorUtils.generateInitializers(properties);
|
||||
expect(inits, contains('_model_name = model_name;'));
|
||||
expect(inits, contains('_is_ready = is_ready ?? false;'));
|
||||
});
|
||||
|
||||
test('identifies constructor methods', () {
|
||||
final initMethod = {
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
final regularMethod = {
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
expect(ConstructorUtils.isConstructor(initMethod), isTrue);
|
||||
expect(ConstructorUtils.isConstructor(regularMethod), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('Interface Implementation Generation', () {
|
||||
test('generates required BaseChain implementations', () {
|
||||
final bases = ['BaseChain'];
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'run',
|
||||
'return_type': 'dict',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'inputs',
|
||||
'type': 'dict',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Execute the chain logic.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
|
||||
// Should include memory property
|
||||
expect(code, contains('late Map<String, dynamic>? _memory;'));
|
||||
expect(code, contains('Map<String, dynamic>? get memory => _memory;'));
|
||||
|
||||
// Should include verbose property
|
||||
expect(code, contains('late bool _verbose;'));
|
||||
expect(code, contains('bool get verbose => _verbose;'));
|
||||
|
||||
// Should include constructor with required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
expect(code, contains('_memory = memory ?? {};'));
|
||||
expect(code, contains('_verbose = verbose ?? false;'));
|
||||
|
||||
// Should include required method implementations
|
||||
expect(code, contains('@override'));
|
||||
expect(code, contains('void setMemory(Map<String, dynamic> memory)'));
|
||||
});
|
||||
|
||||
test('handles multiple interface implementations', () {
|
||||
final bases = ['BaseChain', 'Serializable'];
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
|
||||
// Should include BaseChain implementations
|
||||
expect(code, contains('Map<String, dynamic>? get memory'));
|
||||
expect(code, contains('bool get verbose'));
|
||||
|
||||
// Should include constructor with all required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
});
|
||||
|
||||
test('handles no interface implementations', () {
|
||||
final bases = <String>[];
|
||||
final classContract = {
|
||||
'name': 'SimpleClass',
|
||||
'docstring': 'A simple class.',
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
expect(code, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
37
helpers/tools/converter/test/utils/name_utils_test.dart
Normal file
37
helpers/tools/converter/test/utils/name_utils_test.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/name_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('NameUtils', () {
|
||||
test('converts snake_case to camelCase', () {
|
||||
expect(NameUtils.toDartName('hello_world'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('get_model_name'), equals('getModelName'));
|
||||
expect(NameUtils.toDartName('set_memory'), equals('setMemory'));
|
||||
});
|
||||
|
||||
test('handles single word correctly', () {
|
||||
expect(NameUtils.toDartName('hello'), equals('hello'));
|
||||
expect(NameUtils.toDartName('test'), equals('test'));
|
||||
});
|
||||
|
||||
test('preserves existing camelCase', () {
|
||||
expect(NameUtils.toDartName('helloWorld'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('getModelName'), equals('getModelName'));
|
||||
});
|
||||
|
||||
test('handles empty string', () {
|
||||
expect(NameUtils.toDartName(''), equals(''));
|
||||
});
|
||||
|
||||
test('handles special method names', () {
|
||||
expect(NameUtils.toDartName('__init__'), equals('new'));
|
||||
expect(NameUtils.toDartName('__str__'), equals('str'));
|
||||
expect(NameUtils.toDartName('__repr__'), equals('repr'));
|
||||
});
|
||||
|
||||
test('handles consecutive underscores', () {
|
||||
expect(NameUtils.toDartName('hello__world'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('test___name'), equals('testName'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/type_conversion_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('TypeConversionUtils', () {
|
||||
group('pythonToDartType', () {
|
||||
test('converts basic Python types to Dart types', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('str'), equals('String'));
|
||||
expect(TypeConversionUtils.pythonToDartType('int'), equals('int'));
|
||||
expect(TypeConversionUtils.pythonToDartType('bool'), equals('bool'));
|
||||
expect(TypeConversionUtils.pythonToDartType('None'), equals('void'));
|
||||
expect(TypeConversionUtils.pythonToDartType('dict'),
|
||||
equals('Map<String, dynamic>'));
|
||||
});
|
||||
|
||||
test('converts List types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('List[str]'),
|
||||
equals('List<String>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('List[int]'),
|
||||
equals('List<int>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('List[dict]'),
|
||||
equals('List<Map<String, dynamic>>'));
|
||||
});
|
||||
|
||||
test('converts Dict types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, Any]'),
|
||||
equals('Map<String, dynamic>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, int]'),
|
||||
equals('Map<String, int>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('dict'),
|
||||
equals('Map<String, dynamic>'));
|
||||
});
|
||||
|
||||
test('converts Optional types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[str]'),
|
||||
equals('String?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[int]'),
|
||||
equals('int?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[dict]'),
|
||||
equals('Map<String, dynamic>?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[List[str]]'),
|
||||
equals('List<String>?'));
|
||||
});
|
||||
|
||||
test('handles nested generic types', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('List[Optional[str]]'),
|
||||
equals('List<String?>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, List[int]]'),
|
||||
equals('Map<String, List<int>>'));
|
||||
expect(
|
||||
TypeConversionUtils.pythonToDartType(
|
||||
'Optional[Dict[str, List[int]]]'),
|
||||
equals('Map<String, List<int>>?'));
|
||||
});
|
||||
});
|
||||
|
||||
group('getDefaultValue', () {
|
||||
test('returns correct default values for basic types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('bool'), equals('false'));
|
||||
expect(TypeConversionUtils.getDefaultValue('int'), equals('0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('double'), equals('0.0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('String'), equals("''"));
|
||||
});
|
||||
|
||||
test('returns correct default values for nullable types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('bool?'), equals('false'));
|
||||
expect(TypeConversionUtils.getDefaultValue('int?'), equals('0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('double?'), equals('0.0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('String?'), equals("''"));
|
||||
});
|
||||
|
||||
test('returns correct default values for collection types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('List'), equals('[]'));
|
||||
expect(
|
||||
TypeConversionUtils.getDefaultValue('List<String>'), equals('[]'));
|
||||
expect(TypeConversionUtils.getDefaultValue('Map<String, dynamic>'),
|
||||
equals('{}'));
|
||||
expect(TypeConversionUtils.getDefaultValue('Map<String, int>'),
|
||||
equals('{}'));
|
||||
});
|
||||
|
||||
test('returns null for unknown types', () {
|
||||
expect(
|
||||
TypeConversionUtils.getDefaultValue('CustomType'), equals('null'));
|
||||
expect(TypeConversionUtils.getDefaultValue('UnknownType?'),
|
||||
equals('null'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
45
helpers/tools/converter/test/utils/type_utils_test.dart
Normal file
45
helpers/tools/converter/test/utils/type_utils_test.dart
Normal file
|
@ -0,0 +1,45 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/type_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('TypeUtils', () {
|
||||
test('castToMapList handles null input', () {
|
||||
expect(TypeUtils.castToMapList(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToMapList converts List<dynamic> to List<Map<String, dynamic>>',
|
||||
() {
|
||||
final input = [
|
||||
{'name': 'test', 'value': 1},
|
||||
{'type': 'string', 'optional': true}
|
||||
];
|
||||
final result = TypeUtils.castToMapList(input);
|
||||
expect(result, isA<List<Map<String, dynamic>>>());
|
||||
expect(result.first['name'], equals('test'));
|
||||
expect(result.last['type'], equals('string'));
|
||||
});
|
||||
|
||||
test('castToMap handles null input', () {
|
||||
expect(TypeUtils.castToMap(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToMap converts dynamic to Map<String, dynamic>', () {
|
||||
final input = {'name': 'test', 'value': 1};
|
||||
final result = TypeUtils.castToMap(input);
|
||||
expect(result, isA<Map<String, dynamic>>());
|
||||
expect(result['name'], equals('test'));
|
||||
expect(result['value'], equals(1));
|
||||
});
|
||||
|
||||
test('castToStringList handles null input', () {
|
||||
expect(TypeUtils.castToStringList(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToStringList converts List<dynamic> to List<String>', () {
|
||||
final input = ['test', 1, true];
|
||||
final result = TypeUtils.castToStringList(input);
|
||||
expect(result, isA<List<String>>());
|
||||
expect(result, equals(['test', '1', 'true']));
|
||||
});
|
||||
});
|
||||
}
|
90
helpers/tools/converter/test/yaml_handling_test.dart
Normal file
90
helpers/tools/converter/test/yaml_handling_test.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import '../lib/src/utils/yaml_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('YamlUtils', () {
|
||||
test('converts simple YAML to Map correctly', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: TestInterface
|
||||
methods:
|
||||
- name: testMethod
|
||||
arguments:
|
||||
- name: arg1
|
||||
type: str
|
||||
return_type: str
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(map, isA<Map<String, dynamic>>());
|
||||
expect(map['interfaces'], isA<List>());
|
||||
expect(map['interfaces'][0]['name'], equals('TestInterface'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('str'));
|
||||
});
|
||||
|
||||
test('handles nested YAML structures', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: TestInterface
|
||||
properties:
|
||||
- name: prop1
|
||||
type: List[str]
|
||||
has_default: true
|
||||
methods:
|
||||
- name: testMethod
|
||||
arguments:
|
||||
- name: arg1
|
||||
type: Dict[str, Any]
|
||||
is_optional: true
|
||||
return_type: Optional[int]
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(
|
||||
map['interfaces'][0]['properties'][0]['type'], equals('List[str]'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('Dict[str, Any]'));
|
||||
});
|
||||
|
||||
test('converts actual contract YAML correctly', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: LLMProtocol
|
||||
bases:
|
||||
- Protocol
|
||||
methods:
|
||||
- name: generate
|
||||
arguments:
|
||||
- name: prompts
|
||||
type: List[str]
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: List[str]
|
||||
docstring: Generate completions for the prompts.
|
||||
decorators:
|
||||
- name: abstractmethod
|
||||
is_abstract: true
|
||||
properties: []
|
||||
docstring: Protocol for language models.
|
||||
is_interface: true
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(map['interfaces'][0]['name'], equals('LLMProtocol'));
|
||||
expect(map['interfaces'][0]['bases'][0], equals('Protocol'));
|
||||
expect(map['interfaces'][0]['methods'][0]['name'], equals('generate'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('List[str]'));
|
||||
expect(map['interfaces'][0]['docstring'],
|
||||
equals('Protocol for language models.'));
|
||||
});
|
||||
});
|
||||
}
|
57
helpers/tools/converter/test/yaml_type_test.dart
Normal file
57
helpers/tools/converter/test/yaml_type_test.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import '../lib/src/utils/yaml_utils.dart';
|
||||
import '../lib/src/utils/type_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('YAML Type Casting', () {
|
||||
test('converts YAML data to properly typed maps and lists', () {
|
||||
final yamlStr = '''
|
||||
classes:
|
||||
- name: TestClass
|
||||
properties:
|
||||
- name: model_name
|
||||
type: String
|
||||
has_default: false
|
||||
- name: is_ready
|
||||
type: bool
|
||||
has_default: true
|
||||
methods:
|
||||
- name: process
|
||||
return_type: String
|
||||
arguments:
|
||||
- name: input
|
||||
type: String
|
||||
is_optional: false
|
||||
''';
|
||||
|
||||
final yamlDoc = loadYaml(yamlStr) as YamlMap;
|
||||
final data = YamlUtils.convertYamlToMap(yamlDoc);
|
||||
|
||||
final classes = data['classes'] as List;
|
||||
final firstClass = classes.first as Map<String, dynamic>;
|
||||
|
||||
// Test properties casting
|
||||
final properties =
|
||||
TypeUtils.castToMapList(firstClass['properties'] as List?);
|
||||
expect(properties, isA<List<Map<String, dynamic>>>());
|
||||
expect(properties.first['name'], equals('model_name'));
|
||||
expect(properties.first['type'], equals('String'));
|
||||
expect(properties.first['has_default'], isFalse);
|
||||
|
||||
// Test methods casting
|
||||
final methods = TypeUtils.castToMapList(firstClass['methods'] as List?);
|
||||
expect(methods, isA<List<Map<String, dynamic>>>());
|
||||
expect(methods.first['name'], equals('process'));
|
||||
expect(methods.first['return_type'], equals('String'));
|
||||
|
||||
// Test nested arguments casting
|
||||
final arguments =
|
||||
TypeUtils.castToMapList(methods.first['arguments'] as List?);
|
||||
expect(arguments, isA<List<Map<String, dynamic>>>());
|
||||
expect(arguments.first['name'], equals('input'));
|
||||
expect(arguments.first['type'], equals('String'));
|
||||
expect(arguments.first['is_optional'], isFalse);
|
||||
});
|
||||
});
|
||||
}
|
255
helpers/tools/converter/tools/extract_contracts.dart
Normal file
255
helpers/tools/converter/tools/extract_contracts.dart
Normal file
|
@ -0,0 +1,255 @@
|
|||
import 'dart:io';
|
||||
import 'package:args/args.dart';
|
||||
import 'python_parser.dart';
|
||||
|
||||
/// Represents a Python class or interface contract
|
||||
class ContractDefinition {
|
||||
final String name;
|
||||
final List<String> bases;
|
||||
final List<MethodDefinition> methods;
|
||||
final List<PropertyDefinition> properties;
|
||||
final String? docstring;
|
||||
final List<Map<String, dynamic>> decorators;
|
||||
final bool isInterface;
|
||||
|
||||
ContractDefinition({
|
||||
required this.name,
|
||||
required this.bases,
|
||||
required this.methods,
|
||||
required this.properties,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isInterface,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'bases': bases,
|
||||
'methods': methods.map((m) => m.toJson()).toList(),
|
||||
'properties': properties.map((p) => p.toJson()).toList(),
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_interface': isInterface,
|
||||
};
|
||||
|
||||
/// Create ContractDefinition from PythonClass
|
||||
factory ContractDefinition.fromPythonClass(PythonClass pythonClass) {
|
||||
return ContractDefinition(
|
||||
name: pythonClass.name,
|
||||
bases: pythonClass.bases,
|
||||
methods: pythonClass.methods
|
||||
.map((m) => MethodDefinition(
|
||||
name: m.name,
|
||||
arguments: m.parameters
|
||||
.map((p) => ArgumentDefinition(
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
isOptional: p.isOptional,
|
||||
hasDefault: p.hasDefault,
|
||||
))
|
||||
.toList(),
|
||||
returnType: m.returnType,
|
||||
docstring: m.docstring,
|
||||
decorators: m.decorators.map((d) => {'name': d}).toList(),
|
||||
isAbstract: m.isAbstract,
|
||||
))
|
||||
.toList(),
|
||||
properties: pythonClass.properties
|
||||
.map((p) => PropertyDefinition(
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
hasDefault: p.hasDefault,
|
||||
))
|
||||
.toList(),
|
||||
docstring: pythonClass.docstring,
|
||||
decorators: pythonClass.decorators.map((d) => {'name': d}).toList(),
|
||||
isInterface: pythonClass.isInterface,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a method in a contract
|
||||
class MethodDefinition {
|
||||
final String name;
|
||||
final List<ArgumentDefinition> arguments;
|
||||
final String returnType;
|
||||
final String? docstring;
|
||||
final List<Map<String, dynamic>> decorators;
|
||||
final bool isAbstract;
|
||||
|
||||
MethodDefinition({
|
||||
required this.name,
|
||||
required this.arguments,
|
||||
required this.returnType,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isAbstract,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'arguments': arguments.map((a) => a.toJson()).toList(),
|
||||
'return_type': returnType,
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_abstract': isAbstract,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a method argument
|
||||
class ArgumentDefinition {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool isOptional;
|
||||
final bool hasDefault;
|
||||
|
||||
ArgumentDefinition({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.isOptional,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'is_optional': isOptional,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a class property
|
||||
class PropertyDefinition {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool hasDefault;
|
||||
|
||||
PropertyDefinition({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Main contract extractor class
|
||||
class ContractExtractor {
|
||||
final List<ContractDefinition> interfaces = [];
|
||||
final List<ContractDefinition> classes = [];
|
||||
|
||||
/// Process a Python source file and extract contracts
|
||||
Future<void> processFile(File file) async {
|
||||
try {
|
||||
final pythonClasses = await PythonParser.parseFile(file);
|
||||
|
||||
for (final pythonClass in pythonClasses) {
|
||||
final contract = ContractDefinition.fromPythonClass(pythonClass);
|
||||
if (pythonClass.isInterface) {
|
||||
interfaces.add(contract);
|
||||
} else {
|
||||
classes.add(contract);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error processing file ${file.path}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Process all Python files in a directory recursively
|
||||
Future<void> processDirectory(String dirPath) async {
|
||||
final dir = Directory(dirPath);
|
||||
await for (final entity in dir.list(recursive: true)) {
|
||||
if (entity is File && entity.path.endsWith('.py')) {
|
||||
await processFile(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate YAML output
|
||||
Future<void> generateYaml(String outputPath) async {
|
||||
final output = {
|
||||
'interfaces': interfaces.map((i) => i.toJson()).toList(),
|
||||
'classes': classes.map((c) => c.toJson()).toList(),
|
||||
};
|
||||
|
||||
final yamlString = mapToYaml(output);
|
||||
final file = File(outputPath);
|
||||
await file.writeAsString(yamlString);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Map to YAML string with proper formatting
|
||||
String mapToYaml(Map<String, dynamic> map, {int indent = 0}) {
|
||||
final buffer = StringBuffer();
|
||||
final indentStr = ' ' * indent;
|
||||
|
||||
map.forEach((key, value) {
|
||||
if (value is Map) {
|
||||
buffer.writeln('$indentStr$key:');
|
||||
buffer
|
||||
.write(mapToYaml(value as Map<String, dynamic>, indent: indent + 2));
|
||||
} else if (value is List) {
|
||||
buffer.writeln('$indentStr$key:');
|
||||
for (var item in value) {
|
||||
if (item is Map) {
|
||||
buffer.writeln('$indentStr- ');
|
||||
buffer.write(
|
||||
mapToYaml(item as Map<String, dynamic>, indent: indent + 4));
|
||||
} else {
|
||||
buffer.writeln('$indentStr- $item');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value == null) {
|
||||
buffer.writeln('$indentStr$key: null');
|
||||
} else if (value is String) {
|
||||
// Handle multi-line strings
|
||||
if (value.contains('\n')) {
|
||||
buffer.writeln('$indentStr$key: |');
|
||||
value.split('\n').forEach((line) {
|
||||
buffer.writeln('$indentStr $line');
|
||||
});
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: "$value"');
|
||||
}
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: $value');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption('source',
|
||||
abbr: 's',
|
||||
help: 'Source directory containing Python LangChain implementation',
|
||||
mandatory: true)
|
||||
..addOption('output',
|
||||
abbr: 'o', help: 'Output YAML file path', mandatory: true);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
final sourceDir = results['source'] as String;
|
||||
final outputFile = results['output'] as String;
|
||||
|
||||
final extractor = ContractExtractor();
|
||||
await extractor.processDirectory(sourceDir);
|
||||
await extractor.generateYaml(outputFile);
|
||||
|
||||
print('Contract extraction completed successfully.');
|
||||
print('Interfaces found: ${extractor.interfaces.length}');
|
||||
print('Classes found: ${extractor.classes.length}');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print('Usage: dart extract_contracts.dart --source <dir> --output <file>');
|
||||
exit(1);
|
||||
}
|
||||
}
|
209
helpers/tools/converter/tools/generate_dart_code.dart
Normal file
209
helpers/tools/converter/tools/generate_dart_code.dart
Normal file
|
@ -0,0 +1,209 @@
|
|||
import 'dart:io';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:args/args.dart';
|
||||
import 'package:converter/src/utils/yaml_utils.dart';
|
||||
import 'package:converter/src/utils/name_utils.dart';
|
||||
import 'package:converter/src/utils/class_generator_utils.dart';
|
||||
import 'package:converter/src/utils/type_utils.dart';
|
||||
import 'package:converter/src/utils/type_conversion_utils.dart';
|
||||
|
||||
/// Generates a Dart interface from a contract
|
||||
String generateInterface(Map<String, dynamic> interface) {
|
||||
final buffer = StringBuffer();
|
||||
final name = interface['name'] as String;
|
||||
final docstring = interface['docstring'] as String?;
|
||||
|
||||
// Add documentation
|
||||
if (docstring != null) {
|
||||
buffer.writeln('/// ${docstring.replaceAll('\n', '\n/// ')}');
|
||||
}
|
||||
|
||||
// Begin interface definition
|
||||
buffer.writeln('abstract class $name {');
|
||||
|
||||
// Generate properties
|
||||
final properties = interface['properties'] as List?;
|
||||
if (properties != null) {
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
buffer.writeln(' $propType get $propName;');
|
||||
// Only generate setter if is_readonly is explicitly false
|
||||
final isReadonly = prop['is_readonly'];
|
||||
if (isReadonly != null && !isReadonly) {
|
||||
buffer.writeln(' set $propName($propType value);');
|
||||
}
|
||||
}
|
||||
if (properties.isNotEmpty) buffer.writeln();
|
||||
}
|
||||
|
||||
// Generate methods
|
||||
final methods = interface['methods'] as List?;
|
||||
if (methods != null) {
|
||||
for (final method in methods) {
|
||||
final methodName = NameUtils.toDartName(method['name'] as String);
|
||||
final returnType =
|
||||
TypeConversionUtils.pythonToDartType(method['return_type'] as String);
|
||||
final methodDoc = method['docstring'] as String?;
|
||||
final decorators = TypeUtils.castToMapList(method['decorators'] as List?);
|
||||
final isProperty = decorators.any((d) => d['name'] == 'property');
|
||||
|
||||
if (methodDoc != null) {
|
||||
buffer.writeln(' /// ${methodDoc.replaceAll('\n', '\n /// ')}');
|
||||
}
|
||||
|
||||
if (isProperty) {
|
||||
// Generate as a getter
|
||||
buffer.writeln(' $returnType get $methodName;');
|
||||
} else {
|
||||
// Generate as a method
|
||||
final isAsync = method['is_async'] == true;
|
||||
if (isAsync) {
|
||||
buffer.write(' Future<$returnType> $methodName(');
|
||||
} else {
|
||||
buffer.write(' $returnType $methodName(');
|
||||
}
|
||||
|
||||
// Generate parameters
|
||||
final params = method['arguments'] as List?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
final paramStrings = <String>[];
|
||||
|
||||
for (final param in params) {
|
||||
final paramName = NameUtils.toDartName(param['name'] as String);
|
||||
final paramType =
|
||||
TypeConversionUtils.pythonToDartType(param['type'] as String);
|
||||
final isOptional = param['is_optional'] == true;
|
||||
|
||||
if (isOptional) {
|
||||
paramStrings.add('[$paramType $paramName]');
|
||||
} else {
|
||||
paramStrings.add('$paramType $paramName');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.write(paramStrings.join(', '));
|
||||
}
|
||||
|
||||
buffer.writeln(');');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generates a Dart class implementation from a contract
|
||||
String generateClass(Map<String, dynamic> classContract) {
|
||||
final buffer = StringBuffer();
|
||||
final name = classContract['name'] as String;
|
||||
final bases = TypeUtils.castToStringList(classContract['bases'] as List?);
|
||||
final docstring = classContract['docstring'] as String?;
|
||||
|
||||
// Add documentation
|
||||
if (docstring != null) {
|
||||
buffer.writeln('/// ${docstring.replaceAll('\n', '\n/// ')}');
|
||||
}
|
||||
|
||||
// Begin class definition
|
||||
buffer.write('class $name');
|
||||
if (bases.isNotEmpty) {
|
||||
final implementsStr = bases.join(', ');
|
||||
buffer.write(' implements $implementsStr');
|
||||
}
|
||||
buffer.writeln(' {');
|
||||
|
||||
// Generate required interface implementations first
|
||||
if (bases.contains('BaseChain')) {
|
||||
buffer.write(ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract));
|
||||
}
|
||||
|
||||
// Generate properties from contract properties
|
||||
final properties =
|
||||
TypeUtils.castToMapList(classContract['properties'] as List?);
|
||||
if (properties.isNotEmpty) {
|
||||
buffer.write(ClassGeneratorUtils.generateProperties(properties));
|
||||
}
|
||||
|
||||
// Generate constructor
|
||||
if (properties.isNotEmpty || bases.contains('BaseChain')) {
|
||||
buffer.write(ClassGeneratorUtils.generateConstructor(name, properties));
|
||||
}
|
||||
|
||||
// Generate additional methods
|
||||
final methods = TypeUtils.castToMapList(classContract['methods'] as List?);
|
||||
if (methods.isNotEmpty) {
|
||||
for (final method in methods) {
|
||||
if (method['name'] != '__init__') {
|
||||
buffer.write(ClassGeneratorUtils.generateMethod(method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Main code generator class
|
||||
class DartCodeGenerator {
|
||||
final String outputDir;
|
||||
|
||||
DartCodeGenerator(this.outputDir);
|
||||
|
||||
Future<void> generateFromYaml(String yamlPath) async {
|
||||
final file = File(yamlPath);
|
||||
final content = await file.readAsString();
|
||||
final yamlDoc = loadYaml(content) as YamlMap;
|
||||
final contracts = YamlUtils.convertYamlToMap(yamlDoc);
|
||||
|
||||
// Generate interfaces
|
||||
for (final interface in contracts['interfaces'] ?? []) {
|
||||
final code = generateInterface(interface as Map<String, dynamic>);
|
||||
final fileName = '${interface['name'].toString().toLowerCase()}.dart';
|
||||
final outputFile =
|
||||
File(path.join(outputDir, 'lib', 'src', 'interfaces', fileName));
|
||||
await outputFile.create(recursive: true);
|
||||
await outputFile.writeAsString(code);
|
||||
}
|
||||
|
||||
// Generate classes
|
||||
for (final classContract in contracts['classes'] ?? []) {
|
||||
final code = generateClass(classContract as Map<String, dynamic>);
|
||||
final fileName = '${classContract['name'].toString().toLowerCase()}.dart';
|
||||
final outputFile =
|
||||
File(path.join(outputDir, 'lib', 'src', 'implementations', fileName));
|
||||
await outputFile.create(recursive: true);
|
||||
await outputFile.writeAsString(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption('contracts',
|
||||
abbr: 'c', help: 'Path to the YAML contracts file', mandatory: true)
|
||||
..addOption('output',
|
||||
abbr: 'o',
|
||||
help: 'Output directory for generated Dart code',
|
||||
mandatory: true);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
final contractsFile = results['contracts'] as String;
|
||||
final outputDir = results['output'] as String;
|
||||
|
||||
final generator = DartCodeGenerator(outputDir);
|
||||
await generator.generateFromYaml(contractsFile);
|
||||
|
||||
print('Code generation completed successfully.');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print(
|
||||
'Usage: dart generate_dart_code.dart --contracts <file> --output <dir>');
|
||||
exit(1);
|
||||
}
|
||||
}
|
443
helpers/tools/converter/tools/python_parser.dart
Normal file
443
helpers/tools/converter/tools/python_parser.dart
Normal file
|
@ -0,0 +1,443 @@
|
|||
import 'dart:io';
|
||||
|
||||
/// Represents a Python method parameter
|
||||
class Parameter {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool isOptional;
|
||||
final bool hasDefault;
|
||||
|
||||
Parameter({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.isOptional,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'is_optional': isOptional,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python method
|
||||
class Method {
|
||||
final String name;
|
||||
final List<Parameter> parameters;
|
||||
final String returnType;
|
||||
final String? docstring;
|
||||
final List<String> decorators;
|
||||
final bool isAsync;
|
||||
final bool isAbstract;
|
||||
final bool isProperty;
|
||||
|
||||
Method({
|
||||
required this.name,
|
||||
required this.parameters,
|
||||
required this.returnType,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isAsync,
|
||||
required this.isAbstract,
|
||||
required this.isProperty,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'parameters': parameters.map((p) => p.toJson()).toList(),
|
||||
'return_type': returnType,
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_async': isAsync,
|
||||
'is_abstract': isAbstract,
|
||||
'is_property': isProperty,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python class property
|
||||
class Property {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool hasDefault;
|
||||
final String? defaultValue;
|
||||
|
||||
Property({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.hasDefault,
|
||||
this.defaultValue,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'has_default': hasDefault,
|
||||
if (defaultValue != null) 'default_value': defaultValue,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python class
|
||||
class PythonClass {
|
||||
final String name;
|
||||
final List<String> bases;
|
||||
final List<Method> methods;
|
||||
final List<Property> properties;
|
||||
final String? docstring;
|
||||
final List<String> decorators;
|
||||
final bool isInterface;
|
||||
|
||||
PythonClass({
|
||||
required this.name,
|
||||
required this.bases,
|
||||
required this.methods,
|
||||
required this.properties,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isInterface,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'bases': bases,
|
||||
'methods': methods.map((m) => m.toJson()).toList(),
|
||||
'properties': properties.map((p) => p.toJson()).toList(),
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_interface': isInterface,
|
||||
};
|
||||
}
|
||||
|
||||
/// Parser for Python source code
|
||||
class PythonParser {
|
||||
/// Check if a line looks like code
|
||||
static bool _isCodeLine(String line) {
|
||||
return line.startsWith('def ') ||
|
||||
line.startsWith('@') ||
|
||||
line.startsWith('class ') ||
|
||||
line.contains(' = ') ||
|
||||
line.contains('self.') ||
|
||||
line.contains('return ') ||
|
||||
line.contains('pass') ||
|
||||
line.contains('raise ') ||
|
||||
line.contains('yield ') ||
|
||||
line.contains('async ') ||
|
||||
line.contains('await ') ||
|
||||
(line.contains(':') && !line.startsWith('Note:')) ||
|
||||
line.trim().startsWith('"""') ||
|
||||
line.trim().endsWith('"""');
|
||||
}
|
||||
|
||||
/// Parse a docstring from Python code lines
|
||||
static String? _parseDocstring(
|
||||
List<String> lines, int startIndex, int baseIndent) {
|
||||
if (startIndex >= lines.length) return null;
|
||||
|
||||
final line = lines[startIndex].trim();
|
||||
if (!line.startsWith('"""')) return null;
|
||||
|
||||
// Handle single-line docstring
|
||||
if (line.endsWith('"""') && line.length > 6) {
|
||||
return line.substring(3, line.length - 3).trim();
|
||||
}
|
||||
|
||||
final docLines = <String>[];
|
||||
// Add first line content if it exists after the opening quotes
|
||||
var firstLineContent = line.substring(3).trim();
|
||||
if (firstLineContent.isNotEmpty && !_isCodeLine(firstLineContent)) {
|
||||
docLines.add(firstLineContent);
|
||||
}
|
||||
|
||||
var i = startIndex + 1;
|
||||
while (i < lines.length) {
|
||||
final currentLine = lines[i].trim();
|
||||
|
||||
// Stop at closing quotes
|
||||
if (currentLine.endsWith('"""')) {
|
||||
// Add the last line content if it exists before the closing quotes
|
||||
var lastLineContent =
|
||||
currentLine.substring(0, currentLine.length - 3).trim();
|
||||
if (lastLineContent.isNotEmpty && !_isCodeLine(lastLineContent)) {
|
||||
docLines.add(lastLineContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Only add non-code lines
|
||||
if (currentLine.isNotEmpty && !_isCodeLine(currentLine)) {
|
||||
docLines.add(currentLine);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return docLines.isEmpty ? null : docLines.join('\n').trim();
|
||||
}
|
||||
|
||||
/// Get the indentation level of a line
|
||||
static int _getIndentation(String line) {
|
||||
return line.length - line.trimLeft().length;
|
||||
}
|
||||
|
||||
/// Parse method parameters from a parameter string
|
||||
static List<Parameter> _parseParameters(String paramsStr) {
|
||||
if (paramsStr.trim().isEmpty) return [];
|
||||
|
||||
final params = <Parameter>[];
|
||||
var depth = 0;
|
||||
var currentParam = StringBuffer();
|
||||
|
||||
// Handle nested brackets in parameter types
|
||||
for (var i = 0; i < paramsStr.length; i++) {
|
||||
final char = paramsStr[i];
|
||||
if (char == '[') depth++;
|
||||
if (char == ']') depth--;
|
||||
if (char == ',' && depth == 0) {
|
||||
final param = currentParam.toString().trim();
|
||||
if (param.isNotEmpty && param != 'self' && !param.startsWith('**')) {
|
||||
final paramObj = _parseParameter(param);
|
||||
if (paramObj != null) {
|
||||
params.add(paramObj);
|
||||
}
|
||||
}
|
||||
currentParam.clear();
|
||||
} else {
|
||||
currentParam.write(char);
|
||||
}
|
||||
}
|
||||
|
||||
final lastParam = currentParam.toString().trim();
|
||||
if (lastParam.isNotEmpty &&
|
||||
lastParam != 'self' &&
|
||||
!lastParam.startsWith('**')) {
|
||||
final paramObj = _parseParameter(lastParam);
|
||||
if (paramObj != null) {
|
||||
params.add(paramObj);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/// Parse a single parameter
|
||||
static Parameter? _parseParameter(String param) {
|
||||
if (param.isEmpty) return null;
|
||||
|
||||
var name = param;
|
||||
var type = 'Any';
|
||||
var hasDefault = false;
|
||||
var isOptional = false;
|
||||
|
||||
// Check for type annotation
|
||||
if (param.contains(':')) {
|
||||
final parts = param.split(':');
|
||||
name = parts[0].trim();
|
||||
var typeStr = parts[1];
|
||||
|
||||
// Handle default value
|
||||
if (typeStr.contains('=')) {
|
||||
final typeParts = typeStr.split('=');
|
||||
typeStr = typeParts[0];
|
||||
hasDefault = true;
|
||||
isOptional = true;
|
||||
}
|
||||
|
||||
type = typeStr.trim();
|
||||
|
||||
// Handle Optional type
|
||||
if (type.startsWith('Optional[')) {
|
||||
type = type.substring(9, type.length - 1);
|
||||
isOptional = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for default value without type annotation
|
||||
if (param.contains('=')) {
|
||||
hasDefault = true;
|
||||
isOptional = true;
|
||||
if (!param.contains(':')) {
|
||||
name = param.split('=')[0].trim();
|
||||
}
|
||||
}
|
||||
|
||||
return Parameter(
|
||||
name: name,
|
||||
type: type,
|
||||
isOptional: isOptional,
|
||||
hasDefault: hasDefault,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a method definition
|
||||
static Method? _parseMethod(
|
||||
List<String> lines, int startIndex, List<String> decorators) {
|
||||
final line = lines[startIndex].trim();
|
||||
if (!line.startsWith('def ') && !line.startsWith('async def ')) return null;
|
||||
|
||||
final methodMatch =
|
||||
RegExp(r'(?:async\s+)?def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?\s*:')
|
||||
.firstMatch(line);
|
||||
if (methodMatch == null) return null;
|
||||
|
||||
final name = methodMatch.group(1)!;
|
||||
final paramsStr = methodMatch.group(2) ?? '';
|
||||
var returnType = methodMatch.group(3) ?? 'None';
|
||||
returnType = returnType.trim();
|
||||
|
||||
final isAsync = line.contains('async ');
|
||||
final isAbstract = decorators.contains('abstractmethod');
|
||||
final isProperty = decorators.contains('property');
|
||||
|
||||
// Parse docstring if present
|
||||
var i = startIndex + 1;
|
||||
String? docstring;
|
||||
if (i < lines.length) {
|
||||
final nextLine = lines[i].trim();
|
||||
if (nextLine.startsWith('"""')) {
|
||||
docstring =
|
||||
_parseDocstring(lines, i, _getIndentation(lines[startIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
return Method(
|
||||
name: name,
|
||||
parameters: _parseParameters(paramsStr),
|
||||
returnType: returnType,
|
||||
docstring: docstring,
|
||||
decorators: decorators,
|
||||
isAsync: isAsync,
|
||||
isAbstract: isAbstract,
|
||||
isProperty: isProperty,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a property definition
|
||||
static Property? _parseProperty(String line) {
|
||||
if (!line.contains(':') || line.contains('def ')) return null;
|
||||
|
||||
final propertyMatch =
|
||||
RegExp(r'(\w+)\s*:\s*(.+?)(?:\s*=\s*(.+))?$').firstMatch(line);
|
||||
if (propertyMatch == null) return null;
|
||||
|
||||
final name = propertyMatch.group(1)!;
|
||||
final type = propertyMatch.group(2)!;
|
||||
final defaultValue = propertyMatch.group(3);
|
||||
|
||||
return Property(
|
||||
name: name,
|
||||
type: type.trim(),
|
||||
hasDefault: defaultValue != null,
|
||||
defaultValue: defaultValue?.trim(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse Python source code into a list of classes
|
||||
static Future<List<PythonClass>> parseFile(File file) async {
|
||||
final content = await file.readAsString();
|
||||
final lines = content.split('\n');
|
||||
final classes = <PythonClass>[];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
final line = lines[i];
|
||||
final trimmedLine = line.trim();
|
||||
|
||||
if (trimmedLine.startsWith('class ')) {
|
||||
final classMatch =
|
||||
RegExp(r'class\s+(\w+)(?:\((.*?)\))?:').firstMatch(trimmedLine);
|
||||
if (classMatch != null) {
|
||||
final className = classMatch.group(1)!;
|
||||
final basesStr = classMatch.group(2);
|
||||
final bases =
|
||||
basesStr?.split(',').map((b) => b.trim()).toList() ?? [];
|
||||
final isInterface = bases.any((b) => b.contains('Protocol'));
|
||||
|
||||
final classIndent = _getIndentation(line);
|
||||
var currentDecorators = <String>[];
|
||||
final methods = <Method>[];
|
||||
final properties = <Property>[];
|
||||
|
||||
// Parse class docstring
|
||||
var j = i + 1;
|
||||
String? docstring;
|
||||
if (j < lines.length && lines[j].trim().startsWith('"""')) {
|
||||
docstring = _parseDocstring(lines, j, classIndent);
|
||||
// Skip past docstring
|
||||
while (j < lines.length && !lines[j].trim().endsWith('"""')) {
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
// Parse class body
|
||||
while (j < lines.length) {
|
||||
final currentLine = lines[j];
|
||||
final currentIndent = _getIndentation(currentLine);
|
||||
final trimmedCurrentLine = currentLine.trim();
|
||||
|
||||
// Check if we're still in the class
|
||||
if (trimmedCurrentLine.isNotEmpty && currentIndent <= classIndent) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip empty lines
|
||||
if (trimmedCurrentLine.isEmpty) {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse decorators
|
||||
if (trimmedCurrentLine.startsWith('@')) {
|
||||
currentDecorators
|
||||
.add(trimmedCurrentLine.substring(1).split('(')[0].trim());
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse methods
|
||||
if (trimmedCurrentLine.startsWith('def ') ||
|
||||
trimmedCurrentLine.startsWith('async def ')) {
|
||||
final method =
|
||||
_parseMethod(lines, j, List.from(currentDecorators));
|
||||
if (method != null) {
|
||||
methods.add(method);
|
||||
currentDecorators = [];
|
||||
// Skip past method body
|
||||
while (j < lines.length - 1) {
|
||||
final nextLine = lines[j + 1];
|
||||
if (nextLine.trim().isEmpty ||
|
||||
_getIndentation(nextLine) <= currentIndent) {
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse properties
|
||||
final property = _parseProperty(trimmedCurrentLine);
|
||||
if (property != null) {
|
||||
properties.add(property);
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
i = j - 1;
|
||||
|
||||
classes.add(PythonClass(
|
||||
name: className,
|
||||
bases: bases,
|
||||
methods: methods,
|
||||
properties: properties,
|
||||
docstring: docstring,
|
||||
decorators: [],
|
||||
isInterface: isInterface,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
}
|
152
helpers/tools/converter/tools/setup_langchain.sh
Normal file
152
helpers/tools/converter/tools/setup_langchain.sh
Normal file
|
@ -0,0 +1,152 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
TEMP_DIR="$PACKAGE_DIR/temp"
|
||||
CONTRACTS_FILE="$TEMP_DIR/contracts.yaml"
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print step information
|
||||
print_step() {
|
||||
echo -e "${BLUE}=== $1 ===${NC}"
|
||||
}
|
||||
|
||||
# Ensure required commands are available
|
||||
check_requirements() {
|
||||
print_step "Checking requirements"
|
||||
|
||||
commands=("git" "dart" "pub")
|
||||
for cmd in "${commands[@]}"; do
|
||||
if ! command -v $cmd &> /dev/null; then
|
||||
echo "Error: $cmd is required but not installed."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create necessary directories
|
||||
setup_directories() {
|
||||
print_step "Setting up directories"
|
||||
|
||||
mkdir -p "$TEMP_DIR"
|
||||
mkdir -p "$PACKAGE_DIR/lib/src/interfaces"
|
||||
mkdir -p "$PACKAGE_DIR/lib/src/implementations"
|
||||
}
|
||||
|
||||
# Clone Python LangChain repository
|
||||
clone_langchain() {
|
||||
print_step "Cloning Python LangChain repository"
|
||||
|
||||
if [ -d "$TEMP_DIR/langchain" ]; then
|
||||
echo "Updating existing LangChain repository..."
|
||||
cd "$TEMP_DIR/langchain"
|
||||
git pull
|
||||
else
|
||||
echo "Cloning LangChain repository..."
|
||||
git clone https://github.com/langchain-ai/langchain.git "$TEMP_DIR/langchain"
|
||||
fi
|
||||
}
|
||||
|
||||
# Extract contracts from Python code
|
||||
extract_contracts() {
|
||||
print_step "Extracting contracts from Python code"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
dart run "$SCRIPT_DIR/extract_contracts.dart" \
|
||||
--source "$TEMP_DIR/langchain/langchain" \
|
||||
--output "$CONTRACTS_FILE"
|
||||
}
|
||||
|
||||
# Generate Dart code from contracts
|
||||
generate_dart_code() {
|
||||
print_step "Generating Dart code from contracts"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
dart run "$SCRIPT_DIR/generate_dart_code.dart" \
|
||||
--contracts "$CONTRACTS_FILE" \
|
||||
--output "$PACKAGE_DIR"
|
||||
}
|
||||
|
||||
# Update package dependencies
|
||||
update_dependencies() {
|
||||
print_step "Updating package dependencies"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
|
||||
# Ensure required dependencies are in pubspec.yaml
|
||||
if ! grep -q "yaml:" pubspec.yaml; then
|
||||
echo "
|
||||
dependencies:
|
||||
yaml: ^3.1.0
|
||||
path: ^1.8.0
|
||||
args: ^2.3.0" >> pubspec.yaml
|
||||
fi
|
||||
|
||||
dart pub get
|
||||
}
|
||||
|
||||
# Create package exports file
|
||||
create_exports() {
|
||||
print_step "Creating package exports"
|
||||
|
||||
cat > "$PACKAGE_DIR/lib/langchain.dart" << EOL
|
||||
/// LangChain for Dart
|
||||
///
|
||||
/// This is a Dart implementation of LangChain, providing tools and utilities
|
||||
/// for building applications powered by large language models (LLMs).
|
||||
library langchain;
|
||||
|
||||
// Export interfaces
|
||||
export 'src/interfaces/llm.dart';
|
||||
export 'src/interfaces/chain.dart';
|
||||
export 'src/interfaces/prompt.dart';
|
||||
export 'src/interfaces/memory.dart';
|
||||
export 'src/interfaces/embeddings.dart';
|
||||
export 'src/interfaces/document.dart';
|
||||
export 'src/interfaces/vectorstore.dart';
|
||||
export 'src/interfaces/tool.dart';
|
||||
export 'src/interfaces/agent.dart';
|
||||
|
||||
// Export implementations
|
||||
export 'src/implementations/llm.dart';
|
||||
export 'src/implementations/chain.dart';
|
||||
export 'src/implementations/prompt.dart';
|
||||
export 'src/implementations/memory.dart';
|
||||
export 'src/implementations/embeddings.dart';
|
||||
export 'src/implementations/document.dart';
|
||||
export 'src/implementations/vectorstore.dart';
|
||||
export 'src/implementations/tool.dart';
|
||||
export 'src/implementations/agent.dart';
|
||||
EOL
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
print_step "Starting LangChain setup"
|
||||
|
||||
check_requirements
|
||||
setup_directories
|
||||
clone_langchain
|
||||
extract_contracts
|
||||
generate_dart_code
|
||||
update_dependencies
|
||||
create_exports
|
||||
|
||||
echo -e "${GREEN}Setup completed successfully!${NC}"
|
||||
echo "Next steps:"
|
||||
echo "1. Review generated code in lib/src/"
|
||||
echo "2. Implement TODOs in the generated classes"
|
||||
echo "3. Add tests for the implementations"
|
||||
echo "4. Update the documentation"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
53
helpers/uninstall_code_extensions.sh
Executable file
53
helpers/uninstall_code_extensions.sh
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Array of extensions to uninstall
|
||||
extensions=(
|
||||
"nash.awesome-flutter-snippets" # Dart Data Class Generator
|
||||
"robert-brunhage.flutter-riverpod-snippets" # Flutter Riverpod Snippets
|
||||
"usernamehw.errorlens" # Error Lens
|
||||
"aaron-bond.better-comments" # Better Comments
|
||||
"styfle.remove-comments" # Remove Comments
|
||||
"patbenatar.advanced-new-file" # Advanced New File
|
||||
"GitHub.copilot" # GitHub Copilot
|
||||
"dracula-theme.theme-dracula" # Dracula Theme (optional)
|
||||
"firebase.vscode-firebase-explorer" # Firebase Explorer
|
||||
"pflannery.vscode-versionlens" # Version Lens
|
||||
"tgsup.find-unused-dart-files-and-assets" # Find Unused Dart Files & Assets
|
||||
"humao.rest-client" # REST Client
|
||||
"rangav.vscode-thunder-client" # Thunder Client
|
||||
"ritwickdey.liveserver" # Live Server
|
||||
"Dart-Code.dart-code" # Dart SDK
|
||||
"Dart-Code.flutter" # Flutter SDK
|
||||
"ms-vscode.cpptools" # C/C++
|
||||
"ms-vscode.cpptools-extension-pack" # C/C++ Extension Pack
|
||||
"ms-vscode.cpptools-themes" # C/C++ Themes
|
||||
"twxs.cmake " # CMake
|
||||
"ms-vscode.cmake-tools" # CMake Tools
|
||||
"ms-vscode.makefile-tools" # Makefile Tools
|
||||
"saoudrizwan.claude-dev" # Claude Dev
|
||||
"Continue.continue" # Continue
|
||||
"DEVSENSE.phptools-vscode" # PHP Tools
|
||||
"DEVSENSE.composer-php-vscode" # Composer PHP
|
||||
"DEVSENSE.profiler-php-vscode" # Profiler PHP
|
||||
"ms-vscode.remote-explorer" # Remote - Containers
|
||||
"ms-vscode-remote.remote-ssh" # Remote - SSH
|
||||
"ms-vscode-remote.remote-ssh-edit" # Remote - SSH: Edit
|
||||
"ms-vscode-remote.remote-containers" # Remote - Containers
|
||||
"eamodio.gitlens" # GitLens
|
||||
"DEVSENSE.intelli-php-vscode" # IntelliPHP
|
||||
"blaugold.melos-code" # Melos
|
||||
"vscode-icons-team.vscode-icons" # VSCode Icons
|
||||
"redhat.vscode-yaml" # YAML
|
||||
"GitHub.vscode-github-actions" # GitHub Actions
|
||||
"ms-azuretools.vscode-docker" # Docker
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools" # Kubernetes
|
||||
)
|
||||
|
||||
# Uninstall each extension
|
||||
echo "Uninstalling VSCode extensions..."
|
||||
for extension in "${extensions[@]}"; do
|
||||
code --uninstall-extension "$extension"
|
||||
echo "Uninstalled: $extension"
|
||||
done
|
||||
|
||||
echo "All specified extensions have been uninstalled successfully."
|
|
@ -13,6 +13,7 @@ repository: https://github.com/protevus/platform
|
|||
packages:
|
||||
- apps/**
|
||||
- packages/**
|
||||
- helpers/tools/**
|
||||
- examples/**
|
||||
|
||||
command:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// An easily-extensible web server framework in Dart.
|
||||
library platform_core;
|
||||
|
||||
export 'package:platform_exceptions/http_exception.dart';
|
||||
export 'package:platform_support/exceptions.dart';
|
||||
export 'package:platform_model/model.dart';
|
||||
export 'package:platform_route/route.dart';
|
||||
export 'src/core/core.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' show Cookie;
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:platform_route/route.dart';
|
||||
import 'package:belatuk_combinator/belatuk_combinator.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
|
||||
import 'service.dart';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
library angel_framework.http.metadata;
|
||||
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
|
||||
import 'hooked_service.dart' show HookedServiceEventListener;
|
||||
import 'request_context.dart';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:async';
|
|||
import 'dart:collection' show HashMap;
|
||||
import 'dart:convert';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:platform_route/route.dart';
|
||||
import 'package:belatuk_combinator/belatuk_combinator.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
library platform_core.http.service;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:belatuk_merge_map/belatuk_merge_map.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
import '../util.dart';
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:platform_container/container.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
final RegExp _comma = RegExp(r',\s*');
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:io';
|
|||
import 'package:platform_core/core.dart' hide Header;
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'http2_request_context.dart';
|
||||
import 'http2_response_context.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
|
|
@ -389,20 +389,6 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_exceptions:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../exceptions"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_mocking:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../mocking"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_model:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -417,6 +403,20 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_support:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../support"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_testing:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../testing"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -8,10 +8,10 @@ environment:
|
|||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
platform_container: ^9.0.0
|
||||
platform_exceptions: ^9.0.0
|
||||
platform_model: ^9.0.0
|
||||
platform_route: ^9.0.0
|
||||
platform_mocking: ^9.0.0
|
||||
platform_support: ^9.0.0
|
||||
platform_testing: ^9.0.0
|
||||
belatuk_merge_map: ^5.1.0
|
||||
belatuk_combinator: ^5.2.0
|
||||
belatuk_http_server: ^4.4.0
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final Uri endpoint = Uri.parse('http://example.com/accept');
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_core/http.dart';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'dart:typed_data' show BytesBuilder;
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
Future<List<int>> getBody(MockHttpResponse rs) async {
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final Uri endpoint = Uri.parse('http://example.com');
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:io' show stderr;
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'dart:io';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
71
packages/exceptions/.gitignore
vendored
71
packages/exceptions/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
|
@ -1,72 +0,0 @@
|
|||
|
||||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
* [**Breaking**] `error` for `HttpException` is no longer mandatory
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 3.1.0
|
||||
|
||||
* Upgraded to `lints` linter
|
||||
|
||||
## 3.0.2
|
||||
|
||||
* Updated LICENSE link
|
||||
|
||||
## 3.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* Emit `is_error` and `status_code` in `toJson()`.
|
||||
* No more `camelCase` at all.
|
||||
|
||||
## 1.0.0+3
|
||||
|
||||
* Slightly relax the deserialization of `errors`.
|
||||
|
||||
## 1.0.0+2
|
||||
|
||||
* Added a backwards-compatible way to cast the `errors` List.
|
||||
|
||||
## 1.0.0+1
|
||||
|
||||
* Dart 2 updates.
|
|
@ -1,8 +0,0 @@
|
|||
# Protevus Http Exception
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_http_exception?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/http_exception/LICENSE)
|
||||
|
||||
Exception class that can be serialized to JSON and serialized to clients. Protevus's HTTP exception class.
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,11 +0,0 @@
|
|||
name: platform_exceptions
|
||||
version: 9.0.0
|
||||
description: Protevus Platform Exception package
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/exceptions
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dev_dependencies:
|
||||
lints: ^4.0.0
|
||||
test: ^1.25.8
|
71
packages/mocking/.gitignore
vendored
71
packages/mocking/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
|
@ -1,12 +0,0 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
|
@ -1,29 +0,0 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,6 +0,0 @@
|
|||
export 'src/connection_info.dart';
|
||||
export 'src/headers.dart';
|
||||
export 'src/lockable_headers.dart';
|
||||
export 'src/request.dart';
|
||||
export 'src/response.dart';
|
||||
export 'src/session.dart';
|
|
@ -1,25 +0,0 @@
|
|||
name: platform_mocking
|
||||
version: 9.0.0
|
||||
description: Protevus Platform Mocking for dart:io HttpRequests, HttpResponses, HttpHeaders, etc.
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/mocking
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
charcode: ^1.3.1
|
||||
dev_dependencies:
|
||||
http: ^1.2.2
|
||||
test: ^1.25.8
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# platform_core:
|
||||
# path: ../framework
|
||||
# platform_route:
|
||||
# path: ../route
|
||||
# platform_model:
|
||||
# path: ../model
|
||||
# platform_exceptions:
|
||||
# path: ../exceptions
|
||||
# platform_container:
|
||||
# path: ../container/container
|
7
packages/support/.gitignore
vendored
Normal file
7
packages/support/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
3
packages/support/CHANGELOG.md
Normal file
3
packages/support/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## 1.0.0
|
||||
|
||||
- Initial version.
|
39
packages/support/README.md
Normal file
39
packages/support/README.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
30
packages/support/analysis_options.yaml
Normal file
30
packages/support/analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/src/exceptions/http_exception.dart';
|
||||
|
||||
void main() =>
|
||||
throw PlatformHttpException.notFound(message: "Can't find that page!");
|
8
packages/support/lib/exceptions.dart
Normal file
8
packages/support/lib/exceptions.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
/// Support for doing something awesome.
|
||||
///
|
||||
/// More dartdocs go here.
|
||||
library;
|
||||
|
||||
export 'src/exceptions/http_exception.dart';
|
||||
|
||||
// TODO: Export any libraries intended for clients of this package.
|
15
packages/support/pubspec.yaml
Normal file
15
packages/support/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: platform_support
|
||||
description: Protevus Platform support package.
|
||||
version: 9.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^4.0.0
|
||||
test: ^1.24.0
|
7
packages/testing/.gitignore
vendored
Normal file
7
packages/testing/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
30
packages/testing/analysis_options.yaml
Normal file
30
packages/testing/analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,5 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var rq =
|
6
packages/testing/lib/http.dart
Normal file
6
packages/testing/lib/http.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
export 'src/http/connection_info.dart';
|
||||
export 'src/http/headers.dart';
|
||||
export 'src/http/lockable_headers.dart';
|
||||
export 'src/http/request.dart';
|
||||
export 'src/http/response.dart';
|
||||
export 'src/http/session.dart';
|
15
packages/testing/pubspec.yaml
Normal file
15
packages/testing/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: platform_testing
|
||||
description: Testing helper files for Protevus Platform.
|
||||
version: 9.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
charcode: ^1.3.1
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^4.0.0
|
||||
test: ^1.24.0
|
21
websites/example/assets/logo.svg
Normal file
21
websites/example/assets/logo.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="160" height="40" viewBox="0 0 160 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- P shape with gradient -->
|
||||
<path d="M10 5h15c5.523 0 10 4.477 10 10s-4.477 10-10 10H20v10H10V5z" fill="url(#gradient)"/>
|
||||
<path d="M20 15h5c2.761 0 5 2.239 5 5s-2.239 5-5 5h-5v-10z" fill="#ffffff"/>
|
||||
|
||||
<!-- Circular element representing technology/connectivity -->
|
||||
<circle cx="45" cy="20" r="8" fill="url(#gradient)" opacity="0.8"/>
|
||||
<circle cx="45" cy="20" r="4" fill="#ffffff"/>
|
||||
|
||||
<!-- Text "Protevus" -->
|
||||
<text x="65" y="28" font-family="Arial, sans-serif" font-weight="bold" font-size="24" fill="currentColor">Protevus</text>
|
||||
|
||||
<!-- Gradient definition -->
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#512BD4"/>
|
||||
<stop offset="100%" style="stop-color:#6742D9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 995 B |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue