import 'dart:io'; import 'tl_scheme.dart'; import 'package:recase/recase.dart'; import 'package:path/path.dart' as path; Future main(List 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).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 toJson(); static TdBase fromJson(Map 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 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 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 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 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; }