2021-12-12 06:13:12 +02:00
import ' dart:io ' ;
2021-12-15 14:54:21 +02:00
import ' dart:typed_data ' ;
2021-12-12 06:13:12 +02:00
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. ' ,
bool noNullCheck = false }
) {
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 ) {
final name = abstractPrefix + result . name . pascalCase ;
if ( noNullCheck ) {
return name ;
}
else {
return ' $ name ? ' ;
}
}
else if ( result is TlSchemeObject ) {
final name = objectPrefix + result . name . pascalCase ;
if ( noNullCheck ) {
return name ;
}
else {
return ' $ name ? ' ;
}
}
else if ( result is TlSchemeFunction ) {
final name = functionPrefix + result . name . pascalCase ;
if ( noNullCheck ) {
return name ;
}
else {
return ' $ name ? ' ;
}
}
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. ' ,
String ? varNameInsteadOfKeyName }
) {
2021-12-15 14:47:12 +02:00
final varAccess = varNameInsteadOfKeyName ? ? " json[' $ keyName '] " ;
2021-12-12 06:13:12 +02:00
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);
final innerHandler = findFromJsonHandling (
tmp2 ,
' ' ,
scheme ,
abstractPrefix: abstractPrefix ,
functionPrefix: functionPrefix ,
objectPrefix: objectPrefix ,
varNameInsteadOfKeyName: ' e ' ,
) ;
2021-12-15 14:54:21 +02:00
final innerType = findDartType (
tmp2 ,
scheme ,
abstractPrefix: abstractPrefix ,
functionPrefix: functionPrefix ,
objectPrefix: objectPrefix ,
) ;
return ' $ varAccess == null ? < $ innerType >[] : ( $ varAccess as List<dynamic>).map((e) => ( $ innerHandler )).toList(growable: false) ' ;
2021-12-12 06:13:12 +02:00
}
final predefined = {
' double ' : ' double ' ,
' string ' : ' String ' ,
' int32 ' : ' int ' ,
' int53 ' : ' int ' ,
' Bool ' : ' bool ' ,
} ;
2021-12-15 14:36:23 +02:00
final predefinedDefault = {
' double ' : ' 0 ' ,
' string ' : " '' " ,
' int32 ' : ' 0 ' ,
' int53 ' : ' 0 ' ,
' Bool ' : ' false ' ,
} ;
2021-12-12 06:13:12 +02:00
if ( predefined . containsKey ( type ) ) {
2021-12-15 14:36:23 +02:00
return ' ( $ varAccess as ${ predefined [ type ] } ?) ?? ${ predefinedDefault [ type ] } ' ;
2021-12-12 06:13:12 +02:00
}
else if ( type = = ' int64 ' ) {
2021-12-15 14:36:23 +02:00
return " int.parse( $ varAccess ?? '0') " ;
2021-12-12 06:13:12 +02:00
}
else if ( type = = ' bytes ' ) {
2021-12-15 14:54:21 +02:00
return ' $ varAccess == null ? Uint8List(0) : base64.decode( $ varAccess ) ' ;
2021-12-12 06:13:12 +02:00
}
else {
return ' b.TdBase.fromJson( $ varAccess ) as ${ findDartType ( type , scheme , abstractPrefix: abstractPrefix , functionPrefix: functionPrefix , objectPrefix: objectPrefix ) } ' ;
}
}
String makeBaseFile ( TlSchema scheme ) {
var result = r"" "
import ' obj.dart ' as o ;
abstract class TdBase {
Map < String , dynamic > toJson ( ) ;
@ override
String toString ( ) {
return ' td::TdBase() ' ;
}
2021-12-20 03:37:30 +02:00
static final constructors = {
2021-12-12 06:13:12 +02:00
""" ;
2021-12-20 03:37:30 +02:00
2021-12-12 06:13:12 +02:00
for ( final o in scheme . objects ) {
final normName = o . name . pascalCase ;
result + = '''
2021-12-20 03:37:30 +02:00
' ${ o . name } ' : ( json ) = > o . $normName . fromJson ( json ) ,
2021-12-12 06:13:12 +02:00
''' ;
}
2021-12-20 03:37:30 +02:00
result + = """
} ;
static TdBase ? fromJson ( Map < String , dynamic > ? json ) {
if ( json = = null ) {
return null ;
}
final type = json [ ' @type ' ] as String ;
2021-12-12 06:13:12 +02:00
return constructors [ type ] ! ( json ) ;
}
}
2021-12-20 03:37:30 +02:00
""" ;
2021-12-12 06:13:12 +02:00
return result ;
}
String makeAbstractFile ( TlSchema scheme ) {
var result = r"" "
import ' dart:core ' as dc show Error ;
import ' base.dart ' as b ;
import ' obj.dart ' as o ;
typedef Func1 < T , TResult > = TResult Function ( T ) ;
class MatchError extends dc . Error { }
""" ;
for ( final ac in scheme . abstractClasses ) {
final normName = ac . name . pascalCase ;
final implementors = scheme . objects . where ( ( element ) = > element . baseType = = ac . name ) . toList ( growable: false ) ;
result + = '''
/// ${ac.doc}
abstract class $normName extends b . TdBase {
TResult match < TResult > ( {
''' ;
for ( final impl in implementors ) {
final iNormName = impl . name . pascalCase ;
result + = '''
Func1 < o . $iNormName , TResult > ? is $iNormName ,
''' ;
}
result + = '''
Func1 < $normName , TResult > ? otherwise ,
} ) {
if ( false ) { } // ignore: dead_code
''' ;
for ( final impl in implementors ) {
final iNormName = impl . name . pascalCase ;
result + = '''
else if ( this is o . $iNormName ) {
if ( is $iNormName ! = null ) {
return is $iNormName ( this as o . $iNormName ) ;
}
else if ( otherwise ! = null ) {
return otherwise ( this ) ;
}
}
''' ;
}
result + = '''
else if ( otherwise ! = null ) {
otherwise ( this ) ;
}
else if ( TResult = = null . runtimeType ) {
return null as TResult ;
}
throw MatchError ( ) ;
}
}
''' ;
}
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: ' ' , noNullCheck: true ) ;
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 ( ) ;
''' ;
}
// toString
result + = '''
@ override
String toString ( ) {
var s = ' td:: $ normName ( ' ;
// Params
final params = < String > [ ] ;
''' ;
for ( final param in o . parameters ) {
final normParamName = param . name . camelCase ;
result + = '''
params . add ( $normParamName . toString ( ) ) ;
''' ;
}
result + = '''
s + = params . join ( ' , ' ) ;
s + = ' ) ' ;
return s ;
}
''' ;
// toJson
result + = '''
@ override
Map < String , dynamic > toJson ( ) = > {
' @type ' : ' ${ o . name } ' ,
''' ;
for ( final param in o . parameters ) {
final normParamName = param . name . camelCase ;
final jsonHandling = findToJsonHandling ( param . type , normParamName , scheme ) ;
result + = '''
' ${ param . name } ' : $jsonHandling ,
''' ;
}
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 + = '''
} ) ;
''' ;
}
// toString
result + = '''
@ override
String toString ( ) {
var s = ' td:: $ normName ( ' ;
// Params
final params = < String > [ ] ;
''' ;
for ( final param in f . parameters ) {
final normParamName = param . name . camelCase ;
result + = '''
params . add ( $normParamName . toString ( ) ) ;
''' ;
}
result + = '''
s + = params . join ( ' , ' ) ;
s + = ' ) ' ;
return s ;
}
''' ;
// 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 ;
}