From 1cc3594f096af0092684e2eabcf62447f64d7182 Mon Sep 17 00:00:00 2001 From: teidesu <86301490+teidesu@users.noreply.github.com> Date: Mon, 29 Aug 2022 14:33:11 +0300 Subject: [PATCH] docs(tl-utils): documented everything --- packages/tl-utils/README.md | 7 +- packages/tl-utils/src/codegen/errors.ts | 13 ++ packages/tl-utils/src/codegen/reader.ts | 13 +- packages/tl-utils/src/codegen/types.ts | 21 +- packages/tl-utils/src/codegen/utils.ts | 18 ++ packages/tl-utils/src/codegen/writer.ts | 17 +- packages/tl-utils/src/ctor-id.ts | 10 + packages/tl-utils/src/diff.ts | 12 ++ packages/tl-utils/src/merge.ts | 13 ++ packages/tl-utils/src/parse.ts | 31 +++ packages/tl-utils/src/patch.ts | 11 + packages/tl-utils/src/schema.ts | 24 +++ packages/tl-utils/src/stringify.ts | 8 + packages/tl-utils/src/types.ts | 254 ++++++++++++++++++++++++ packages/tl-utils/src/utils.ts | 20 ++ packages/tl-utils/typedoc.js | 3 +- 16 files changed, 467 insertions(+), 8 deletions(-) diff --git a/packages/tl-utils/README.md b/packages/tl-utils/README.md index df53f058..a948438f 100644 --- a/packages/tl-utils/README.md +++ b/packages/tl-utils/README.md @@ -1,3 +1,8 @@ # @mtcute/tl-utils -This package contains utilities for TL schema parsing and manipulation. +This package contains utilities for TL schema parsing and manipulation: + - Parsing & generating TL schema definitions + - Computing constructor IDs + - Parsing & generating TDLib style comments + - Code generation for readers and writers maps, errors, type definitions + - Merging and diffing entries, schemas diff --git a/packages/tl-utils/src/codegen/errors.ts b/packages/tl-utils/src/codegen/errors.ts index adb7260c..8c709746 100644 --- a/packages/tl-utils/src/codegen/errors.ts +++ b/packages/tl-utils/src/codegen/errors.ts @@ -1,6 +1,12 @@ import { camelToPascal, jsComment, snakeToCamel } from './utils' import { TlError, TlErrors } from '../types' +/** + * Transform TL error name to JS error name + * + * @param code TL error code + * @example 'MSG_ID_INVALID' -> 'MsgIdInvalidError' + */ export function errorCodeToClassName(code: string): string { let str = camelToPascal(snakeToCamel(code.toLowerCase().replace(/ /g, '_'))) + @@ -105,6 +111,13 @@ function placeholderType(name: string): string { return 'number' } +/** + * Generate code for given TL errors + * + * @param errors Errors to generate code for + * @param exports Prefix for exports object + * @returns Tuple containing `[ts, js]` code + */ export function generateCodeForErrors( errors: TlErrors, exports = 'exports.' diff --git a/packages/tl-utils/src/codegen/reader.ts b/packages/tl-utils/src/codegen/reader.ts index 5b0e617f..39212026 100644 --- a/packages/tl-utils/src/codegen/reader.ts +++ b/packages/tl-utils/src/codegen/reader.ts @@ -3,7 +3,11 @@ import { computeConstructorIdFromEntry } from '../ctor-id' import { snakeToCamel } from './utils' /** - * Returns code as an object entry + * Generate binary reader code for a given entry. + * + * @param entry Entry to generate reader for + * @param includeFlags Whether to include `flags` field in the result object + * @returns Code as a writers map entry */ export function generateReaderCodeForTlEntry( entry: TlEntry, @@ -118,6 +122,13 @@ export function generateReaderCodeForTlEntry( return `${pre}${beforeReturn.replace(/;var /g, ',')}return{${returnCode}}},` } +/** + * Generate binary reader code for a given schema. + * + * @param entries Entries to generate reader for + * @param varName Name of the variable containing the result + * @param methods Whether to include method readers + */ export function generateReaderCodeForTlEntries( entries: TlEntry[], varName: string, diff --git a/packages/tl-utils/src/codegen/types.ts b/packages/tl-utils/src/codegen/types.ts index cc4d3d56..64b8ad11 100644 --- a/packages/tl-utils/src/codegen/types.ts +++ b/packages/tl-utils/src/codegen/types.ts @@ -3,6 +3,9 @@ import { groupTlEntriesByNamespace, splitNameToNamespace } from '../utils' import { camelToPascal, indent, jsComment, snakeToCamel } from './utils' import { errorCodeToClassName, generateCodeForErrors } from './errors' +/** + * Mapping of TL primitive types to TS types + */ export const PRIMITIVE_TO_TS: Record = { int: 'number', long: 'Long', @@ -56,6 +59,14 @@ function entryFullTypeName(entry: TlEntry): string { return fullTypeName(entry.name, '', false, entry.kind === 'method') } +/** + * Generate TypeScript definitions for a given entry + * + * @param entry Entry to generate definitions for + * @param baseNamespace Base TL namespace containing the entries + * @param errors Errors information object + * @param withFlags Whether to include `flags` field in the type + */ export function generateTypescriptDefinitionsForTlEntry( entry: TlEntry, baseNamespace = 'tl.', @@ -189,7 +200,15 @@ ns.$extendTypes = function(types) { ns.LAYER = $LAYER$; ` -// returns pair of generated ts and js code +/** + * Generate TypeScript definitions for a given TL schema + * + * @param schema TL schema to generate definitions for + * @param layer Layer of the schema + * @param namespace namespace of the schema + * @param errors Errors information object + * @returns Tuple containing `[ts, js]` code + */ export function generateTypescriptDefinitionsForTlSchema( schema: TlFullSchema, layer: number, diff --git a/packages/tl-utils/src/codegen/utils.ts b/packages/tl-utils/src/codegen/utils.ts index 9e0b7d41..5d94fbd1 100644 --- a/packages/tl-utils/src/codegen/utils.ts +++ b/packages/tl-utils/src/codegen/utils.ts @@ -1,12 +1,24 @@ +/** + * Transform snake_case string to camelCase string + * @param s Snake_case string + */ export const snakeToCamel = (s: string): string => { return s.replace(/(? { return $1.substring(1).toUpperCase() }) } +/** + * Transform camelCase string to PascalCase string + * @param s camelCase string + */ export const camelToPascal = (s: string): string => s[0].toUpperCase() + s.substring(1) +/** + * Format a string as a JS documentation comment + * @param s Comment to format + */ export function jsComment(s: string): string { return ( '/**' + @@ -23,6 +35,12 @@ export function jsComment(s: string): string { ) } +/** + * Indent the string with the given amount of spaces + * + * @param size Number of spaces to indent with + * @param s String to indent + */ export function indent(size: number, s: string): string { let prefix = '' while (size--) prefix += ' ' diff --git a/packages/tl-utils/src/codegen/writer.ts b/packages/tl-utils/src/codegen/writer.ts index 3c223eca..5f4242f7 100644 --- a/packages/tl-utils/src/codegen/writer.ts +++ b/packages/tl-utils/src/codegen/writer.ts @@ -2,7 +2,7 @@ import { TL_PRIMITIVES, TlEntry } from '../types' import { computeConstructorIdFromEntry } from '../ctor-id' import { snakeToCamel } from './utils' -export const TL_WRITER_PRELUDE = +const TL_WRITER_PRELUDE = 'function h(o, p){' + 'var q=o[p];' + 'if(q===void 0)' + @@ -10,9 +10,12 @@ export const TL_WRITER_PRELUDE = 'return q}\n' /** - * Returns code as an object entry. - * + * Generate writer code for a single entry. * `h` (has) function should be available + * + * @param entry Entry to generate writer for + * @param withFlags Whether to include `flags` field in the result object + * @returns Code as a readers map entry */ export function generateWriterCodeForTlEntry( entry: TlEntry, @@ -99,6 +102,14 @@ export function generateWriterCodeForTlEntry( return ret + '},' } +/** + * Generate writer code for a given TL schema. + * + * @param entries Entries to generate writers for + * @param varName Name of the variable to use for the writers map + * @param prelude Whether to include the prelude (containing `h` function) + * @param withFlags Whether to include `flags` field in the result object + */ export function generateWriterCodeForTlEntries( entries: TlEntry[], varName: string, diff --git a/packages/tl-utils/src/ctor-id.ts b/packages/tl-utils/src/ctor-id.ts index 20ece255..4cdce6ff 100644 --- a/packages/tl-utils/src/ctor-id.ts +++ b/packages/tl-utils/src/ctor-id.ts @@ -3,6 +3,11 @@ import CRC32 from 'crc-32' import { TlEntry } from './types' import { writeTlEntryToString } from './stringify' +/** + * Computes the constructor id for a given TL entry string. + * + * @param line Line containing TL entry definition + */ export function computeConstructorIdFromString(line: string): number { return ( CRC32.str( @@ -20,6 +25,11 @@ export function computeConstructorIdFromString(line: string): number { ) } +/** + * Computes the constructor id for a given TL entry. + * + * @param entry TL entry + */ export function computeConstructorIdFromEntry(entry: TlEntry): number { return CRC32.str(writeTlEntryToString(entry, true)) >>> 0 } diff --git a/packages/tl-utils/src/diff.ts b/packages/tl-utils/src/diff.ts index 8d6390a6..c5cd60e1 100644 --- a/packages/tl-utils/src/diff.ts +++ b/packages/tl-utils/src/diff.ts @@ -8,6 +8,12 @@ import { } from './types' import { computeConstructorIdFromEntry } from './ctor-id' +/** + * Compute difference between two TL entries. + * + * @param a Entry A (field `old` in diff) + * @param b Entry B (field `new` in diff) + */ export function generateTlEntriesDifference( a: TlEntry, b: TlEntry @@ -125,6 +131,12 @@ export function generateTlEntriesDifference( return diff } +/** + * Compute difference between two TL schemas. + * + * @param a Entry A (field `old` in diff) + * @param b Entry B (field `new` in diff) + */ export function generateTlSchemasDifference( a: TlFullSchema, b: TlFullSchema diff --git a/packages/tl-utils/src/merge.ts b/packages/tl-utils/src/merge.ts index e24f83bb..2170b72b 100644 --- a/packages/tl-utils/src/merge.ts +++ b/packages/tl-utils/src/merge.ts @@ -1,6 +1,13 @@ import { TlEntry, TlFullSchema } from './types' import { computeConstructorIdFromEntry } from './ctor-id' +/** + * Merge multiple TL entries into a single entry. + * + * Note: this will only succeed if all entries have the same ID. + * + * @param entries Entries to merge + */ export function mergeTlEntries(entries: TlEntry[]): TlEntry | string { const first = entries[0] @@ -94,6 +101,12 @@ export function mergeTlEntries(entries: TlEntry[]): TlEntry | string { return result } +/** + * Merge multiple TL schemas into a single schema. + * + * @param schemas Schemas to merge + * @param onConflict Callback to handle conflicts + */ export async function mergeTlSchemas( schemas: TlFullSchema[], onConflict: ( diff --git a/packages/tl-utils/src/parse.ts b/packages/tl-utils/src/parse.ts index 1c9b6043..8bd7682c 100644 --- a/packages/tl-utils/src/parse.ts +++ b/packages/tl-utils/src/parse.ts @@ -14,13 +14,44 @@ function applyPrefix(prefix: string, type: string): string { return prefix + type } +/** + * Parse TL schema into a list of entries. + * + * @param tl TL schema + * @param params Additional parameters + */ export function parseTlToEntries( tl: string, params?: { + /** + * Whether to throw an error if a line failed to parse + */ panicOnError?: boolean + + /** + * Function to be called if there was an error while parsing a line + * + * @param err Error + * @param line Line that failed to parse + * @param num Line number + */ onError?: (err: Error, line: string, num: number) => void + + /** + * Function to be called a comment is found not belonging to any entry + * + * @param comment Comment text + */ onOrphanComment?: (comment: string) => void + + /** + * Prefix to be applied to all types + */ prefix?: string + + /** + * Whether to apply the prefix to arguments as well + */ applyPrefixToArguments?: boolean } ): TlEntry[] { diff --git a/packages/tl-utils/src/patch.ts b/packages/tl-utils/src/patch.ts index 03fbace7..e4a1111f 100644 --- a/packages/tl-utils/src/patch.ts +++ b/packages/tl-utils/src/patch.ts @@ -8,6 +8,17 @@ function evalForResult(js: string): any { return new Function(js)() } +/** + * Patch runtime TL schema (readers and writers map) with the given schema. + * + * Entries in the schema will override the ones in the existing one. + * Original readers and writers will be preserved, new ones will be returned. + * + * @param schema Schema containing new entries + * @param readers Original readers map + * @param writers Original writers map + * @returns New readers and writers map + */ export function patchRuntimeTlSchema( schema: string, readers: TlReaderMap, diff --git a/packages/tl-utils/src/schema.ts b/packages/tl-utils/src/schema.ts index f42f68c9..1badb46f 100644 --- a/packages/tl-utils/src/schema.ts +++ b/packages/tl-utils/src/schema.ts @@ -5,6 +5,12 @@ import { writeTlEntryToString } from './stringify' const replaceNewlineInComment = (s: string): string => s.replace(/\n/g, '\n//- ') +/** + * Parse TL entries into a full schema object + * by creating indexes on the entries. + * + * @param entries Entries to parse + */ export function parseFullTlSchema(entries: TlEntry[]): TlFullSchema { const ret: TlFullSchema = { entries, @@ -34,11 +40,29 @@ export function parseFullTlSchema(entries: TlEntry[]): TlFullSchema { return ret } +/** + * Write TL entries to schema text + * + * @param entries Entries to write + * @param params Additional parameters + */ export function writeTlEntriesToString( entries: TlEntry[], params?: { + /** + * Whether to force compute IDs if one is not present + */ computeIds?: boolean + + /** + * Whether to use TDLib style comments for arguments + */ tdlibComments?: boolean + + /** + * Whether to omit prelude containing primitive types + * (like `int`, `string`, etc.) + */ omitPrimitives?: boolean } ): string { diff --git a/packages/tl-utils/src/stringify.ts b/packages/tl-utils/src/stringify.ts index 89623ab1..a9e7618d 100644 --- a/packages/tl-utils/src/stringify.ts +++ b/packages/tl-utils/src/stringify.ts @@ -8,6 +8,14 @@ function normalizeType(s: string): string { .replace('int53', 'long') } +/** + * Generate TL definition for a given entry. + * + * @param entry Entry to generate definition for + * @param forIdComputation + * Whether to generate definition for constructor ID computation + * (it has slightly different syntax, will not contain `true` flags, etc.) + */ export function writeTlEntryToString( entry: TlEntry, forIdComputation = false diff --git a/packages/tl-utils/src/types.ts b/packages/tl-utils/src/types.ts index c378f89f..af423ba2 100644 --- a/packages/tl-utils/src/types.ts +++ b/packages/tl-utils/src/types.ts @@ -1,103 +1,357 @@ +/** + * An argument of a TL entry + */ export interface TlArgument { + /** + * Name of the argument + */ name: string + + /** + * Type of the argument + */ type: string + + /** + * Predicate of the argument + * @example `flags.3` + */ predicate?: string + + /** + * Comment of the argument + */ comment?: string } +/** + * A generic argument of a TL entry + */ export interface TlGeneric { + /** + * Name of the generic + */ name: string + + /** + * Super type of the generic + */ type: string } +/** + * A TL entry + */ export interface TlEntry { + /** + * Kind of the entry + */ kind: 'method' | 'class' + + /** + * Name of the entry + */ name: string + + /** + * ID of the entry (may be 0 if not known) + */ id: number + + /** + * Type of the entry + */ type: string + + /** + * Comment of the entry + */ comment?: string + + /** + * Generic arguments of the entry + */ generics?: TlGeneric[] + + /** + * Arguments of the entry + */ arguments: TlArgument[] // additional fields for methods, // used primarily for documentation + /** + * Errors that this method can throw (only if kind is `method`) + */ throws?: { + /** + * Error code + * @example 429 + */ code: number + + /** + * Error name + * @example `FLOOD_WAIT_%d` + */ name: string + + /** + * Description of the error + */ comment?: string }[] + + /** + * Availability of the method (only if kind is `method`) + */ available?: 'both' | 'bot' | 'user' } +/** + * A TL union (classes that share the same `type`) + */ export interface TlUnion { + /** + * Name of the union (`== classes[*].type`) + */ name: string + + /** + * Description of the union + */ comment?: string + + /** + * Classes in the union + */ classes: TlEntry[] } +/** + * A full TL schema information + * + * Basically an index over {@link TlEntry[]} + */ export interface TlFullSchema { + /** + * Entries in the schema + */ entries: TlEntry[] + + /** + * Index of classes by name + */ classes: Record + + /** + * Index of methods by name + */ methods: Record + + /** + * Index of unions by name + */ unions: Record } +/** + * A TL error + */ export interface TlError { + /** + * Error code + */ code: number + + /** + * Error name + */ name: string + + /** + * Description of the error + */ description?: string + + /** + * Whether this is a "virtual" error (only thrown by mtcute itself) + */ virtual?: true // internal fields used by generator + + /** @hidden */ _auto?: true + /** @hidden */ _paramNames?: string[] } +/** + * TL errors information + */ export interface TlErrors { + /** + * Base errors + */ base: TlError[] + + /** + * Index of errors by name + */ errors: Record + + /** + * Object describing which errors may be thrown by which methods + * + * Mapping from method name to array of error names + */ throws: Record + + /** + * Index of the methods only usable by user + */ userOnly: Record } +/** + * Basic difference type + * + * @typeParam T Type that is being diff-ed + * @typeParam K Information about the modifications + */ export interface BasicDiff { + /** + * Added elements + */ added: T[] + + /** + * Removed elements + */ removed: T[] + + /** + * Modified elements + */ modified: K[] } +/** + * A simple old/new difference for a single property + * + * @typeParam T Type of the property + */ export interface PropertyDiff { + /** + * Old value of the property + */ old: T + + /** + * New value of the property + */ new: T } +/** + * Difference of a single argument + */ export interface TlArgumentDiff { + /** + * Name of the argument + */ name: string + + /** + * Type of the argument diff + */ type?: PropertyDiff + + /** + * Predicate of the argument diff + */ predicate?: PropertyDiff + + /** + * Comment of the argument diff + */ comment?: PropertyDiff } +/** + * Difference of a single entry + */ export interface TlEntryDiff { + /** + * Name of the entry + */ name: string + + /** + * Constructor ID of the entry diff + */ id?: PropertyDiff + + /** + * Generic types diff + */ generics?: PropertyDiff + + /** + * Arguments of the entry diff + */ arguments?: BasicDiff + + /** + * Comment of the entry diff + */ comment?: PropertyDiff } +/** + * Difference of a single union + */ export interface TlUnionDiff { + /** + * Name of the union + */ name: string + + /** + * Difference in union classes + */ classes: BasicDiff + + /** + * Difference in union methods + */ methods: BasicDiff } +/** + * Difference between two TL schemas + */ export interface TlSchemaDiff { + /** + * Difference in classes + */ classes: BasicDiff + + /** + * Difference in methods + */ methods: BasicDiff + + /** + * Difference in unions + */ unions: BasicDiff } +/** + * Index of TL primitive types + */ export const TL_PRIMITIVES = { int: 1, long: 1, diff --git a/packages/tl-utils/src/utils.ts b/packages/tl-utils/src/utils.ts index 0491e508..e691ffeb 100644 --- a/packages/tl-utils/src/utils.ts +++ b/packages/tl-utils/src/utils.ts @@ -1,11 +1,25 @@ import { TlEntry } from './types' +/** + * Split qualified TL entry name into namespace and name + * + * @param name Qualified TL entry name + * @returns Namespace (if any) and name + * @example `splitNameToNamespace('messages.sendMessage') => ['messages', 'sendMessage']` + * @example `splitNameToNamespace('updatesTooLong') => [null, 'updatesTooLong']` + */ export function splitNameToNamespace(name: string): [string | null, string] { const s = name.split('.') if (s.length === 2) return s as [string, string] return [null, name] } +/** + * Parse TDLib style comment describing arguments of a TL entry + * + * @param str TDLib style comment + * @returns Mapping of argument names to argument descriptions + */ export function parseTdlibStyleComment(str: string): Record { const obj: Record = {} @@ -23,6 +37,12 @@ export function parseTdlibStyleComment(str: string): Record { return obj } +/** + * Group TL entries by their namespace + * + * @param entries Entries to group + * @returns Mapping of namespace to entries. Base namespace is `''` (empty string). + */ export function groupTlEntriesByNamespace( entries: TlEntry[] ): Record { diff --git a/packages/tl-utils/typedoc.js b/packages/tl-utils/typedoc.js index 84f04de8..56296ca7 100644 --- a/packages/tl-utils/typedoc.js +++ b/packages/tl-utils/typedoc.js @@ -7,6 +7,5 @@ module.exports = { '../../docs/packages/' + require('./package.json').name.replace(/^@.+\//, '') ), - entryPoints: ['./src'], - entryPointStrategy: 'expand', + entryPoints: ['./src/index.ts'], }