Compare commits

..

10 commits

Author SHA1 Message Date
Patrick Stewart
1007479792 update: adding tool dir to melos system 2024-11-11 12:52:28 -07:00
Patrick Stewart
7bf27e2e59 remove: deleting .gitkeep 2024-11-11 12:51:31 -07:00
Patrick Stewart
23c9e2b107 add: adding scripts for vscode setup 2024-11-11 12:51:08 -07:00
Patrick Stewart
16c1047bfe remove: removing support for devbox 2024-11-11 12:50:45 -07:00
Patrick Stewart
83fd901968 refactored: cleanup from refactoring 2024-11-11 12:50:12 -07:00
Patrick Stewart
601a5e6210 refactored: refactored mocking, exception to testing, support packages 2024-11-11 12:49:32 -07:00
Patrick Stewart
cb16079d14 refactored: refactored exceptions package to support 2024-11-11 12:47:52 -07:00
Patrick Stewart
a0d5f68fad refactor: refactored mocking package to testing 2024-11-11 12:46:51 -07:00
Patrick Stewart
b42e1daa89 add: adding support for project static website generation 2024-11-11 12:43:18 -07:00
Patrick Stewart
df19fbea36 add: adding extract/converter tool (wip) 2024-11-11 12:40:51 -07:00
103 changed files with 5185 additions and 416 deletions

View file

@ -3,6 +3,7 @@ repository: https://github.com/protevus/platform
packages: packages:
- apps/** - apps/**
- packages/** - packages/**
- helpers/tools/**
- examples/** - examples/**
command: command:

View file

@ -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"
]
}
}
}

View file

@ -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"
}
}
}
}
}

View 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."

View file

View 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);
}
}

View 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);
}

View 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,
};
}
}

View 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;
}
}

View 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);
}
}

View 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();
}
}

View 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__';
}
}

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

View file

@ -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';
}
}
}

View 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();
}
}

View 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();
}
}

View 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"

View 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

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

View file

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

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

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

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

View 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

View 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]}

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

View file

@ -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);
});
});
}

View file

@ -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);
});
});
}

View file

@ -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);
});
});
}

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

View file

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

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

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

View 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);
});
});
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View 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

View 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."

View file

@ -13,6 +13,7 @@ repository: https://github.com/protevus/platform
packages: packages:
- apps/** - apps/**
- packages/** - packages/**
- helpers/tools/**
- examples/** - examples/**
command: command:

View file

@ -1,7 +1,7 @@
/// An easily-extensible web server framework in Dart. /// An easily-extensible web server framework in Dart.
library platform_core; 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_model/model.dart';
export 'package:platform_route/route.dart'; export 'package:platform_route/route.dart';
export 'src/core/core.dart'; export 'src/core/core.dart';

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io' show Cookie; 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:platform_route/route.dart';
import 'package:belatuk_combinator/belatuk_combinator.dart'; import 'package:belatuk_combinator/belatuk_combinator.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:platform_exceptions/http_exception.dart'; import 'package:platform_support/exceptions.dart';
import 'service.dart'; import 'service.dart';

View file

@ -1,6 +1,6 @@
library angel_framework.http.metadata; 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 'hooked_service.dart' show HookedServiceEventListener;
import 'request_context.dart'; import 'request_context.dart';

View file

@ -4,7 +4,7 @@ import 'dart:async';
import 'dart:collection' show HashMap; import 'dart:collection' show HashMap;
import 'dart:convert'; import 'dart:convert';
import 'package:platform_container/container.dart'; 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:platform_route/route.dart';
import 'package:belatuk_combinator/belatuk_combinator.dart'; import 'package:belatuk_combinator/belatuk_combinator.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';

View file

@ -1,7 +1,7 @@
library platform_core.http.service; library platform_core.http.service;
import 'dart:async'; 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:belatuk_merge_map/belatuk_merge_map.dart';
import 'package:quiver/core.dart'; import 'package:quiver/core.dart';
import '../util.dart'; import '../util.dart';

View file

@ -5,7 +5,7 @@ import 'package:platform_container/container.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:collection/collection.dart' show IterableExtension; import 'package:collection/collection.dart' show IterableExtension;
import 'package:http2/transport.dart'; import 'package:http2/transport.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
final RegExp _comma = RegExp(r',\s*'); final RegExp _comma = RegExp(r',\s*');

View file

@ -4,7 +4,7 @@ import 'dart:io';
import 'package:platform_core/core.dart' hide Header; import 'package:platform_core/core.dart' hide Header;
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:http2/transport.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_request_context.dart';
import 'http2_response_context.dart'; import 'http2_response_context.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';

View file

@ -389,20 +389,6 @@ packages:
relative: true relative: true
source: path source: path
version: "9.0.0" 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: platform_model:
dependency: "direct main" dependency: "direct main"
description: description:
@ -417,6 +403,20 @@ packages:
relative: true relative: true
source: path source: path
version: "9.0.0" 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: pool:
dependency: transitive dependency: transitive
description: description:

View file

@ -8,10 +8,10 @@ environment:
sdk: '>=3.3.0 <4.0.0' sdk: '>=3.3.0 <4.0.0'
dependencies: dependencies:
platform_container: ^9.0.0 platform_container: ^9.0.0
platform_exceptions: ^9.0.0
platform_model: ^9.0.0 platform_model: ^9.0.0
platform_route: ^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_merge_map: ^5.1.0
belatuk_combinator: ^5.2.0 belatuk_combinator: ^5.2.0
belatuk_http_server: ^4.4.0 belatuk_http_server: ^4.4.0

View file

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
final Uri endpoint = Uri.parse('http://example.com/accept'); final Uri endpoint = Uri.parse('http://example.com/accept');

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {

View file

@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:http/http.dart' as http; 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 'package:test/test.dart';
import 'common.dart'; import 'common.dart';

View file

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {

View file

@ -6,7 +6,7 @@ import 'package:platform_core/http.dart';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:http/http.dart' as http; 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 'package:test/test.dart';
import 'common.dart'; import 'common.dart';

View file

@ -6,7 +6,7 @@ import 'dart:typed_data' show BytesBuilder;
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
Future<List<int>> getBody(MockHttpResponse rs) async { Future<List<int>> getBody(MockHttpResponse rs) async {

View file

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
final Uri endpoint = Uri.parse('http://example.com'); final Uri endpoint = Uri.parse('http://example.com');

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:http_parser/http_parser.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'; import 'package:test/test.dart';
void main() { void main() {

View file

@ -4,7 +4,7 @@ import 'dart:convert';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.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:logging/logging.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View file

@ -3,7 +3,7 @@ import 'dart:convert';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View file

@ -4,7 +4,7 @@ import 'dart:io' show stderr;
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View file

@ -4,7 +4,7 @@ import 'dart:convert';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {

View file

@ -5,7 +5,7 @@ import 'dart:io';
import 'package:platform_container/mirrors.dart'; import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View file

@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
import 'package:platform_core/core.dart'; import 'package:platform_core/core.dart';
import 'package:platform_core/http.dart'; import 'package:platform_core/http.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -1 +0,0 @@
include: package:lints/recommended.yaml

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -1 +0,0 @@
include: package:lints/recommended.yaml

View file

@ -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';

View file

@ -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
View 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

View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View 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.

View 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

View file

@ -1,4 +1,4 @@
import 'package:platform_exceptions/http_exception.dart'; import 'package:platform_support/src/exceptions/http_exception.dart';
void main() => void main() =>
throw PlatformHttpException.notFound(message: "Can't find that page!"); throw PlatformHttpException.notFound(message: "Can't find that page!");

View 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.

View 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
View 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

View 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

View file

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:platform_mocking/mocking.dart'; import 'package:platform_testing/http.dart';
Future<void> main() async { Future<void> main() async {
var rq = var rq =

View 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';

View 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

View 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