Files
tdesktop/Telegram/lib_tl/tl/generate_tl.py
allhaileris afb81b8278
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
init
2026-02-16 15:50:16 +03:00

1690 lines
75 KiB
Python

# This file is part of Desktop App Toolkit,
# a set of libraries for developing nice desktop applications.
#
# For license and copyright information please follow this link:
# https://github.com/desktop-app/legal/blob/master/LEGAL
import glob, re, binascii, os, sys
from pprint import pprint
def readInputs(inputFiles):
lines = []
names = []
layer = 0
for inputFile in inputFiles:
names.append(os.path.basename(inputFile))
lines.append('---types---')
with open(inputFile, encoding="utf-8") as f:
for line in f:
layerline = re.match(r'// LAYER (\d+)', line)
if (layerline):
layer = int(layerline.group(1))
else:
lines.append(line)
return lines, layer, names
# text serialization: types and funcs
def addTextSerialize(typeList, typeData, typesDict, idPrefix, primeType, boxed, prefix):
result = ''
for restype in typeList:
v = typeData[restype]
for data in v:
name = data[0]
prmsList = data[2]
prms = data[3]
hasFlags = data[4]
hasFlags64 = data[5]
conditionsList = data[6]
conditions = data[7]
trivialConditions = data[8]
isTemplate = data[9]
flagRawType = 'uint64' if hasFlags64 != '' else 'uint32'
templateArgument = ''
if (isTemplate != ''):
templateArgument = '<SerializedRequest>'
result += 'bool Serialize_' + name + '(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n'
if (len(conditions)):
result += '\tauto flag = ' + prefix + name + templateArgument + '::Flags::from_raw(' + flagRawType + '(iflag));\n\n'
if (len(prms)):
result += '\tif (stage) {\n'
result += '\t\tto.add(",\\n").addSpaces(lev);\n'
result += '\t} else {\n'
result += '\t\tto.add("{ ' + name + '");\n'
result += '\t\tto.add("\\n").addSpaces(lev);\n'
result += '\t}\n'
result += '\tswitch (stage) {\n'
stage = 0
for k in prmsList:
v = prms[k]
result += '\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); '
if (k == hasFlags):
if (hasFlags64 != ''):
result += 'if (start + 1 >= end) return false; else flags.back() = int64(*start) + (int64(*(start + 1)) << 32); '
else:
result += 'if (start >= end) return false; else flags.back() = *start; '
if (k in trivialConditions):
flagBitValue = int(conditions[k])
flagFieldName = hasFlags64 if flagBitValue >= 32 else hasFlags
flagBitLogged = (flagBitValue - 32) if flagBitValue >= 32 else flagBitValue
result += 'if (flag & ' + prefix + name + templateArgument + '::Flag::f_' + k + ') { '
result += 'to.add("YES [ BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); '
result += '} else { to.add("[ SKIPPED BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); } '
else:
if (k in conditions):
result += 'if (flag & ' + prefix + name + templateArgument + '::Flag::f_' + k + ') { '
result += 'types.push_back('
vtypeget = re.match(r'^[Vv]ector<MTP([A-Za-z0-9\._]+)>', v)
if (vtypeget):
if (not re.match(r'^[A-Z]', v)):
result += idPrefix + 'vector'
else:
result += '0'
restype = vtypeget.group(1)
try:
if boxed[restype]:
restype = 0
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0
else:
restype = v
try:
if boxed[restype]:
restype = 0
except KeyError:
if re.match(r'^[A-Z]', restype):
restype = 0
if (restype):
try:
conses = typesDict[restype]
if (len(conses) > 1):
print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"')
continue
if (vtypeget):
result += '); vtypes.push_back('
result += idPrefix + conses[0][0]
if (not vtypeget):
result += '); vtypes.push_back(0'
except KeyError:
if (vtypeget):
result += '); vtypes.push_back('
if (re.match(r'^flags<', restype)):
result += idPrefix + 'flags'
if (k == hasFlags):
if (hasFlags64 != ''):
result += '64'
else:
result += idPrefix + restype + '+0'
if (not vtypeget):
result += '); vtypes.push_back(0'
else:
if (not vtypeget):
result += '0'
result += '); vtypes.push_back(0'
result += '); stages.push_back(0); flags.push_back(0); '
if (k in conditions):
flagBitValue = int(conditions[k])
flagFieldName = hasFlags64 if flagBitValue >= 32 else hasFlags
flagBitLogged = (flagBitValue - 32) if flagBitValue >= 32 else flagBitValue
result += '} else { to.add("[ SKIPPED BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); } '
result += 'break;\n'
stage = stage + 1
result += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n'
result += '\t}\n'
else:
result += '\tto.add("{ ' + name + ' }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n'
result += '\treturn true;\n'
result += '}\n\n'
return result
# text serialization: types and funcs
def addTextSerializeInit(typeList, typeData, idPrefix):
result = ''
for restype in typeList:
v = typeData[restype]
for data in v:
name = data[0]
result += '\t\t{ ' + idPrefix + name + ', Serialize_' + name + ' },\n'
return result
def generate(scheme):
inputFiles = []
outputPath = ''
nextOutputPath = False
for arg in sys.argv[1:]:
if nextOutputPath:
nextOutputPath = False
outputPath = arg
elif arg == '-o':
nextOutputPath = True
elif re.match(r'^-o(.+)', arg):
outputPath = arg[2:]
else:
inputFiles.append(arg)
if len(inputFiles) == 0:
raise ValueError('Input file required.')
if outputPath == '':
raise ValueError('Output path required.')
readAndGenerate(inputFiles, outputPath, scheme)
def endsWithForTag(comments, tag, ending):
position = comments.find('@' + tag + ' ')
if (position < 0):
return False
tail = comments[(position + len(tag) + 1):]
till = tail.find('@')
line = tail[:till] if till >= 0 else tail
stripped = line.strip()
fullending = '; ' + ending.strip()
if len(stripped) < len(fullending):
return False
if stripped.endswith(fullending) or stripped.find(fullending + '.') >= 0 or stripped.find(fullending + ';') >= 0 or stripped.find(fullending + ' if') >= 0 or stripped.find(fullending + ' to') >= 0 or stripped.find(fullending + ' otherwise') >= 0 or stripped.find(fullending + ' unless') >= 0 or stripped.find(fullending + ' even') >= 0 or stripped.find(fullending + ' for') >= 0:
return True
if line.find(ending) >= 0:
print('WARNING: Found "' + ending + '" in "' + stripped + '"')
return False
def paramNameTag(name):
return 'param_description' if name == 'description' else name
def isBotsOnlyLine(comments):
return endsWithForTag(comments, 'description', 'for bots only') or endsWithForTag(comments, 'description', 'bots only')
def isBotsOnlyParam(comments, name):
return endsWithForTag(comments, paramNameTag(name), 'for bots only') or endsWithForTag(comments, paramNameTag(name), 'bots only')
def isMobileOnlyLine(comments):
return endsWithForTag(comments, 'description', 'for official mobile applications only')
def isNullableVector(comments, name):
return name.endswith('s') and endsWithForTag(comments, paramNameTag(name), name + ' may be null')
def isNullableParam(comments, name):
return endsWithForTag(comments, paramNameTag(name), 'may be null') or endsWithForTag(comments, paramNameTag(name), 'pass null')
def readAndGenerate(inputFiles, outputPath, scheme):
outputHeader = outputPath + '.h'
outputSource = outputPath + '.cpp'
outputConversionHeaderFrom = outputPath + '-conversion-from.h'
outputConversionSourceFrom = outputPath + '-conversion-from.cpp'
outputConversionHeaderTo = outputPath + '-conversion-to.h'
outputConversionSourceTo = outputPath + '-conversion-to.cpp'
outputSerializationHeader = outputPath + '-dump_to_text.h'
outputSerializationSource = outputPath + '-dump_to_text.cpp'
outputHeaderBasename = os.path.basename(outputHeader)
outputConversionHeaderFromBasename = os.path.basename(outputConversionHeaderFrom)
outputConversionHeaderToBasename = os.path.basename(outputConversionHeaderTo)
outputSerializationHeaderBasename = os.path.basename(outputSerializationHeader)
prefixes = scheme.get('prefixes', {})
dataPrefix = prefixes.get('data', '')
typePrefix = prefixes.get('type', '')
idPrefix = prefixes.get('id') + '_'
constructPrefix = prefixes.get('construct')
def normalizedName(name):
return name.replace('.', '_')
def normalizedBareName(name):
full = re.match(r'^([a-zA-Z0-9])+\.([A-Z][a-zA-Z0-9]+)$', name)
if (full):
return full.group(1) + '_' + full.group(2)[0:1].lower() + full.group(2)[1:]
elif name.find('.') >= 0:
raise ValueError('Bad name: ' + name)
return name[0:1].lower() + name[1:]
def fullTypeName(name):
return typePrefix + normalizedName(name)
def fullBareTypeName(name):
return typePrefix + normalizedBareName(name)
def fullDataName(name):
return dataPrefix + normalizedName(name)
def optionalInVector(name):
templ = re.match(r'(.*?)([vV]ector<)([A-Za-z0-9_]+)>$', name)
if templ:
return templ.group(1) + templ.group(2) + 'std::optional<' + templ.group(3) + '>>';
else:
raise ValueError('Bad optional vector: ' + name)
def handleTemplate(name, process = fullTypeName):
templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._<>]+)>$', name)
if (templ):
vectemplate = templ.group(2)
if (vectemplate.find('<') >= 0):
return templ.group(1) + process(handleTemplate(vectemplate, process)) + '>'
elif (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+\.[A-Z]', vectemplate)):
return templ.group(1) + process(vectemplate) + '>'
elif (vectemplate in builtinTypes):
return templ.group(1) + process(vectemplate) + '>'
else:
foundmeta = ''
for metatype in typesDict:
for typedata in typesDict[metatype]:
if (typedata[0] == normalizedName(vectemplate)):
foundmeta = metatype
break
if (len(foundmeta) > 0):
break
if (len(foundmeta) > 0):
return templ.group(1) + process(foundmeta) + '>'
else:
raise ValueError('Bad vector param: ' + vectemplate)
else:
raise ValueError('Bad template type: ' + name)
namespaces = scheme.get('namespaces')
globalNamespace = namespaces.get('global', '')
creatorNamespace = namespaces.get('creator', '')
creatorNamespaceFull = (globalNamespace + '::' if creatorNamespace != '' and globalNamespace != '' else globalNamespace) + creatorNamespace
# this is a map (key flags -> map (flag name -> flag bit))
# each key flag of parentFlags should be a subset of the value flag here
parentFlagsCheck = {}
flagInheritance = scheme.get('flagInheritance', {})
typeIdExceptions = scheme.get('typeIdExceptions', [])
renamedTypes = scheme.get('renamedTypes', {})
skipLines = scheme.get('skip', [])
builtinTypes = scheme.get('builtin', [])
builtinTemplateTypes = scheme.get('builtinTemplates', [])
builtinInclude = scheme.get('builtinInclude', '')
nullableTypes = scheme.get('nullable', [])
synonyms = scheme.get('synonyms', {})
writeSections = scheme.get('sections', [])
readWriteSection = 'read-write' in writeSections
primitiveTypeNames = scheme.get('types')
typeIdType = primitiveTypeNames.get('typeId')
primeType = primitiveTypeNames.get('prime', '')
bufferType = primitiveTypeNames.get('buffer', '')
writeConversion = 'conversion' in scheme
optimizeSingleData = 'optimizeSingleData' in scheme
conversionScheme = scheme.get('conversion', {})
conversionInclude = conversionScheme.get('include') if writeConversion else ''
conversionNamespace = conversionScheme.get('namespace') if writeConversion else ''
conversionBuiltinTypes = conversionScheme.get('builtinAdditional', [])
conversionBuiltinIncludeFrom = conversionScheme.get('builtinIncludeFrom', '')
conversionBuiltinIncludeTo = conversionScheme.get('builtinIncludeTo', '')
def conversionName(name):
return '::' + conversionNamespace + '::' + name
def conversionTemplate(name, param):
return conversionName(name) + '<' + conversionName(param) + '>'
def conversionPointer(name):
return conversionTemplate('object_ptr', name)
def conversionMake(name):
return conversionTemplate('make_object', name)
def conversionMove(name):
return conversionTemplate('move_object_as', name)
writeSerialization = 'dumpToText' in scheme
serializationScheme = scheme.get('dumpToText', {})
serializationInclude = serializationScheme.get('include') if writeSerialization else ''
if primeType == '' or bufferType == '':
if readWriteSection or writeSerialization:
raise ValueError('Required types not provided.')
def isBuiltinType(name):
return name in builtinTypes or name in builtinTemplateTypes
funcs = 0
types = 0
consts = 0
funcsNow = 0
enums = []
funcsDict = {}
funcsList = []
typesDict = {}
TypesDict = {}
typesList = []
TypeConstructors = {}
boxed = {}
funcsText = ''
typesText = ''
dataTexts = ''
creatorProxyText = ''
factories = ''
flagOperators = ''
methods = ''
inlineMethods = ''
visitorMethods = ''
textSerializeInit = ''
textSerializeMethods = ''
forwards = ''
forwTypedefs = ''
conversionHeaderFrom = ''
conversionHeaderTo = ''
conversionSourceFrom = ''
conversionSourceTo = ''
accumulatedComments = ''
lines, layer, names = readInputs(inputFiles)
inputNames = '\'' + '\', \''.join(names) + '\''
for line in lines:
comment = ''
nocomment = re.match(r'^(.*?)//(.*?)$', line)
if (nocomment):
line = nocomment.group(1)
comment = nocomment.group(2)
if (re.match(r'\-\-\-functions\-\-\-', line)):
funcsNow = 1
continue
if (re.match(r'\-\-\-types\-\-\-', line)):
funcsNow = 0
continue
if (re.match(r'^\s*$', line)):
if not nocomment:
accumulatedComments = ''
elif comment != '':
accumulatedComments += ' ' + comment
continue
if line.strip() in skipLines:
continue
nametype = re.match(r'([a-zA-Z\.0-9_]+)(#[0-9a-f]+)?([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line)
if (not nametype):
raise ValueError('Bad line found: ' + line)
comments = accumulatedComments
accumulatedComments = ''
if isBotsOnlyLine(comments) or isMobileOnlyLine(comments):
continue
originalname = nametype.group(1)
name = originalname
if (name in renamedTypes):
name = renamedTypes[name]
nameInd = name.rfind('.')
if (nameInd >= 0):
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:]
name = normalizedName(name)
else:
Name = name[0:1].upper() + name[1:]
typeid = nametype.group(2)
if (typeid and len(typeid) > 0):
typeid = typeid[1:]; # Skip '#'
while (typeid and len(typeid) > 0 and typeid[0] == '0'):
typeid = typeid[1:]
cleanline = nametype.group(1) + nametype.group(3) + '= ' + nametype.group(4)
cleanline = re.sub(r' [a-zA-Z0-9_]+\:flags2?\.[0-9]+\?true', '', cleanline)
cleanline = cleanline.replace('<', ' ').replace('>', ' ').replace(' ', ' ')
cleanline = re.sub(r'^ ', '', cleanline)
cleanline = re.sub(r' $', '', cleanline)
for synonym in synonyms:
synonymOf = synonyms[synonym]
cleanline = cleanline.replace(':' + synonym + ' ', ':' + synonymOf + ' ')
cleanline = cleanline.replace('?' + synonym + ' ', '?' + synonymOf + ' ')
cleanline = cleanline.replace('{', '')
cleanline = cleanline.replace('}', '')
countTypeId = binascii.crc32(binascii.a2b_qp(cleanline))
if (countTypeId < 0):
countTypeId += 2 ** 32
countTypeId = re.sub(r'^0x|L$', '', hex(countTypeId))
if (typeid and len(typeid) > 0):
typeid = typeid
if (typeid != countTypeId):
key = originalname + '#' + typeid
if (not key in typeIdExceptions):
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')')
continue
else:
typeid = countTypeId
typeid = '0x' + typeid
params = nametype.group(3)
restype = nametype.group(4)
if (restype.find('<') >= 0):
restype = handleTemplate(restype)
resType = normalizedName(restype)
if (restype.find('.') >= 0):
parts = re.match(r'([a-zA-Z0-9\.]+)\.([A-Z][A-Za-z0-9<>_]+)', restype)
if (parts):
restype = parts.group(1).replace('.', '_') + '_' + parts.group(2)[0:1].lower() + parts.group(2)[1:]
else:
raise ValueError('Bad result type name with dot: ' + restype)
else:
if (re.match(r'^[A-Z]', restype)):
restype = restype[:1].lower() + restype[1:]
else:
raise ValueError('Bad result type name: ' + restype)
boxed[resType] = restype
boxed[Name] = name
enums.append('\t' + idPrefix + name + ' = ' + typeid)
paramsList = params.strip().split(' ')
prms = {}
conditions = {}
trivialConditions = {} # true type
nullablePrms = {}
nullableVectors = {}
botsOnlyPrms = {}
prmsList = []
conditionsList = []
isTemplate = hasFlags = hasFlags64 = hasTemplate = ''
for param in paramsList:
if (re.match(r'^\s*$', param)):
continue
templ = re.match(r'^{([A-Za-z]+):Type}$', param)
if (templ):
hasTemplate = templ.group(1)
continue
pnametype = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param)
if (not pnametype):
raise ValueError('Bad param found: "' + param + '" in line: ' + line)
pname = pnametype.group(1)
ptypewide = pnametype.group(2)
botsOnlyPrm = isBotsOnlyParam(comments, pname)
nullableVector = not botsOnlyPrm and isNullableVector(comments, pname)
nullablePrm = not botsOnlyPrm and not nullableVector and isNullableParam(comments, pname)
if (re.match(r'^!([a-zA-Z]+)$', ptypewide)):
if ('!' + hasTemplate == ptypewide):
isTemplate = pname
ptype = 'TQueryType'
else:
raise ValueError('Bad template param name: "' + param + '" in line: ' + line)
if nullablePrm or nullableVector:
raise ValueError('Template param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments)
elif (ptypewide == '#'):
if hasFlags != '' and pname == hasFlags + '2':
hasFlags64 = pname
continue
hasFlags = pname
if funcsNow:
ptype = 'flags<' + fullTypeName(name) + '::Flags>'
else:
ptype = 'flags<' + fullDataName(name) + '::Flags>'
if nullablePrm or nullableVector:
raise ValueError('Flags param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments)
else:
ptype = ptypewide
if botsOnlyPrm:
botsOnlyPrms[pname] = 1
if (ptype.find('?') >= 0):
pmasktype = re.match(r'([a-z_][a-z0-9_]*)\.([0-9]+)\?([A-Za-z0-9<>\._]+)', ptype)
if not pmasktype:
raise ValueError('Bad param found: "' + param + '" in line: ' + line)
flagsName = pmasktype.group(1)
if flagsName != hasFlags and flagsName != hasFlags64:
raise ValueError('Bad param found: "' + param + '" in line: ' + line)
if nullablePrm or nullableVector:
raise ValueError('Conditional param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments)
ptype = pmasktype.group(3)
if (ptype.find('<') >= 0):
if not readWriteSection:
ptype = handleTemplate(ptype, fullBareTypeName)
else:
ptype = handleTemplate(ptype)
if (not pname in conditions):
conditionsList.append(pname)
if flagsName == hasFlags64:
conditions[pname] = str(int(pmasktype.group(2)) + 32)
else:
conditions[pname] = pmasktype.group(2)
if (ptype == 'true'):
trivialConditions[pname] = flagsName
elif (ptype.find('<') >= 0):
if nullablePrm:
raise ValueError('Vector param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments)
if nullableVector:
nullableVectors[pname] = 1
if not readWriteSection:
ptype = handleTemplate(ptype, fullBareTypeName)
else:
ptype = handleTemplate(ptype)
elif nullableVector:
raise ValueError('Non-vector param should not be vector-nullable: "' + param + '" in line: ' + line + ', comments: ' + comments)
elif nullablePrm:
nullablePrms[pname] = 1
prmsList.append(pname)
if not readWriteSection:
normalizedType = normalizedBareName(ptype)
else:
normalizedType = normalizedName(ptype)
if (normalizedType in TypeConstructors):
prms[pname] = TypeConstructors[normalizedType]['typeBare']
else:
prms[pname] = normalizedType
if (isTemplate == '' and resType == 'X'):
raise ValueError('Bad response type "X" in "' + name +'" in line: ' + line)
if funcsNow:
methodBodies = ''
if (isTemplate != ''):
funcsText += '\ntemplate <typename TQueryType>'
funcsText += '\nclass ' + fullTypeName(name) + ' { // RPC method \'' + nametype.group(1) + '\'\n'; # class
funcsText += 'public:\n'
prmsStr = []
prmsInit = []
prmsNames = []
if hasFlags != '':
flagsType = 'uint64' if hasFlags64 != '' else 'uint32'
flagsBit = '1ULL' if hasFlags64 != '' else '1U'
funcsText += '\tenum class Flag : ' + flagsType + ' {\n'
maxbit = 0
parentFlagsCheck[fullTypeName(name)] = {}
for paramName in conditionsList:
funcsText += '\t\tf_' + paramName + ' = (' + flagsBit + ' << ' + conditions[paramName] + '),\n'
parentFlagsCheck[fullTypeName(name)][paramName] = conditions[paramName]
maxbit = max(maxbit, int(conditions[paramName]))
if (maxbit > 0):
funcsText += '\n'
funcsText += '\t\tMAX_FIELD = (' + flagsBit + ' << ' + str(maxbit) + '),\n'
funcsText += '\t};\n'
funcsText += '\tusing Flags = base::flags<Flag>;\n'
funcsText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n'
funcsText += '\n'
if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)):
for paramName in prmsList:
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
paramType = prms[paramName]
prmsInit.append('_' + paramName + '(' + paramName + '_)')
prmsNames.append(paramName + '_')
if (paramName == isTemplate):
ptypeFull = paramType
else:
ptypeFull = fullTypeName(paramType)
if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags<Flags>', 'long', 'int32', 'int53', 'int64', 'double']):
prmsStr.append(ptypeFull + ' ' + paramName + '_')
elif paramName in nullableVectors:
prmsStr.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_')
elif paramName in nullablePrms:
prmsStr.append('const std::optional<' + ptypeFull + '> &' + paramName + '_')
else:
prmsStr.append('const ' + ptypeFull + ' &' + paramName + '_')
funcsText += '\t' + fullTypeName(name) + '();\n';# = default; # constructor
if (isTemplate != ''):
methodBodies += 'template <typename TQueryType>\n'
methodBodies += fullTypeName(name) + '<TQueryType>::' + fullTypeName(name) + '() = default;\n'
else:
methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '() = default;\n'
if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)):
explicitText = 'explicit ' if (len(prms) - len(trivialConditions) - len(botsOnlyPrms) == 1) else ''
funcsText += '\t' + explicitText + fullTypeName(name) + '(' + ', '.join(prmsStr) + ');\n'
if (isTemplate != ''):
methodBodies += 'template <typename TQueryType>\n'
methodBodies += fullTypeName(name) + '<TQueryType>::' + fullTypeName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'
else:
methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'
funcsText += '\t' + typeIdType + ' type() const {\n\t\treturn ' + idPrefix + name + ';\n\t}\n'; # type id
if readWriteSection:
funcsText += '\n'
funcsText += '\ttemplate <typename Prime>\n'
funcsText += '\t[[nodiscard]] bool read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons = ' + idPrefix + name + ');\n'; # read method
if (isTemplate != ''):
methodBodies += 'template <typename TQueryType>\n'
methodBodies += 'template <typename Prime>\n'
methodBodies += 'bool ' + fullTypeName(name) + '<TQueryType>::read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons) {\n'
else:
methodBodies += 'template <typename Prime>\n'
methodBodies += 'bool ' + fullTypeName(name) + '::read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons) {\n'
readFunc = ''
for k in prmsList:
v = prms[k]
if k in conditionsList:
if not k in trivialConditions:
readFunc += '\t\t&& ((_' + hasFlags + '.v & Flag::f_' + k + ') ? _' + k + '.read(from, end) : ((_' + k + ' = ' + fullTypeName(v) + '()), true))\n'
else:
readFunc += '\t\t&& _' + k + '.read(from, end)\n'
if readFunc != '':
methodBodies += '\treturn' + readFunc[4:len(readFunc)-1] + ';\n'
else:
methodBodies += '\treturn true;\n'
methodBodies += '}\n'
if isTemplate == '':
methodBodies += 'template bool ' + fullTypeName(name) + '::read<' + primeType + '>(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons);\n'
funcsText += '\ttemplate <typename Accumulator>\n'
funcsText += '\tvoid write(Accumulator &to) const;\n'; # write method
if (isTemplate != ''):
methodBodies += 'template <typename TQueryType>\n'
methodBodies += 'template <typename Accumulator>\n'
methodBodies += 'void ' + fullTypeName(name) + '<TQueryType>::write(Accumulator &to) const {\n'
else:
methodBodies += 'template <typename Accumulator>\n'
methodBodies += 'void ' + fullTypeName(name) + '::write(Accumulator &to) const {\n'
for k in prmsList:
if k in conditionsList:
if not k in trivialConditions:
methodBodies += '\tif (_' + hasFlags + '.v & Flag::f_' + k + ') _' + k + '.write(to);\n'
else:
methodBodies += '\t_' + k + '.write(to);\n'
methodBodies += '}\n'
if isTemplate == '':
methodBodies += 'template void ' + fullTypeName(name) + '::write<' + bufferType + '>(' + bufferType + ' &to) const;\n'
methodBodies += 'template void ' + fullTypeName(name) + '::write<::tl::details::LengthCounter>(::tl::details::LengthCounter &to) const;\n'
if writeConversion:
conversionSourceTo += '\n\
template <>\n\
ExternalGenerator tl_to_generator('+ fullTypeName(name) + ' &&request) {\n\
\treturn [value = std::move(request)]() -> ExternalRequest {\n\
\t\treturn new ' + conversionName(name) + '('
conversionArguments = []
for k in prmsList:
prmsTypeBare = prms[k]
if (k in conditionsList):
raise ValueError('Conversion with flags :(')
elif k in botsOnlyPrms:
conversionArguments.append('{}')
elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool':
conversionArguments.append('tl_to_simple(value.v' + k + '())')
elif prmsTypeBare.find('<') >= 0:
if (k in nullableVectors):
conversionArguments.append('tl_to_vector_optional(value.v' + k + '())')
else:
conversionArguments.append('tl_to_vector(value.v' + k + '())')
else:
if (k in nullablePrms):
conversionValue = 'value.v' + k + '() ? tl_to(*value.v' + k + '()) : nullptr'
else:
conversionValue = 'tl_to(value.v' + k + '())'
if len(typesDict[prmsTypeBare]) > 1:
conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare[0:1].upper() + prmsTypeBare[1:]) + '>(' + conversionValue + ')')
else:
conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare) + '>(' + conversionValue + ')')
conversionSourceTo += ', '.join(conversionArguments) + ');\n\
\t};\n\
}\n'
if len(prmsList) > 0:
funcsText += '\n'
for paramName in prmsList: # getters
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
paramType = prms[paramName]
ptypeFull = fullTypeName(paramType)
if (paramName in conditions):
funcsText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n'
methodBodies += 'tl::conditional<' + ptypeFull + '> ' + fullTypeName(name) + '::v' + paramName + '() const {\n'
methodBodies += '\treturn (_' + hasFlags + '.v & Flag::f_' + paramName + ') ? &_' + paramName + ' : nullptr;\n'
methodBodies += '}\n'
elif (paramName in nullableVectors):
funcsText += '\t[[nodiscard]] const ' + optionalInVector(ptypeFull) + ' &v' + paramName + '() const;\n'
methodBodies += 'const ' + optionalInVector(ptypeFull) + ' &' + fullTypeName(name) + '::v' + paramName + '() const {\n'
methodBodies += '\treturn _' + paramName + ';\n'
methodBodies += '}\n'
elif (paramName in nullablePrms):
funcsText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n'
methodBodies += 'tl::conditional<' + ptypeFull + '> ' + fullTypeName(name) + '::v' + paramName + '() const {\n'
methodBodies += '\treturn _' + paramName + ' ? &*_' + paramName + ' : nullptr;\n'
methodBodies += '}\n'
else:
funcsText += '\t[[nodiscard]] const ' + ptypeFull + ' &v' + paramName + '() const;\n'
methodBodies += 'const ' + ptypeFull + ' &' + fullTypeName(name) + '::v' + paramName + '() const {\n'
methodBodies += '\treturn _' + paramName + ';\n'
methodBodies += '}\n'
if (isTemplate != ''):
funcsText += '\n\tusing ResponseType = typename TQueryType::ResponseType;\n\n'
inlineMethods += methodBodies
else:
# method return type
if not readWriteSection:
funcsText += '\n\tusing ResponseType = ' + fullTypeName(restype) + ';\n\n'
else:
funcsText += '\n\tusing ResponseType = ' + fullTypeName(resType) + ';\n\n'
methods += methodBodies
if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)):
funcsText += 'private:\n'
for paramName in prmsList:
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
paramType = prms[paramName]
if (paramName == isTemplate):
ptypeFull = paramType
else:
ptypeFull = fullTypeName(paramType)
if (paramName in nullableVectors):
funcsText += '\t' + optionalInVector(ptypeFull) + ' _' + paramName + ';\n'
elif (paramName in nullablePrms):
funcsText += '\tstd::optional<' + ptypeFull + '> _' + paramName + ';\n'
else:
funcsText += '\t' + ptypeFull + ' _' + paramName + ';\n'
funcsText += '\n'
funcsText += '};\n'; # class ending
if readWriteSection:
if (isTemplate != ''):
funcsText += 'template <typename TQueryType>\n'
funcsText += 'using ' + fullTypeName(Name) + ' = tl::boxed<' + fullTypeName(name) + '<TQueryType>>;\n'
else:
funcsText += 'using ' + fullTypeName(Name) + ' = tl::boxed<' + fullTypeName(name) + '>;\n'
funcs = funcs + 1
if (not restype in funcsDict):
funcsList.append(restype)
funcsDict[restype] = []
# TypesDict[restype] = resType
funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, hasFlags64, conditionsList, conditions, trivialConditions, isTemplate, nullablePrms, nullableVectors, botsOnlyPrms])
else:
if (isTemplate != ''):
print('Template types not allowed: "' + resType + '" in line: ' + line)
continue
if (not restype in typesDict):
typesList.append(restype)
typesDict[restype] = []
TypesDict[restype] = resType
typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, hasFlags64, conditionsList, conditions, trivialConditions, isTemplate, nullablePrms, nullableVectors, botsOnlyPrms])
TypeConstructors[name] = {'typeBare': restype, 'typeBoxed': resType}
consts = consts + 1
if readWriteSection:
for typeName in builtinTypes:
forwTypedefs += 'using ' + fullTypeName(typeName[:1].upper() + typeName[1:]) + ' = tl::boxed<' + fullTypeName(typeName) + '>;\n'
for typeName in builtinTemplateTypes:
forwTypedefs += 'template <typename T>\n'
forwTypedefs += 'using ' + fullTypeName(typeName[:1].upper() + typeName[1:]) + ' = tl::boxed<' + fullTypeName(typeName) + '<T>>;\n'
if writeSerialization:
textSerializeMethods += addTextSerialize(typesList, typesDict, typesDict, idPrefix, primeType, boxed, dataPrefix)
textSerializeInit += addTextSerializeInit(typesList, typesDict, idPrefix) + '\n'
textSerializeMethods += addTextSerialize(funcsList, funcsDict, typesDict, idPrefix, primeType, boxed, typePrefix)
textSerializeInit += addTextSerializeInit(funcsList, funcsDict, idPrefix) + '\n'
for restype in typesList:
v = typesDict[restype]
resType = TypesDict[restype]
withData = 0
creatorsDeclarations = ''
creatorsBodies = ''
flagDeclarations = ''
constructsText = ''
constructsBodies = ''
forwards += 'class ' + fullTypeName(restype) + ';\n'
if readWriteSection:
forwTypedefs += 'using ' + fullTypeName(resType) + ' = tl::boxed<' + fullTypeName(restype) + '>;\n'
withType = (len(v) > 1)
nullable = restype in nullableTypes
switchLines = ''
friendDecl = ''
getters = ''
visitor = ''
reader = ''
writer = ''
newFast = ''
if writeConversion:
if not restype in builtinTypes and not restype in conversionBuiltinTypes:
if len(v) > 1:
conversionType = resType
conversionHeaderFrom += 'template <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response);\n'
conversionSourceFrom += '\ntemplate <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response) {\n'
if nullable:
conversionSourceFrom += '\tif (!response) {\n\t\treturn nullptr;\n\t}\n\n'
else:
conversionSourceFrom += '\tExpects(response != nullptr);\n\n'
conversionSourceFrom += '\tswitch (response->get_id()) {\n'
for data in v:
name = data[0]
prmsList = data[2]
prms = data[3]
trivialConditions = data[8]
isTemplate = data[9]
nullablePrms = data[10]
nullableVectors = data[11]
botsOnlyPrms = data[12]
conversionSourceFrom += '\tcase ' + conversionName(name) + '::ID: '
if (len(prmsList) == 0):
conversionSourceFrom += 'return ' + constructPrefix + name + '();\n'
else:
conversionSourceFrom += '{\n\t\tconst auto specific = static_cast<const ' + conversionName(name) + '*>(response);\n'
conversionSourceFrom += '\t\treturn ' + constructPrefix + name + '('
conversionArguments = []
for k in prmsList:
prmsTypeBare = prms[k]
ptypeFullBare = fullTypeName(prmsTypeBare)
if k in conditionsList:
raise ValueError('Conversion with flags :(')
elif k in botsOnlyPrms:
continue
elif prmsTypeBare == 'string':
conversionArguments.append('tl_from_string(specific->' + k + '_)')
elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool':
conversionArguments.append('tl_from_simple(specific->' + k + '_)')
elif prmsTypeBare.find('<') >= 0:
if k in nullableVectors:
conversionArguments.append('tl_from_vector_optional<' + ptypeFullBare + '>(specific->' + k + '_)')
else:
conversionArguments.append('tl_from_vector<' + ptypeFullBare + '>(specific->' + k + '_)')
else:
if k in nullablePrms:
conversionArguments.append('specific->' + k + '_.get() ? std::make_optional(tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())) : std::nullopt')
else:
conversionArguments.append('tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())')
conversionSourceFrom += ', '.join(conversionArguments) + ');\n'
conversionSourceFrom += '\t} break;\n'
conversionSourceFrom += '\tdefault: Unexpected("Type in ' + fullTypeName(restype) + ' tl_from.");\n\t}\n}\n'
else:
conversionType = v[0][0]
conversionHeaderTo += conversionName(conversionType) + ' *tl_to(const ' + fullTypeName(restype) + ' &value);\n'
conversionSourceTo += '\n' + conversionName(conversionType) + ' *tl_to(const ' + fullTypeName(restype) + ' &value) {\n'
if withType:
conversionSourceTo += '\tswitch (value.type()) {\n'
if nullable:
conversionSourceTo += '\tcase ' + typeIdType + '(0): return nullptr;\n'
for data in v:
name = data[0]
prms = data[3]
trivialConditions = data[8]
isTemplate = data[9]
nullablePrms = data[10]
nullableVectors = data[11]
botsOnlyPrms = data[12]
if (len(prms) == len(trivialConditions)):
conversionSourceTo += '\tcase ' + idPrefix + name + ': return new ' + conversionName(name) + '();\n'
else:
conversionSourceTo += '\tcase ' + idPrefix + name + ': return tl_to(value.c_' + name + '());\n'
conversionSourceTo += '\tdefault: Unexpected("Type in tl_to(' + fullTypeName(restype) + ').");\n\t}\n'
else:
if nullable:
conversionSourceTo += '\tif (!value) {\n\t\treturn nullptr;\n\t}\n'
conversionSourceTo += '\treturn tl_to(value.c_' + v[0][0] + '());\n'
conversionSourceTo += '}\n'
for data in v:
name = data[0]
typeid = data[1]
prmsList = data[2]
prms = data[3]
hasFlags = data[4]
hasFlags64 = data[5]
conditionsList = data[6]
conditions = data[7]
trivialConditions = data[8]
isTemplate = data[9]
nullablePrms = data[10]
nullableVectors = data[11]
botsOnlyPrms = data[12]
dataText = ''
if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)):
withData = 1
dataText += '\nclass ' + fullDataName(name) + ' : public tl::details::type_data {\n'; # data class
else:
dataText += '\nclass ' + fullDataName(name) + ' {\n'; # empty data class for visitors
dataText += 'public:\n'
dataText += '\ttemplate <typename Other>\n'
dataText += '\tstatic constexpr bool Is() { return std::is_same_v<std::decay_t<Other>, ' + fullDataName(name) + '>; };\n\n'
creatorParams = []
creatorParamsList = []
readText = ''
writeText = ''
if (hasFlags != ''):
flagsType = 'uint64' if hasFlags64 != '' else 'uint32'
flagsBit = '1ULL' if hasFlags64 != '' else '1U'
dataText += '\tenum class Flag : ' + flagsType + ' {\n'
maxbit = 0
parentFlagsCheck[fullDataName(name)] = {}
for paramName in conditionsList:
dataText += '\t\tf_' + paramName + ' = (' + flagsBit + ' << ' + conditions[paramName] + '),\n'
parentFlagsCheck[fullDataName(name)][paramName] = conditions[paramName]
maxbit = max(maxbit, int(conditions[paramName]))
if (maxbit > 0):
dataText += '\n'
dataText += '\t\tMAX_FIELD = (' + flagsBit + ' << ' + str(maxbit) + '),\n'
dataText += '\t};\n'
dataText += '\tusing Flags = base::flags<Flag>;\n'
dataText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n'
dataText += '\n'
if (len(conditions)):
for paramName in conditionsList:
if paramName in trivialConditions:
dataText += '\t[[nodiscard]] bool is_' + paramName + '() const;\n'
constructsBodies += 'bool ' + fullDataName(name) + '::is_' + paramName + '() const {\n'
constructsBodies += '\treturn _' + hasFlags + '.v & Flag::f_' + paramName + ';\n'
constructsBodies += '}\n'
dataText += '\n'
switchLines += '\tcase ' + idPrefix + name + ': '; # for by-type-id type constructor
getters += '\t[[nodiscard]] const ' + fullDataName(name) + ' &c_' + name + '() const;\n'; # const getter
if optimizeSingleData and withData and not withType:
getters += '\t[[nodiscard]] const ' + fullDataName(name) + ' &data() const;\n';
visitor += '\tcase ' + idPrefix + name + ': return base::match_method(c_' + name + '(), std::forward<Method>(method), std::forward<Methods>(methods)...);\n'
forwards += 'class ' + fullDataName(name) + ';\n'; # data class forward declaration
if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)):
dataText += '\t' + fullDataName(name) + '();\n'; # default constructor
switchLines += 'setData(new ' + fullDataName(name) + '()); '
constructsBodies += fullDataName(name) + '::' + fullDataName(name) + '() = default;\n'
constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::c_' + name + '() const {\n'
if (withType):
constructsBodies += '\tExpects(_type == ' + idPrefix + name + ');\n\n'
constructsBodies += '\treturn queryData<' + fullDataName(name) + '>();\n'
constructsBodies += '}\n'
if optimizeSingleData and withData and not withType:
constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::data() const {\n'
constructsBodies += '\treturn queryData<' + fullDataName(name) + '>();\n'
constructsBodies += '}\n'
constructsText += '\texplicit ' + fullTypeName(restype) + '(const ' + fullDataName(name) + ' *data);\n'; # by-data type constructor
constructsBodies += fullTypeName(restype) + '::' + fullTypeName(restype) + '(const ' + fullDataName(name) + ' *data) : type_owner(data)'
if (withType):
constructsBodies += ', _type(' + idPrefix + name + ')'
constructsBodies += ' {\n}\n'
dataText += '\t' + fullDataName(name) + '('; # params constructor
prmsStr = []
prmsInit = []
for paramName in prmsList:
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
paramType = prms[paramName]
ptypeFull = fullTypeName(paramType)
if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags<Flags>', 'long', 'int32', 'int53', 'int64', 'double']):
prmsStr.append(ptypeFull + ' ' + paramName + '_')
creatorParams.append(ptypeFull + ' ' + paramName + '_')
elif paramName in nullableVectors:
prmsStr.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_')
creatorParams.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_')
elif paramName in nullablePrms:
prmsStr.append('const std::optional<' + ptypeFull + '> &' + paramName + '_')
creatorParams.append('const std::optional<' + ptypeFull + '> &' + paramName + '_')
else:
prmsStr.append('const ' + ptypeFull + ' &' + paramName + '_')
creatorParams.append('const ' + ptypeFull + ' &' + paramName + '_')
creatorParamsList.append(paramName + '_')
prmsInit.append('_' + paramName + '(' + paramName + '_)')
if withType:
writeText += '\t'
if (paramName in conditions):
readText += '\t\t&& (v' + paramName + '() ? _' + paramName + '.read(from, end) : ((_' + paramName + ' = ' + fullTypeName(paramType) + '()), true))\n'
writeText += '\tif (const auto v' + paramName + ' = v.v' + paramName + '()) v' + paramName + '->write(to);\n'
else:
readText += '\t\t&& _' + paramName + '.read(from, end)\n'
writeText += '\tv.v' + paramName + '().write(to);\n'
dataText += ', '.join(prmsStr) + ');\n'
constructsBodies += fullDataName(name) + '::' + fullDataName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n'
if readWriteSection:
dataText += '\n'
dataText += '\t[[nodiscard]] bool read(const ' + primeType + ' *&from, const ' + primeType + ' *end);\n'
constructsBodies += 'bool ' + fullDataName(name) + '::read(const ' + primeType + ' *&from, const ' + primeType + ' *end) {\n'
if readText != '':
constructsBodies += '\treturn' + readText[4:len(readText)-1] + ';\n'
else:
constructsBodies += '\treturn true;\n'
constructsBodies += '}\n'
dataText += '\n'
if len(prmsList) > 0:
for paramName in prmsList: # getters
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
paramType = prms[paramName]
ptypeFull = fullTypeName(paramType)
if paramName in conditions:
dataText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n'
constructsBodies += 'tl::conditional<' + ptypeFull + '> ' + fullDataName(name) + '::v' + paramName + '() const {\n'
constructsBodies += '\treturn (_' + hasFlags + '.v & Flag::f_' + paramName + ') ? &_' + paramName + ' : nullptr;\n'
constructsBodies += '}\n'
elif (paramName in nullableVectors):
dataText += '\t[[nodiscard]] const ' + optionalInVector(ptypeFull) + ' &v' + paramName + '() const;\n'
constructsBodies += 'const ' + optionalInVector(ptypeFull) + ' &' + fullDataName(name) + '::v' + paramName + '() const {\n'
constructsBodies += '\treturn _' + paramName + ';\n'
constructsBodies += '}\n'
elif (paramName in nullablePrms):
dataText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n'
constructsBodies += 'tl::conditional<' + ptypeFull + '> ' + fullDataName(name) + '::v' + paramName + '() const {\n'
constructsBodies += '\treturn _' + paramName + ' ? &*_' + paramName + ' : nullptr;\n'
constructsBodies += '}\n'
else:
dataText += '\t[[nodiscard]] const ' + ptypeFull + ' &v' + paramName + '() const;\n'
constructsBodies += 'const ' + ptypeFull + ' &' + fullDataName(name) + '::v' + paramName + '() const {\n'
constructsBodies += '\treturn _' + paramName + ';\n'
constructsBodies += '}\n'
dataText += '\n'
dataText += 'private:\n'
for paramName in prmsList: # fields declaration
paramType = prms[paramName]
ptypeFull = fullTypeName(paramType)
if (paramName in trivialConditions or paramName in botsOnlyPrms):
continue
elif (paramName in nullableVectors):
dataText += '\t' + optionalInVector(ptypeFull) + ' _' + paramName + ';\n'
elif (paramName in nullablePrms):
dataText += '\tstd::optional<' + ptypeFull + '> _' + paramName + ';\n'
else:
dataText += '\t' + ptypeFull + ' _' + paramName + ';\n'
dataText += '\n'
newFast = 'new ' + fullDataName(name) + '()'
else:
constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::c_' + name + '() const {\n'
if (withType):
constructsBodies += '\tExpects(_type == ' + idPrefix + name + ');\n\n'
constructsBodies += '\tstatic const ' + fullDataName(name) + ' result;\n'
constructsBodies += '\treturn result;\n'
constructsBodies += '}\n'
if writeConversion and not restype in builtinTypes and not restype in conversionBuiltinTypes:
if (len(v) == 1):
conversionHeaderFrom += 'template <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response);\n'
conversionSourceFrom += '\ntemplate <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response) {\n'
if nullable:
conversionSourceFrom += '\tif (!response) {\n\t\treturn nullptr;\n\t}\n\n'
else:
conversionSourceFrom += '\tExpects(response != nullptr);\n'
conversionSourceFrom += '\tExpects(response->get_id() == ' + conversionName(name) + '::ID);\n\n'
if (len(prmsList) > 0):
conversionSourceFrom += '\tconst auto specific = static_cast<const ' + conversionName(name) + '*>(response);\n'
conversionSourceFrom += '\treturn ' + constructPrefix + normalizedName(name) + '('
conversionArguments = []
for k in prmsList:
prmsTypeBare = prms[k]
ptypeFullBare = fullTypeName(prmsTypeBare)
if k in conditionsList:
raise ValueError('Conversion with flags :(')
elif k in botsOnlyPrms:
continue
elif prmsTypeBare == 'string':
conversionArguments.append('tl_from_string(specific->' + k + '_)')
elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool':
conversionArguments.append('tl_from_simple(specific->' + k + '_)')
elif prmsTypeBare.find('<') >= 0:
if k in nullableVectors:
conversionArguments.append('tl_from_vector_optional<' + ptypeFullBare + '>(specific->' + k + '_)')
else:
conversionArguments.append('tl_from_vector<' + ptypeFullBare + '>(specific->' + k + '_)')
else:
if k in nullablePrms:
conversionArguments.append('specific->' + k + '_.get() ? std::make_optional(tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())) : std::nullopt')
else:
conversionArguments.append('tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())')
conversionSourceFrom += ', '.join(conversionArguments) + ');\n'
conversionSourceFrom += '}\n'
conversionHeaderTo += conversionName(name) + ' *tl_to(const ' + fullDataName(name) + ' &value);\n'
conversionSourceTo += '\n' + conversionName(name) + ' *tl_to(const ' + fullDataName(name) + ' &value) {\n'
conversionSourceTo += '\treturn new ' + conversionName(name) + '('
conversionArguments = []
for k in prmsList:
prmsTypeBare = prms[k]
if (k in conditionsList):
raise ValueError('Conversion with flags :(')
elif k in botsOnlyPrms:
conversionArguments.append('{}')
elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool':
conversionArguments.append('tl_to_simple(value.v' + k + '())')
elif prmsTypeBare.find('<') >= 0:
if (k in nullableVectors):
conversionArguments.append('tl_to_vector_optional(value.v' + k + '())')
else:
conversionArguments.append('tl_to_vector(value.v' + k + '())')
else:
if (k in nullablePrms):
conversionValue = 'value.v' + k + '() ? tl_to(*value.v' + k + '()) : nullptr'
else:
conversionValue = 'tl_to(value.v' + k + '())'
if len(typesDict[prmsTypeBare]) > 1:
conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare[0:1].upper() + prmsTypeBare[1:]) + '>(' + conversionValue + ')')
else:
conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare) + '>(' + conversionValue + ')')
conversionSourceTo += ', '.join(conversionArguments) + ');\n}\n'
switchLines += 'break;\n'
dataText += '};\n'; # class ending
dataTexts += dataText; # add data class
if not friendDecl:
friendDecl += '\tfriend class ::' + creatorNamespaceFull + '::TypeCreator;\n'
creatorProxyText += '\tinline static ' + fullTypeName(restype) + ' new_' + name + '(' + ', '.join(creatorParams) + ') {\n'
if len(prms) > len(trivialConditions): # creator with params
creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '(new ' + fullDataName(name) + '(' + ', '.join(creatorParamsList) + '));\n'
else:
if withType: # creator by type
creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '(' + idPrefix + name + ');\n'
else: # single creator
creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '();\n'
creatorProxyText += '\t}\n'
creatorsDeclarations += fullTypeName(restype) + ' ' + constructPrefix + name + '(' + ', '.join(creatorParams) + ');\n'
creatorsBodies += fullTypeName(restype) + ' ' + constructPrefix + name + '(' + ', '.join(creatorParams) + ') {\n'
creatorsBodies += '\treturn ::' + creatorNamespaceFull + '::TypeCreator::new_' + name + '(' + ', '.join(creatorParamsList) + ');\n'
creatorsBodies += '}\n'
if (withType):
reader += '\tcase ' + idPrefix + name + ': _type = cons; '; # read switch line
if (len(prms) > len(trivialConditions)):
reader += '{\n'
reader += '\t\tif (const auto data = new ' + fullDataName(name) + '(); data->read(from, end)) {\n'
reader += '\t\t\tsetData(data);\n'
reader += '\t\t} else {\n'
reader += '\t\t\tdelete data;\n'
reader += '\t\t\treturn false;\n'
reader += '\t\t}\n'
reader += '\t} break;\n'
writer += '\tcase ' + idPrefix + name + ': {\n'; # write switch line
writer += '\t\tconst ' + fullDataName(name) + ' &v = c_' + name + '();\n'
writer += writeText
writer += '\t} break;\n'
else:
reader += 'break;\n'
else:
if (len(prms) > len(trivialConditions)):
reader += '\tif (const auto data = new ' + fullDataName(name) + '(); data->read(from, end)) {\n'
reader += '\t\tsetData(data);\n'
reader += '\t} else {\n'
reader += '\t\tdelete data;\n'
reader += '\t\treturn false;\n'
reader += '\t}\n'
writer += '\tconst ' + fullDataName(name) + ' &v = c_' + name + '();\n'
writer += writeText
if nullable:
if not withType and not withData:
raise ValueError('No way to make a nullable non-data-owner non-type-distinct type')
elif readWriteSection:
raise ValueError('No way to make read-write code for a nullable type')
forwards += '\n'
typesText += '\nclass ' + fullTypeName(restype); # type class declaration
if withData:
typesText += ' : private tl::details::type_owner'; # if has data fields
typesText += ' {\n'
typesText += 'public:\n'
typesText += '\t' + fullTypeName(restype) + '();\n'; # default constructor
if withData and not withType:
methods += '\n' + fullTypeName(restype) + '::' + fullTypeName(restype) + '() : type_owner(' + newFast + ') {\n}\n'
else:
methods += '\n' + fullTypeName(restype) + '::' + fullTypeName(restype) + '() = default;\n'
if nullable:
typesText += '\t' + fullTypeName(restype) + '(std::nullptr_t);\n'
methods += fullTypeName(restype) + '::' + fullTypeName(restype) + '(std::nullptr_t) {\n}\n'
typesText += '\n'
if nullable:
typesText += '\texplicit operator bool() const;\n'
methods += fullTypeName(restype) + '::operator bool() const {\n\t'
if withData:
methods += '\treturn hasData();\n'
else:
methods += '\treturn _type != 0;\n'
methods += '}\n'
typesText += getters
typesText += '\n'
typesText += '\ttemplate <typename Method, typename ...Methods>\n'
typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n'
visitorMethods += 'template <typename Method, typename ...Methods>\n'
visitorMethods += 'decltype(auto) ' + fullTypeName(restype) + '::match(Method &&method, Methods &&...methods) const {\n'
if withType:
visitorMethods += '\tswitch (_type) {\n'
visitorMethods += visitor
visitorMethods += '\t}\n'
visitorMethods += '\tUnexpected("Type in ' + fullTypeName(restype) + '::match.");\n'
else:
visitorMethods += '\treturn base::match_method(c_' + v[0][0] + '(), std::forward<Method>(method), std::forward<Methods>(methods)...);\n'
visitorMethods += '}\n\n'
typesText += '\t' + typeIdType + ' type() const;\n'; # type id method
methods += typeIdType + ' ' + fullTypeName(restype) + '::type() const {\n'
if withType:
if nullable:
methods += '\treturn _type;\n'
else:
methods += '\tExpects(_type != 0);\n\n'
methods += '\treturn _type;\n'
else:
if nullable:
methods += '\treturn hasData() ? ' + idPrefix + v[0][0] + ' : ' + typeIdType + '(0);\n'
else:
methods += '\treturn ' + idPrefix + v[0][0] + ';\n'
methods += '}\n'
if readWriteSection:
typesText += '\n'
typesText += '\t[[nodiscard]] bool read(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons'; # read method
if (not withType):
typesText += ' = ' + idPrefix + name
typesText += ');\n'
methods += 'bool ' + fullTypeName(restype) + '::read(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons) {\n'
if (withData):
if not (withType):
methods += '\tif (cons != ' + idPrefix + v[0][0] + ') return false;\n'
if (withType):
methods += '\tswitch (cons) {\n'
methods += reader
methods += '\tdefault: return false;\n'
methods += '\t}\n'
else:
methods += reader
methods += '\treturn true;\n'
methods += '}\n'
typesText += '\ttemplate <typename Accumulator>\n' # write method
typesText += '\tvoid write(Accumulator &to) const;\n'
methods += 'template <typename Accumulator>\n'
methods += 'void ' + fullTypeName(restype) + '::write(Accumulator &to) const {\n'
if (withType and writer != ''):
methods += '\tswitch (_type) {\n'
methods += writer
methods += '\t}\n'
else:
methods += writer
methods += '}\n'
methods += 'template void ' + fullTypeName(restype) + '::write<' + bufferType + '>(' + bufferType + ' &to) const;\n'
methods += 'template void ' + fullTypeName(restype) + '::write<::tl::details::LengthCounter>(::tl::details::LengthCounter &to) const;\n'
typesText += '\n\tusing ResponseType = void;\n'; # no response types declared
if optimizeSingleData:
if withData and not withType:
for data in v:
name = data[0]
typesText += '\tusing SingleDataType = ' + fullDataName(name) + ';\n'
else:
typesText += '\tusing SingleDataType = NotSingleDataTypePlaceholder;\n';
typesText += '\nprivate:\n'; # private constructors
if (withType): # by-type-id constructor
typesText += '\texplicit ' + fullTypeName(restype) + '(' + typeIdType + ' type);\n'
methods += fullTypeName(restype) + '::' + fullTypeName(restype) + '(' + typeIdType + ' type) : '
methods += '_type(type)'
methods += ' {\n'
methods += '\tswitch (type) {\n'; # type id check
methods += switchLines
methods += '\tdefault: Unexpected("Type in ' + fullTypeName(restype) + '::' + fullTypeName(restype) + '.");\n'
methods += '\t}\n'
methods += '}\n'; # by-type-id constructor end
if (withData):
typesText += constructsText
methods += constructsBodies
if (friendDecl):
typesText += '\n' + friendDecl
if (withType):
typesText += '\n\t' + typeIdType + ' _type = 0;\n'; # type field var
typesText += '};\n'; # type class ended
flagOperators += flagDeclarations
factories += creatorsDeclarations
methods += creatorsBodies
if readWriteSection:
typesText += 'using ' + fullTypeName(resType) + ' = tl::boxed<' + fullTypeName(restype) + '>;\n'; # boxed type definition
flagOperators += '\n'
for pureChildName in flagInheritance:
childName = dataPrefix + pureChildName
parentName = dataPrefix + flagInheritance[pureChildName]
for flag in parentFlagsCheck[childName]:
#
# 'channelForbidden' has 'until_date' flag and 'channel' doesn't have it.
# But as long as flags don't collide this is not a problem.
#
# if (not flag in parentFlagsCheck[parentName]):
# raise ValueError('Flag ' + flag + ' not found in ' + parentName + ' which should be a flags-parent of ' + childName)
#
if (flag in parentFlagsCheck[parentName]):
if (parentFlagsCheck[childName][flag] != parentFlagsCheck[parentName][flag]):
raise ValueError('Flag ' + flag + ' has different value in ' + parentName + ' which should be a flags-parent of ' + childName)
else:
parentFlagsCheck[parentName][flag] = parentFlagsCheck[childName][flag]
flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(' + childName + '::Flags flags) { return static_cast<' + parentName + '::Flag>(flags.value()); }\n'
flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(MTPflags<' + childName + '::Flags> flags) { return mtpCastFlags(flags.v); }\n'
textSerializeSource = ''
if writeSerialization:
# manual types added here
textSerializeMethods += '\
bool Serialize_rpc_result(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\
if (stage) {\n\
to.add(",\\n").addSpaces(lev);\n\
} else {\n\
to.add("{ rpc_result");\n\
to.add("\\n").addSpaces(lev);\n\
}\n\
switch (stage) {\n\
case 0: to.add(" req_msg_id: "); ++stages.back(); types.push_back(' + idPrefix + 'long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
case 1: to.add(" result: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\
}\n\
return true;\n\
}\n\
\n\
bool Serialize_msg_container(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\
if (stage) {\n\
to.add(",\\n").addSpaces(lev);\n\
} else {\n\
to.add("{ msg_container");\n\
to.add("\\n").addSpaces(lev);\n\
}\n\
switch (stage) {\n\
case 0: to.add(" messages: "); ++stages.back(); types.push_back(' + idPrefix + 'vector); vtypes.push_back(' + idPrefix + 'core_message); stages.push_back(0); flags.push_back(0); break;\n\
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\
}\n\
return true;\n\
}\n\
\n\
bool Serialize_core_message(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\
if (stage) {\n\
to.add(",\\n").addSpaces(lev);\n\
} else {\n\
to.add("{ core_message");\n\
to.add("\\n").addSpaces(lev);\n\
}\n\
switch (stage) {\n\
case 0: to.add(" msg_id: "); ++stages.back(); types.push_back(' + idPrefix + 'long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
case 1: to.add(" seq_no: "); ++stages.back(); types.push_back(' + idPrefix + 'int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
case 2: to.add(" bytes: "); ++stages.back(); types.push_back(' + idPrefix + 'int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
case 3: to.add(" body: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\
}\n\
return true;\n\
}\n\
\n'
textSerializeInit += '\
{ ' + idPrefix + 'rpc_result, Serialize_rpc_result },\n\
{ ' + idPrefix + 'msg_container, Serialize_msg_container },\n\
{ ' + idPrefix + 'core_message, Serialize_core_message },\n'
textSerializeSource = '\
namespace {\n\
\n\
using Types = QVector<' + typeIdType + '>;\n\
using Stages = QVector<int32>;\n\
using Flags = QVector<int64>;\n\
\n\
' + textSerializeMethods + '\n\
\n\
using TextSerializer = bool (*)(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag);\n\
\n\
base::flat_map<' + typeIdType + ', TextSerializer> CreateTextSerializers() {\n\
return {\n\
' + textSerializeInit + '\n\
};\n\
}\n\
\n\
} // namespace\n\
\n\
bool DumpToTextType(DumpToTextBuffer &to, const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + primeType + ' cons, uint32 level, ' + primeType + ' vcons) {\n\
static auto kSerializers = CreateTextSerializers();\n\
\n\
Types types, vtypes;\n\
Stages stages;\n\
Flags flags;\n\
types.reserve(20); vtypes.reserve(20); stages.reserve(20); flags.reserve(20);\n\
types.push_back(' + typeIdType + '(cons)); vtypes.push_back(' + typeIdType + '(vcons)); stages.push_back(0); flags.push_back(0);\n\
\n\
' + typeIdType + ' type = cons, vtype = vcons;\n\
int32 stage = 0;\n\
int64 flag = 0;\n\
\n\
while (!types.isEmpty()) {\n\
type = types.back();\n\
vtype = vtypes.back();\n\
stage = stages.back();\n\
flag = flags.back();\n\
if (!type) {\n\
if (from >= end) {\n\
to.error("insufficient data");\n\
return false;\n\
} else if (stage) {\n\
to.error("unknown type on stage > 0");\n\
return false;\n\
}\n\
types.back() = type = *from;\n\
++from;\n\
}\n\
\n\
int32 lev = level + types.size() - 1;\n\
auto it = kSerializers.find(type);\n\
if (it != kSerializers.end()) {\n\
if (!(*it->second)(to, stage, lev, types, vtypes, stages, flags, from, end, flag)) {\n\
to.error();\n\
return false;\n\
}\n\
} else if (DumpToTextCore(to, from, end, type, lev, vtype)) {\n\
types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n\
} else {\n\
to.error();\n\
\n\
const auto bad = QByteArray::number(type, 16);\n\
to.add("(ERROR_SCHEME_BAD_CONS:0x").add(bad.data()).add(")");\n\
\n\
return false;\n\
}\n\
}\n\
return true;\n\
}\n'
if forwTypedefs != '':
forwTypedefs = '// Boxed types definitions\n' + forwTypedefs + '\n'
# module itself
header = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#pragma once\n\
\n\
' + ('#include "' + builtinInclude + '"\n' if builtinInclude != '' else '') + '\
#include "base/assertion.h"\n\
#include "base/flags.h"\n\
#include "tl/tl_boxed.h"\n\
#include "tl/tl_type_owner.h"\n\
\n\
' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\
' + ('namespace ' + creatorNamespace + ' {\n' if creatorNamespace != '' else '') + '\
\n\
' + ('inline constexpr auto kCurrentLayer = ' + primeType + '(' + str(layer) + ');\n\n' if layer != 0 else '') +'\
class TypeCreator;\n\
\n\
' + ('} // namespace ' + creatorNamespace + '\n\n' if creatorNamespace != '' else '') + '\
// Type id constants\n\
enum {\n\
' + ',\n'.join(enums) + '\n\
};\n\
\n\
// Type forward declarations\n\
' + forwards + '\n\
' + forwTypedefs + '\
// Type classes definitions\n\
' + typesText + '\n\
// Type constructors with data\n\
' + dataTexts + '\n\
// RPC methods\n\
' + funcsText + '\n\
// Template methods definition\n\
' + inlineMethods + '\n\
// Visitor definition\n\
' + visitorMethods + '\n\
// Flag operators definition\n\
' + flagOperators + '\n\
// Factory methods declaration\n\
' + factories + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '')
source = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#include "' + outputHeaderBasename + '"\n\
\n\
// Creator proxy class definition\n\
' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\
' + ('namespace ' + creatorNamespace + ' {\n' if creatorNamespace != '' else '') + '\
\n\
class TypeCreator final {\n\
public:\n\
' + creatorProxyText + '\n\
};\n\
\n\
' + ('} // namespace ' + creatorNamespace + '\n\n' if creatorNamespace != '' else '') + '\
// Methods definition\n\
' + methods + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '')
conversionHeaderFrom = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#pragma once\n\
\n\
#include "' + conversionInclude + '"\n\
#include "' + outputHeaderBasename + '"\n\
\n\
' + ('namespace ' + globalNamespace + ' {\n\n' if globalNamespace != '' else '') + '\
' + conversionHeaderFrom + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') +'\
' + ('\n#include "' + conversionBuiltinIncludeFrom + '"\n' if conversionBuiltinIncludeFrom != '' else '')
conversionHeaderTo = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#pragma once\n\
\n\
#include "' + conversionInclude + '"\n\
#include "' + outputHeaderBasename + '"\n\
\n\
' + ('namespace ' + globalNamespace + ' {\n\n' if globalNamespace != '' else '') + '\
' + conversionHeaderTo + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') +'\
' + ('\n#include "' + conversionBuiltinIncludeTo + '"\n' if conversionBuiltinIncludeTo != '' else '')
conversionSourceFrom = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#include "' + outputConversionHeaderFromBasename + '"\n\
\n\
' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\
' + conversionSourceFrom + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '')
conversionSourceTo = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#include "' + outputConversionHeaderToBasename + '"\n\
\n\
' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\
' + conversionSourceTo + '\n\
' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '')
serializationHeader = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#pragma once\n\
\n\
' + ('#include "' + builtinInclude + '"\n\n' if builtinInclude != '' else '') + '\
namespace MTP::details {\n\
\n\
struct DumpToTextBuffer;\n\
\n\
[[nodiscard]] bool DumpToTextType(DumpToTextBuffer &to, const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + primeType + ' cons = 0, uint32 level = 0, ' + primeType + ' vcons = 0);\n\
\n\
} // namespace MTP::details\n'
serializationSource = '\
// WARNING! All changes made in this file will be lost!\n\
// Created from ' + inputNames + ' by \'generate.py\'\n\
//\n\
#include "' + outputSerializationHeaderBasename + '"\n\
#include "' + outputHeaderBasename + '"\n\
#include "' + serializationInclude + '"\n\
#include "base/flat_map.h"\n\
\n\
namespace MTP::details {\n\
' + textSerializeSource + '\n\
} // namespace MTP::details\n'
alreadyHeader = ''
if os.path.isfile(outputHeader):
with open(outputHeader, 'r') as already:
alreadyHeader = already.read()
if alreadyHeader != header:
with open(outputHeader, 'w') as out:
out.write(header)
alreadySource = ''
if os.path.isfile(outputSource):
with open(outputSource, 'r') as already:
alreadySource = already.read()
if alreadySource != source:
with open(outputSource, 'w') as out:
out.write(source)
if writeConversion:
alreadyHeader = ''
if os.path.isfile(outputConversionHeaderFrom):
with open(outputConversionHeaderFrom, 'r') as already:
alreadyHeader = already.read()
if alreadyHeader != conversionHeaderFrom:
with open(outputConversionHeaderFrom, 'w') as out:
out.write(conversionHeaderFrom)
if writeConversion:
alreadyHeader = ''
if os.path.isfile(outputConversionHeaderTo):
with open(outputConversionHeaderTo, 'r') as already:
alreadyHeader = already.read()
if alreadyHeader != conversionHeaderTo:
with open(outputConversionHeaderTo, 'w') as out:
out.write(conversionHeaderTo)
alreadySource = ''
if os.path.isfile(outputConversionSourceFrom):
with open(outputConversionSourceFrom, 'r') as already:
alreadySource = already.read()
if alreadySource != conversionSourceFrom:
with open(outputConversionSourceFrom, 'w') as out:
out.write(conversionSourceFrom)
alreadySource = ''
if os.path.isfile(outputConversionSourceTo):
with open(outputConversionSourceTo, 'r') as already:
alreadySource = already.read()
if alreadySource != conversionSourceTo:
with open(outputConversionSourceTo, 'w') as out:
out.write(conversionSourceTo)
if writeSerialization:
alreadyHeader = ''
if os.path.isfile(outputSerializationHeader):
with open(outputSerializationHeader, 'r') as already:
alreadyHeader = already.read()
if alreadyHeader != serializationHeader:
with open(outputSerializationHeader, 'w') as out:
out.write(serializationHeader)
alreadySource = ''
if os.path.isfile(outputSerializationSource):
with open(outputSerializationSource, 'r') as already:
alreadySource = already.read()
if alreadySource != serializationSource:
with open(outputSerializationSource, 'w') as out:
out.write(serializationSource)
with open(outputPath + '.timestamp', 'w') as out:
out.write('1')