tdlib bindings generator for Dart
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

425 lines
9.3 KiB

import 'dart:io';
import 'tl_scheme.dart';
import 'package:recase/recase.dart';
import 'package:path/path.dart' as path;
Future<void> main(List<String> arguments) async {
if (arguments.length != 2) {
print('The program must be run with 2 arguments:');
print(' path to .tl schema');
print(' path to Dart project source folder');
exit(1);
}
final schemeFileStr = arguments[0];
final srcFolderStr = arguments[1];
if (! await File.fromUri(Uri.file(schemeFileStr)).exists()) {
print("Schema file $schemeFileStr doesn't exist");
exit(1);
}
if (! await Directory.fromUri(Uri.directory(srcFolderStr)).exists()) {
print("Dart project source folder $srcFolderStr doesn't exist");
exit(1);
}
print('.tl schema folder: $schemeFileStr');
print('Dart project source folder: $srcFolderStr');
print('');
print('Reading .tl schema file...');
final schemeFileContents = await File.fromUri(Uri.file(schemeFileStr)).readAsString();
print('Parsing .tl schema file...');
final scheme = TlSchema.parse(schemeFileContents);
print('Generating...');
final baseFile = File.fromUri(Uri.file(path.join(srcFolderStr, 'base.dart')));
final abstractFile = File.fromUri(Uri.file(path.join(srcFolderStr, 'abstract.dart')));
final objFile = File.fromUri(Uri.file(path.join(srcFolderStr, 'obj.dart')));
final fnFile = File.fromUri(Uri.file(path.join(srcFolderStr, 'fn.dart')));
await baseFile.writeAsString(makeBaseFile(scheme));
await abstractFile.writeAsString(makeAbstractFile(scheme));
await objFile.writeAsString(makeObjFile(scheme));
await fnFile.writeAsString(makeFnFile(scheme));
print('Done!');
}
String findDartType(
String type,
TlSchema scheme,
{String abstractPrefix = 'a.',
String objectPrefix = 'o.',
String functionPrefix = 'f.'}
) {
if (type.startsWith('vector<')) {
final tmp1 = type.replaceFirst('vector<', '');
final tmp2 = tmp1.substring(0, tmp1.length - 1);
final innerType = findDartType(
tmp2,
scheme,
abstractPrefix: abstractPrefix,
functionPrefix: functionPrefix,
objectPrefix: objectPrefix,
);
return 'List<$innerType>';
}
final predefined = {
'double': 'double',
'string': 'String',
'int32': 'int',
'int53': 'int',
'int64': 'int',
'bytes': 'Uint8List',
'Bool': 'bool',
};
if (predefined.containsKey(type)) {
return predefined[type]!;
}
final result = scheme.findType(type);
if (result == null) {
throw Exception("Couldn't find type: $type");
}
if (result is TlSchemeAbstractClass) {
return abstractPrefix + result.name.pascalCase;
}
else if (result is TlSchemeObject) {
return objectPrefix + result.name.pascalCase;
}
else if (result is TlSchemeFunction) {
return functionPrefix + result.name.pascalCase;
}
else {
throw Exception('Unknown tl object: $result');
}
}
String findToJsonHandling(
String type,
String varName,
TlSchema scheme,
) {
if (type.startsWith('vector<')) {
final tmp1 = type.replaceFirst('vector<', '');
final tmp2 = tmp1.substring(0, tmp1.length - 1);
late String newVarName;
if (varName.startsWith('_e')) {
final num = int.parse(varName.substring(2));
newVarName = '_e${num + 1}';
}
else {
newVarName = '_e1';
}
final innerHandling = findToJsonHandling(
tmp2,
newVarName,
scheme,
);
return '$varName.map(($newVarName) => $innerHandling).toList(growable: false)';
}
final predefined = {
'double': 'double',
'string': 'String',
'int32': 'int',
'int53': 'int',
'Bool': 'bool',
};
if (predefined.containsKey(type)) {
return varName;
}
else if (type == 'int64') {
return '$varName.toString()';
}
else if (type == 'bytes') {
return 'base64.encode($varName)';
}
else {
return '$varName.toJson()';
}
}
String findFromJsonHandling(
String type,
String keyName,
TlSchema scheme,
{String abstractPrefix = 'a.',
String objectPrefix = 'o.',
String functionPrefix = 'f.'}
) {
if (type.startsWith('vector<')) {
final tmp1 = type.replaceFirst('vector<', '');
final tmp2 = tmp1.substring(0, tmp1.length - 1);
final innerType = findDartType(tmp2, scheme, abstractPrefix: abstractPrefix, functionPrefix: functionPrefix, objectPrefix: objectPrefix);
return "(json['$keyName'] as List<dynamic>).map((e) => b.TdBase.fromJson(e) as $innerType).toList(growable: false)";
}
final predefined = {
'double': 'double',
'string': 'String',
'int32': 'int',
'int53': 'int',
'Bool': 'bool',
};
if (predefined.containsKey(type)) {
return "json['$keyName']";
}
else if (type == 'int64') {
return "int.parse(json['$keyName'])";
}
else if (type == 'bytes') {
return "base64.decode(json['$keyName'])";
}
else {
return "b.TdBase.fromJson(json['$keyName']) as ${findDartType(type, scheme, abstractPrefix: abstractPrefix, functionPrefix: functionPrefix, objectPrefix: objectPrefix)}";
}
}
String makeBaseFile(TlSchema scheme) {
var result = r"""
import 'dart:convert';
import 'dart:typed_data';
import 'abstract.dart' as a;
import 'obj.dart' as o;
import 'fn.dart' as f;
abstract class TdBase {
Map<String, dynamic> toJson();
static TdBase fromJson(Map<String, dynamic> json) {
final type = json['@type'] as String;
if (false) {}
""";
for (final o in scheme.objects) {
final normName = o.name.pascalCase;
result += '''
else if (type == '${o.name}') {
return o.$normName.fromJson(json);
}
''';
}
result += '''
else {
throw Exception('Unknown type: \$type');
}
}
}
''';
return result;
}
String makeAbstractFile(TlSchema scheme) {
var result = r"""
import 'base.dart' as b;
""";
for (final ac in scheme.abstractClasses) {
final normName = ac.name.pascalCase;
result += '''
/// ${ac.doc}
abstract class $normName extends b.TdBase {}
''';
}
return result;
}
String makeObjFile(TlSchema scheme) {
var result = r"""
import 'dart:convert';
import 'dart:typed_data';
import 'base.dart' as b;
import 'abstract.dart' as a;
""";
for (final o in scheme.objects) {
final normName = o.name.pascalCase;
final baseName = findDartType(o.baseType, scheme, objectPrefix: '');
result += '''
/// ${o.doc}
class $normName extends $baseName {
''';
for (final param in o.parameters) {
final normParamName = param.name.camelCase;
final paramType = findDartType(param.type, scheme, objectPrefix: '');
result += '''
/// ${param.doc}
final $paramType $normParamName;
''';
}
// Constructor
if (o.parameters.isNotEmpty) {
result += '''
$normName({
''';
for (final param in o.parameters) {
final normParamName = param.name.camelCase;
result += '''
required this.$normParamName,
''';
}
result += '''
});
''';
}
else {
result += '''
$normName();
''';
}
// toJson
result += '''
@override
Map<String, dynamic> toJson() => {
'@type': '${o.name}',
''';
for (final param in o.parameters) {
final normParamName = param.name.camelCase;
result += '''
'${param.name}': $normParamName,
''';
}
result += '''
};
''';
// fromJson
result += '''
factory $normName.fromJson(Map<String, dynamic> json) => $normName(
''';
for (final param in o.parameters) {
final normParamName = param.name.camelCase;
final handle = findFromJsonHandling(param.type, param.name, scheme, objectPrefix: '');
result += '''
$normParamName: $handle,
''';
}
result += '''
);
''';
result += '''
}
''';
}
return result;
}
String makeFnFile(TlSchema scheme) {
var result = r"""
import 'dart:convert';
import 'dart:typed_data';
import 'base.dart' as b;
import 'abstract.dart' as a;
import 'obj.dart' as o;
abstract class TdFunction extends b.TdBase {}
""";
for (final f in scheme.functions) {
final normName = f.name.pascalCase;
result += '''
/// ${f.doc}
class $normName extends TdFunction {
''';
// Parameters
for (final param in f.parameters) {
final pNormName = param.name.camelCase;
final pType = findDartType(param.type, scheme, functionPrefix: '');
result += '''
/// ${param.doc}
final $pType $pNormName;
''';
}
// Constructor
if (f.parameters.isEmpty) {
result += '''
$normName();
''';
}
else {
result += '''
$normName({
''';
for (final param in f.parameters) {
final pNormName = param.name.camelCase;
result += '''
required this.$pNormName,
''';
}
result += '''
});
''';
}
// toJson
result += '''
@override
Map<String, dynamic> toJson() => {
'@type': '${f.name}',
''';
for (final param in f.parameters) {
final pNormName = param.name.camelCase;
final jsonHandling = findToJsonHandling(param.type, pNormName, scheme);
result += '''
'${param.name}': $jsonHandling,
''';
}
result += '''
};
''';
// fromJson
result += '''
factory $normName.fromJson(Map<String, dynamic> json) => $normName(
''';
for (final param in f.parameters) {
final normParamName = param.name.camelCase;
final handle = findFromJsonHandling(param.type, param.name, scheme);
result += '''
$normParamName: $handle,
''';
}
result += '''
);
''';
result += '''
}
''';
}
return result;
}