refactor(dispatcher): codegen for update types

i'm just too lazy to be bothered with pasting that. also, i forgot to implement builder for editMessage, teehee~
This commit is contained in:
teidesu 2021-05-05 00:43:35 +03:00
parent fd92121b14
commit 1c1aed147a
5 changed files with 394 additions and 42 deletions

View file

@ -0,0 +1,272 @@
const fs = require('fs')
const path = require('path')
const prettier = require('prettier')
const {
snakeToCamel,
camelToPascal,
camelToSnake,
} = require('../../tl/scripts/common')
function parseUpdateTypes() {
const lines = fs
.readFileSync(path.join(__dirname, 'update-types.txt'), 'utf-8')
.split('\n')
.map((it) => it.trim())
.filter((it) => it && it[0] !== '#')
const ret = []
for (const line of lines) {
const m = line.match(/^([a-z_]+)(?:: ([a-zA-Z]+))? = ([a-zA-Z]+)$/)
if (!m) throw new Error(`invalid syntax: ${line}`)
ret.push({
typeName: m[1],
handlerTypeName: m[2] || camelToPascal(snakeToCamel(m[1])),
updateType: m[3],
funcName: m[2]
? m[2][0].toLowerCase() + m[2].substr(1)
: snakeToCamel(m[1]),
})
}
return ret
}
function replaceSections(filename, sections) {
let lines = fs
.readFileSync(path.join(__dirname, '../src', filename), 'utf-8')
.split('\n')
const findMarker = (marker) => {
const idx = lines.findIndex((line) => line.trim() === `// ${marker}`)
if (idx === -1) throw new Error(marker + ' not found')
return idx
}
for (const [name, content] of Object.entries(sections)) {
const start = findMarker(`begin-${name}`)
const end = findMarker(`end-${name}`)
if (start > end) throw new Error('begin is after end')
lines.splice(start + 1, end - start - 1, content)
}
fs.writeFileSync(path.join(__dirname, '../src', filename), lines.join('\n'))
}
const types = parseUpdateTypes()
console.log(types)
async function formatFile(filename) {
const targetFile = path.join(__dirname, '../src/', filename)
const prettierConfig = await prettier.resolveConfig(targetFile)
let fullSource = await fs.promises.readFile(targetFile, 'utf-8')
fullSource = await prettier.format(fullSource, {
...(prettierConfig || {}),
filepath: targetFile,
})
await fs.promises.writeFile(targetFile, fullSource)
}
function toSentence(type, stype = 'inline') {
const name = camelToSnake(type.handlerTypeName)
.toLowerCase()
.replace(/_/g, ' ')
if (stype === 'inline') {
return `${name[0].match(/[aeiouy]/i) ? 'an' : 'a'} ${name} handler`
} else {
return `${name[0].toUpperCase()}${name.substr(1)} handler`
}
}
function generateBuilders() {
const lines = []
const imports = ['UpdateHandler']
types.forEach((type) => {
imports.push(`${type.handlerTypeName}Handler`)
if (type.updateType === 'IGNORE') {
lines.push(`
/**
* Create ${toSentence(type)}
*
* @param handler ${toSentence(type, 'full')}
*/
export function ${type.funcName}(
handler: ${type.handlerTypeName}Handler['callback']
): ${type.handlerTypeName}Handler
/**
* Create ${toSentence(type)} with a filter
*
* @param filter Predicate to check the update against
* @param handler ${toSentence(type, 'full')}
*/
export function ${type.funcName}(
filter: ${type.handlerTypeName}Handler['check'],
handler: ${type.handlerTypeName}Handler['callback']
): ${type.handlerTypeName}Handler
/** @internal */
export function ${type.funcName}(filter: any, handler?: any): ${
type.handlerTypeName
}Handler {
return _create('${type.typeName}', filter, handler)
}
`)
} else {
lines.push(`
/**
* Create ${toSentence(type)}
*
* @param handler ${toSentence(type, 'full')}
*/
export function ${type.funcName}(
handler: ${type.handlerTypeName}Handler['callback']
): ${type.handlerTypeName}Handler
/**
* Create ${toSentence(type)} with a filter
*
* @param filter Update filter
* @param handler ${toSentence(type, 'full')}
*/
export function ${type.funcName}<Mod>(
filter: UpdateFilter<${type.updateType}, Mod>,
handler: ${type.handlerTypeName}Handler<
filters.Modify<${type.updateType}, Mod>
>['callback']
): ${type.handlerTypeName}Handler
export function ${type.funcName}(
filter: any,
handler?: any
): ${type.handlerTypeName}Handler {
return _create('${type.typeName}', filter, handler)
}
`)
}
})
replaceSections('builders.ts', {
codegen: lines.join('\n'),
'codegen-imports':
'import {\n' +
imports.map((i) => ` ${i},\n`).join('') +
"} from './handler'",
})
}
function generateHandler() {
const lines = []
const names = ['RawUpdateHandler']
// imports must be added manually because yeah
types.forEach((type) => {
if (type.updateType === 'IGNORE') return
lines.push(
`export type ${type.handlerTypeName}Handler<T = ${type.updateType}> = ParsedUpdateHandler<'${type.typeName}', T>`
)
names.push(`${type.handlerTypeName}Handler`)
})
replaceSections('handler.ts', {
codegen:
lines.join('\n') +
'\n\nexport type UpdateHandler = \n' +
names.map((i) => ` | ${i}\n`).join(''),
})
}
function generateDispatcher() {
const lines = []
const imports = ['UpdateHandler']
types.forEach((type) => {
imports.push(`${type.handlerTypeName}Handler`)
if (type.updateType === 'IGNORE') {
lines.push(`
/**
* Register ${toSentence(type)} without any filters
*
* @param handler ${toSentence(type, 'full')}
* @param group Handler group index
*/
on${type.handlerTypeName}(handler: ${type.handlerTypeName}Handler['callback'], group?: number): void
/**
* Register ${toSentence(type)} with a filter
*
* @param filter Update filter function
* @param handler ${toSentence(type, 'full')}
* @param group Handler group index
*/
on${type.handlerTypeName}(
filter: ${type.handlerTypeName}Handler['check'],
handler: ${type.handlerTypeName}Handler['callback'],
group?: number
): void
/** @internal */
on${type.handlerTypeName}(filter: any, handler?: any, group?: number): void {
this._addKnownHandler('${type.funcName}', filter, handler, group)
}
`)
} else {
lines.push(`
/**
* Register ${toSentence(type)} without any filters
*
* @param handler ${toSentence(type, 'full')}
* @param group Handler group index
* @internal
*/
on${type.handlerTypeName}(handler: ${type.handlerTypeName}Handler['callback'], group?: number): void
/**
* Register ${toSentence(type)} with a filter
*
* @param filter Update filter
* @param handler ${toSentence(type, 'full')}
* @param group Handler group index
*/
on${type.handlerTypeName}<Mod>(
filter: UpdateFilter<${type.updateType}, Mod>,
handler: ${type.handlerTypeName}Handler<filters.Modify<${type.updateType}, Mod>>['callback'],
group?: number
): void
/** @internal */
on${type.handlerTypeName}(filter: any, handler?: any, group?: number): void {
this._addKnownHandler('${type.funcName}', filter, handler, group)
}
`)
}
})
replaceSections('dispatcher.ts', {
codegen: lines.join('\n'),
'codegen-imports':
'import {\n' +
imports.map((i) => ` ${i},\n`).join('') +
"} from './handler'",
})
}
async function main() {
generateBuilders()
generateHandler()
generateDispatcher()
await formatFile('builders.ts')
await formatFile('handler.ts')
await formatFile('dispatcher.ts')
}
main().catch(console.error)

View file

@ -0,0 +1,8 @@
# format: type_name[: handler_type_name] = update_type
# IGNORE as update_type disables filters modification
raw: RawUpdate = IGNORE
new_message = Message
edit_message = Message
chat_member: ChatMemberUpdate = ChatMemberUpdate
inline_query = InlineQuery
chosen_inline_result = ChosenInlineResult

View file

@ -1,11 +1,14 @@
// begin-codegen-imports
import { import {
ChatMemberUpdateHandler,
ChosenInlineResultHandler,
InlineQueryHandler,
NewMessageHandler,
RawUpdateHandler,
UpdateHandler, UpdateHandler,
RawUpdateHandler,
NewMessageHandler,
EditMessageHandler,
ChatMemberUpdateHandler,
InlineQueryHandler,
ChosenInlineResultHandler,
} from './handler' } from './handler'
// end-codegen-imports
import { filters, UpdateFilter } from './filters' import { filters, UpdateFilter } from './filters'
import { InlineQuery, Message } from '@mtcute/client' import { InlineQuery, Message } from '@mtcute/client'
import { ChatMemberUpdate } from './updates' import { ChatMemberUpdate } from './updates'
@ -31,44 +34,47 @@ function _create<T extends UpdateHandler>(
} }
export namespace handlers { export namespace handlers {
// begin-codegen
/** /**
* Create a {@link RawUpdateHandler} * Create a raw update handler
* *
* @param handler Update handler * @param handler Raw update handler
*/ */
export function rawUpdate( export function rawUpdate(
handler: RawUpdateHandler['callback'] handler: RawUpdateHandler['callback']
): RawUpdateHandler ): RawUpdateHandler
/** /**
* Create a {@link RawUpdateHandler} with a predicate * Create a raw update handler with a filter
* *
* @param filter Predicate to check the update against * @param filter Predicate to check the update against
* @param handler Update handler * @param handler Raw update handler
*/ */
export function rawUpdate( export function rawUpdate(
filter: RawUpdateHandler['check'], filter: RawUpdateHandler['check'],
handler: RawUpdateHandler['callback'] handler: RawUpdateHandler['callback']
): RawUpdateHandler ): RawUpdateHandler
/** @internal */
export function rawUpdate(filter: any, handler?: any): RawUpdateHandler { export function rawUpdate(filter: any, handler?: any): RawUpdateHandler {
return _create('raw', filter, handler) return _create('raw', filter, handler)
} }
/** /**
* Create a {@link NewMessageHandler} * Create a new message handler
* *
* @param handler Message handler * @param handler New message handler
*/ */
export function newMessage( export function newMessage(
handler: NewMessageHandler['callback'] handler: NewMessageHandler['callback']
): NewMessageHandler ): NewMessageHandler
/** /**
* Create a {@link NewMessageHandler} with a filter * Create a new message handler with a filter
* *
* @param filter Message update filter * @param filter Update filter
* @param handler Message handler * @param handler New message handler
*/ */
export function newMessage<Mod>( export function newMessage<Mod>(
filter: UpdateFilter<Message, Mod>, filter: UpdateFilter<Message, Mod>,
@ -80,7 +86,34 @@ export namespace handlers {
} }
/** /**
* Create a {@link ChatMemberUpdateHandler} * Create an edit message handler
*
* @param handler Edit message handler
*/
export function editMessage(
handler: EditMessageHandler['callback']
): EditMessageHandler
/**
* Create an edit message handler with a filter
*
* @param filter Update filter
* @param handler Edit message handler
*/
export function editMessage<Mod>(
filter: UpdateFilter<Message, Mod>,
handler: EditMessageHandler<filters.Modify<Message, Mod>>['callback']
): EditMessageHandler
export function editMessage(
filter: any,
handler?: any
): EditMessageHandler {
return _create('edit_message', filter, handler)
}
/**
* Create a chat member update handler
* *
* @param handler Chat member update handler * @param handler Chat member update handler
*/ */
@ -89,9 +122,9 @@ export namespace handlers {
): ChatMemberUpdateHandler ): ChatMemberUpdateHandler
/** /**
* Create a {@link ChatMemberUpdateHandler} with a filter * Create a chat member update handler with a filter
* *
* @param filter Chat member update filter * @param filter Update filter
* @param handler Chat member update handler * @param handler Chat member update handler
*/ */
export function chatMemberUpdate<Mod>( export function chatMemberUpdate<Mod>(
@ -118,9 +151,9 @@ export namespace handlers {
): InlineQueryHandler ): InlineQueryHandler
/** /**
* Create an inline query with a filter * Create an inline query handler with a filter
* *
* @param filter Inline query update filter * @param filter Update filter
* @param handler Inline query handler * @param handler Inline query handler
*/ */
export function inlineQuery<Mod>( export function inlineQuery<Mod>(
@ -149,7 +182,7 @@ export namespace handlers {
/** /**
* Create a chosen inline result handler with a filter * Create a chosen inline result handler with a filter
* *
* @param filter Chosen inline result filter * @param filter Update filter
* @param handler Chosen inline result handler * @param handler Chosen inline result handler
*/ */
export function chosenInlineResult<Mod>( export function chosenInlineResult<Mod>(
@ -165,4 +198,6 @@ export namespace handlers {
): ChosenInlineResultHandler { ): ChosenInlineResultHandler {
return _create('chosen_inline_result', filter, handler) return _create('chosen_inline_result', filter, handler)
} }
// end-codegen
} }

View file

@ -11,14 +11,17 @@ import {
StopChildrenPropagation, StopChildrenPropagation,
StopPropagation, StopPropagation,
} from './propagation' } from './propagation'
// begin-codegen-imports
import { import {
ChatMemberUpdateHandler,
ChosenInlineResultHandler,
InlineQueryHandler,
NewMessageHandler,
RawUpdateHandler,
UpdateHandler, UpdateHandler,
RawUpdateHandler,
NewMessageHandler,
EditMessageHandler,
ChatMemberUpdateHandler,
InlineQueryHandler,
ChosenInlineResultHandler,
} from './handler' } from './handler'
// end-codegen-imports
import { filters, UpdateFilter } from './filters' import { filters, UpdateFilter } from './filters'
import { handlers } from './builders' import { handlers } from './builders'
import { ChatMemberUpdate } from './updates' import { ChatMemberUpdate } from './updates'
@ -381,19 +384,21 @@ export class Dispatcher {
} }
} }
// begin-codegen
/** /**
* Register a raw update handler * Register a raw update handler without any filters
* *
* @param handler Handler function * @param handler Raw update handler
* @param group Handler group index * @param group Handler group index
*/ */
onRawUpdate(handler: RawUpdateHandler['callback'], group?: number): void onRawUpdate(handler: RawUpdateHandler['callback'], group?: number): void
/** /**
* Register a filtered raw update handler * Register a raw update handler with a filter
* *
* @param filter Update filter function * @param filter Update filter function
* @param handler Handler function * @param handler Raw update handler
* @param group Handler group index * @param group Handler group index
*/ */
onRawUpdate( onRawUpdate(
@ -408,19 +413,19 @@ export class Dispatcher {
} }
/** /**
* Register a message handler without any filters. * Register a new message handler without any filters.
* *
* @param handler Message handler * @param handler New message handler
* @param group Handler group index * @param group Handler group index
* @internal * @internal
*/ */
onNewMessage(handler: NewMessageHandler['callback'], group?: number): void onNewMessage(handler: NewMessageHandler['callback'], group?: number): void
/** /**
* Register a message handler with a given filter * Register a new message handler with a filter
* *
* @param filter Update filter * @param filter Update filter
* @param handler Message handler * @param handler New message handler
* @param group Handler group index * @param group Handler group index
*/ */
onNewMessage<Mod>( onNewMessage<Mod>(
@ -434,10 +439,37 @@ export class Dispatcher {
this._addKnownHandler('newMessage', filter, handler, group) this._addKnownHandler('newMessage', filter, handler, group)
} }
/**
* Register an edit message handler without any filters.
*
* @param handler Edit message handler
* @param group Handler group index
* @internal
*/
onEditMessage(handler: EditMessageHandler['callback'], group?: number): void
/**
* Register an edit message handler with a filter
*
* @param filter Update filter
* @param handler Edit message handler
* @param group Handler group index
*/
onEditMessage<Mod>(
filter: UpdateFilter<Message, Mod>,
handler: EditMessageHandler<filters.Modify<Message, Mod>>['callback'],
group?: number
): void
/** @internal */
onEditMessage(filter: any, handler?: any, group?: number): void {
this._addKnownHandler('editMessage', filter, handler, group)
}
/** /**
* Register a chat member update handler without any filters. * Register a chat member update handler without any filters.
* *
* @param handler Update handler * @param handler Chat member update handler
* @param group Handler group index * @param group Handler group index
* @internal * @internal
*/ */
@ -447,10 +479,10 @@ export class Dispatcher {
): void ): void
/** /**
* Register a message handler with a given filter * Register a chat member update handler with a filter
* *
* @param filter Update filter * @param filter Update filter
* @param handler Update handler * @param handler Chat member update handler
* @param group Handler group index * @param group Handler group index
*/ */
onChatMemberUpdate<Mod>( onChatMemberUpdate<Mod>(
@ -469,17 +501,17 @@ export class Dispatcher {
/** /**
* Register an inline query handler without any filters. * Register an inline query handler without any filters.
* *
* @param handler Update handler * @param handler Inline query handler
* @param group Handler group index * @param group Handler group index
* @internal * @internal
*/ */
onInlineQuery(handler: InlineQueryHandler['callback'], group?: number): void onInlineQuery(handler: InlineQueryHandler['callback'], group?: number): void
/** /**
* Register an inline query handler with a given filter * Register an inline query handler with a filter
* *
* @param filter Update filter * @param filter Update filter
* @param handler Update handler * @param handler Inline query handler
* @param group Handler group index * @param group Handler group index
*/ */
onInlineQuery<Mod>( onInlineQuery<Mod>(
@ -498,7 +530,7 @@ export class Dispatcher {
/** /**
* Register a chosen inline result handler without any filters. * Register a chosen inline result handler without any filters.
* *
* @param handler Update handler * @param handler Chosen inline result handler
* @param group Handler group index * @param group Handler group index
* @internal * @internal
*/ */
@ -508,10 +540,10 @@ export class Dispatcher {
): void ): void
/** /**
* Register an inline query handler with a given filter * Register a chosen inline result handler with a filter
* *
* @param filter Update filter * @param filter Update filter
* @param handler Update handler * @param handler Chosen inline result handler
* @param group Handler group index * @param group Handler group index
*/ */
onChosenInlineResult<Mod>( onChosenInlineResult<Mod>(
@ -526,4 +558,6 @@ export class Dispatcher {
onChosenInlineResult(filter: any, handler?: any, group?: number): void { onChosenInlineResult(filter: any, handler?: any, group?: number): void {
this._addKnownHandler('chosenInlineResult', filter, handler, group) this._addKnownHandler('chosenInlineResult', filter, handler, group)
} }
// end-codegen
} }

View file

@ -41,6 +41,7 @@ export type RawUpdateHandler = BaseUpdateHandler<
) => MaybeAsync<boolean> ) => MaybeAsync<boolean>
> >
// begin-codegen
export type NewMessageHandler<T = Message> = ParsedUpdateHandler< export type NewMessageHandler<T = Message> = ParsedUpdateHandler<
'new_message', 'new_message',
T T
@ -68,3 +69,5 @@ export type UpdateHandler =
| ChatMemberUpdateHandler | ChatMemberUpdateHandler
| InlineQueryHandler | InlineQueryHandler
| ChosenInlineResultHandler | ChosenInlineResultHandler
// end-codegen