test(tl-utils): improved test coverage
This commit is contained in:
parent
96a443f8d3
commit
59a4a7553f
23 changed files with 1665 additions and 755 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -10,4 +10,6 @@ private/
|
||||||
# docs are generated in ci
|
# docs are generated in ci
|
||||||
docs
|
docs
|
||||||
|
|
||||||
*.tsbuildinfo
|
coverage
|
||||||
|
.rollup.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
574
packages/tl-utils/src/__snapshots__/diff.test.ts.snap
Normal file
574
packages/tl-utils/src/__snapshots__/diff.test.ts.snap
Normal file
|
@ -0,0 +1,574 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows added constructors 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"name": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows added unions 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3739166976,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"arguments": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": {
|
||||||
|
"new": "Foo",
|
||||||
|
"old": "int",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"new": 3348640942,
|
||||||
|
"old": 1331975629,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3739166976,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows modified constructors 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"arguments": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": {
|
||||||
|
"new": "Foo",
|
||||||
|
"old": "int",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"new": 3348640942,
|
||||||
|
"old": 1331975629,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows modified methods 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"id": {
|
||||||
|
"new": 3994885231,
|
||||||
|
"old": 471282454,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [
|
||||||
|
undefined,
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
undefined,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows modified unions 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"arguments": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": {
|
||||||
|
"new": "Foo",
|
||||||
|
"old": "int",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"new": 3348640942,
|
||||||
|
"old": 1331975629,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1809692154,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1809692154,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"name": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows modified unions 2`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "Foo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 711487159,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 704164487,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test3",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "int",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 1331975629,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1809692154,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "Foo",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 711487159,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 704164487,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test3",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": "int",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 1331975629,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1809692154,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"name": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows modified unions 3`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"id": {
|
||||||
|
"new": 1997819349,
|
||||||
|
"old": 471282454,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": {
|
||||||
|
"new": 3739166976,
|
||||||
|
"old": 1809692154,
|
||||||
|
},
|
||||||
|
"name": "test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [
|
||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1997819349,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3739166976,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 471282454,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 1809692154,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows removed constructors 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3847402009,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test2",
|
||||||
|
"type": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"name": "Test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTlSchemasDifference > shows removed unions 1`] = `
|
||||||
|
{
|
||||||
|
"classes": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"arguments": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"type": {
|
||||||
|
"new": "Foo",
|
||||||
|
"old": "int",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"new": 3348640942,
|
||||||
|
"old": 1331975629,
|
||||||
|
},
|
||||||
|
"name": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3739166976,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [],
|
||||||
|
},
|
||||||
|
"unions": {
|
||||||
|
"added": [],
|
||||||
|
"modified": [],
|
||||||
|
"removed": [
|
||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 3739166976,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "test1",
|
||||||
|
"type": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
31
packages/tl-utils/src/__snapshots__/utils.test.ts.snap
Normal file
31
packages/tl-utils/src/__snapshots__/utils.test.ts.snap
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`groupTlEntriesByNamespace > should group entries correctly 1`] = `
|
||||||
|
{
|
||||||
|
"": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 0,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "bar",
|
||||||
|
"type": "Bar",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"foo": [
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 0,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "foo.bar",
|
||||||
|
"type": "FooBar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [],
|
||||||
|
"id": 0,
|
||||||
|
"kind": "class",
|
||||||
|
"name": "foo.baz",
|
||||||
|
"type": "FooBaz",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
|
@ -26,11 +26,15 @@ describe('calculateStaticSizes', () => {
|
||||||
|
|
||||||
it('correctly skips constructors with predicated fields', () => {
|
it('correctly skips constructors with predicated fields', () => {
|
||||||
test(
|
test(
|
||||||
'help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;',
|
'help.promoData#8c39793f flags:# expires:int psa_type:flags.1?int psa_message:flags.2?int = help.PromoData;',
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('correctly skips constructors with generic fields', () => {
|
||||||
|
test('invokeWithLayer {T:X} = !X;', {})
|
||||||
|
})
|
||||||
|
|
||||||
it('correctly skips constructors with non-static fields', () => {
|
it('correctly skips constructors with non-static fields', () => {
|
||||||
test('help.promoData#8c39793f psa_type:string psa_message:string = help.PromoData;', {})
|
test('help.promoData#8c39793f psa_type:string psa_message:string = help.PromoData;', {})
|
||||||
})
|
})
|
||||||
|
@ -62,4 +66,33 @@ describe('calculateStaticSizes', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('correctly handles differently sized union children', () => {
|
||||||
|
test(
|
||||||
|
'peerUser user_id:int53 = Peer;\n' +
|
||||||
|
'peerChannel channel_id:int53 access_hash:long = Peer;\n' +
|
||||||
|
'help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer = help.PromoData;',
|
||||||
|
{
|
||||||
|
peerUser: 12,
|
||||||
|
peerChannel: 20,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('correctly handles non static-sized union children', () => {
|
||||||
|
test(
|
||||||
|
'peerUser user_id:int53 = Peer;\n' +
|
||||||
|
'peerChannel channel_id:int53 access_hash:bytes = Peer;\n' +
|
||||||
|
'help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer = help.PromoData;',
|
||||||
|
{
|
||||||
|
peerUser: 12,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
test(
|
||||||
|
'peerUser user_id:int53 access_hash:bytes = Peer;\n' +
|
||||||
|
'peerChannel channel_id:int53 access_hash:bytes = Peer;\n' +
|
||||||
|
'help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer = help.PromoData;',
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`generateCodeForErrors > should correctly generate errors 1`] = `
|
||||||
|
[
|
||||||
|
"type MtErrorText =
|
||||||
|
| 'USER_NOT_FOUND'
|
||||||
|
| 'FLOOD_WAIT_%d'
|
||||||
|
|
||||||
|
| (string & {}) // to keep hints
|
||||||
|
|
||||||
|
interface MtErrorArgMap {
|
||||||
|
'FLOOD_WAIT_%d': { duration: number },
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcErrorWithArgs<T extends string> =
|
||||||
|
RpcError & { text: T } & (T extends keyof MtErrorArgMap ? (RpcError & MtErrorArgMap[T]) : {});
|
||||||
|
|
||||||
|
export class RpcError extends Error {
|
||||||
|
static BAD_REQUEST: 400;
|
||||||
|
|
||||||
|
|
||||||
|
readonly code: number;
|
||||||
|
readonly text: MtErrorText;
|
||||||
|
readonly unknown: boolean;
|
||||||
|
constructor(code: number, text: MtErrorText);
|
||||||
|
|
||||||
|
is<const T extends MtErrorText>(text: T): this is RpcErrorWithArgs<T>;
|
||||||
|
static is<const T extends MtErrorText>(err: unknown): err is RpcError;
|
||||||
|
static is<const T extends MtErrorText>(err: unknown, text: T): err is RpcErrorWithArgs<T>;
|
||||||
|
static create<const T extends MtErrorText>(code: number, text: T): RpcErrorWithArgs<T>;
|
||||||
|
static fromTl(obj: object): RpcError;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"const _descriptionsMap = JSON.parse('{\\"FLOOD_WAIT_%d\\":\\"Wait %d seconds\\"}')
|
||||||
|
class RpcError extends Error {
|
||||||
|
constructor(code, text, description) {
|
||||||
|
super(description || 'Unknown RPC error: [' + code + ':' + text + ']');
|
||||||
|
this.code = code;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
static is(err, text) { return err.constructor === RpcError && (!text || err.text === text); }
|
||||||
|
is(text) { return this.text === text; }
|
||||||
|
}
|
||||||
|
RpcError.fromTl = function (obj) {
|
||||||
|
if (obj.errorMessage in _descriptionsMap) {
|
||||||
|
return new RpcError(obj.errorCode, obj.errorMessage, _descriptionsMap[obj.errorMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var err = new RpcError(obj.errorCode, obj.errorMessage);
|
||||||
|
var match;
|
||||||
|
if ((match=err.text.match(/^FLOOD_WAIT_(\\\\d+)$/))!=null){ err.text = 'FLOOD_WAIT_%d'; err.duration = parseInt(match[1]) }
|
||||||
|
|
||||||
|
else return err
|
||||||
|
|
||||||
|
err.message = _descriptionsMap[err.text];
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
RpcError.create = function(code, text) {
|
||||||
|
var desc = _descriptionsMap[text];
|
||||||
|
var err = new RpcError(code, text, desc);
|
||||||
|
if (!desc) {
|
||||||
|
err.unknown = true;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
RpcError.BAD_REQUEST = 400;
|
||||||
|
|
||||||
|
exports.RpcError = RpcError;
|
||||||
|
",
|
||||||
|
]
|
||||||
|
`;
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > doesn't generate code for methods by default 1`] = `
|
||||||
|
"var m={
|
||||||
|
471282454:function(r){return{_:'test'}},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > generates code for methods if asked to 1`] = `
|
||||||
|
"var m={
|
||||||
|
471282454:function(r){return{_:'test'}},
|
||||||
|
2119910527:function(r){return{_:'test2'}},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > generates code for multiple entries 1`] = `
|
||||||
|
"var m={
|
||||||
|
471282454:function(r){return{_:'test'}},
|
||||||
|
2119910527:function(r){return{_:'test2'}},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > method return readers > doesn't include Bool parsing 1`] = `
|
||||||
|
"var m={
|
||||||
|
_results:{
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > method return readers > includes primitive return type parsing info 1`] = `
|
||||||
|
"var m={
|
||||||
|
1809692154:function(r){return{_:'test1'}},
|
||||||
|
_results:{
|
||||||
|
'test':function(r){return r.int()},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > method return readers > includes primitive vectors return type parsing info 1`] = `
|
||||||
|
"var m={
|
||||||
|
_results:{
|
||||||
|
'test':function(r){return r.vector(r.int)},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > method return readers > includes primitive vectors return type parsing info 2`] = `
|
||||||
|
"var m={
|
||||||
|
_results:{
|
||||||
|
'test':function(r){return r.vector(r.int)},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntries > updates readers used in bare vectors 1`] = `
|
||||||
|
"var m={
|
||||||
|
471282454:function(r=this){return{_:'test'}},
|
||||||
|
3562390222:function(r){return{_:'test2',a:r.vector(m[471282454],1),}},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for bare types 1`] = `"1945237724:function(r){return{_:'msg_container',messages:r.vector(m[155834844],1),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for bare types 2`] = `"2924480661:function(r){return{_:'future_salts',salts:r.vector(m[155834844]),current:r.object(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for bare types 3`] = `"2924480661:function(r){return{_:'future_salts',salts:r.vector(m[155834844],1),current:m[155834844](r),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with arguments before flags field 1`] = `"2262925665:function(r){var id=r.long(),flags=r.uint();return{_:'poll',id:id,quiz:!!(flags&8),question:r.string(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with generics 1`] = `"3667594509:function(r){return{_:'invokeWithLayer',layer:r.int(),query:r.object(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with multiple flags fields 1`] = `"1041346555:function(r){var flags=r.uint(),pts=r.int(),timeout=flags&2?r.int():void 0,flags2=r.uint();return{_:'updates.channelDifferenceEmpty',final:!!(flags&1),pts:pts,timeout:timeout,canDeleteChannel:!!(flags2&1),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with optional arguments 1`] = `"1041346555:function(r){var flags=r.uint();return{_:'updates.channelDifferenceEmpty',final:!!(flags&1),pts:r.int(),timeout:flags&2?r.int():void 0,}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with optional vector arguments 1`] = `"2338894028:function(r){var flags=r.uint();return{_:'messages.getWebPagePreview',message:r.string(),entities:flags&8?r.vector(r.object):void 0,}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with simple arguments 1`] = `"2299280777:function(r){return{_:'inputBotInlineMessageID',dcId:r.int(),id:r.long(),accessHash:r.long(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with simple arguments 2`] = `"341499403:function(r){return{_:'contact',userId:r.long(),mutual:r.boolean(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with simple arguments 3`] = `"2933316530:function(r){return{_:'maskCoords',n:r.int(),x:r.double(),y:r.double(),zoom:r.double(),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with true flags 1`] = `"649453030:function(r){var flags=r.uint();return{_:'messages.messageEditData',caption:!!(flags&1),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors with vector arguments 1`] = `"2131196633:function(r){return{_:'contacts.resolvedPeer',peer:r.object(),chats:r.vector(r.object),users:r.vector(r.object),}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code for constructors without arguments 1`] = `"2875595611:function(r){return{_:'topPeerCategoryBotsPM'}},"`;
|
||||||
|
|
||||||
|
exports[`generateReaderCodeForTlEntry > generates code with raw flags for constructors with flags 1`] = `"1554225816:function(r){var flags=r.uint(),flags2=r.uint();return{_:'test',flags:flags,flags2:flags2,}},"`;
|
292
packages/tl-utils/src/codegen/__snapshots__/types.test.ts.snap
Normal file
292
packages/tl-utils/src/codegen/__snapshots__/types.test.ts.snap
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds return type comments 1`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test method
|
||||||
|
*
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawTestRequest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds return type comments 2`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test method
|
||||||
|
*
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawTestRequest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds return type comments 3`] = `
|
||||||
|
"/**
|
||||||
|
* RPC method returns {@link tl.TypeTest} array
|
||||||
|
*/
|
||||||
|
interface RawTestRequest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds tdlib style comments 1`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test constructor
|
||||||
|
*/
|
||||||
|
interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
/**
|
||||||
|
* Some field
|
||||||
|
*/
|
||||||
|
field: number;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds tl style comments 1`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test constructor
|
||||||
|
*/
|
||||||
|
interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds usage info comments 1`] = `
|
||||||
|
"/**
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*
|
||||||
|
* This method is **not** available for bots
|
||||||
|
*
|
||||||
|
* This method *may* throw one of these errors: FOO, BAR
|
||||||
|
*/
|
||||||
|
interface RawTestRequest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > adds usage info comments 2`] = `
|
||||||
|
"/**
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*
|
||||||
|
* This method is **not** available for normal users
|
||||||
|
*/
|
||||||
|
interface RawTestBotRequest {
|
||||||
|
_: 'testBot';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > should not break @link tags 1`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test constructor with a very long comment
|
||||||
|
* {@link whatever} more text
|
||||||
|
*/
|
||||||
|
interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > wraps long comments 1`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test constructor with a very very very very very
|
||||||
|
* very very very long comment
|
||||||
|
*/
|
||||||
|
interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > comments > wraps long comments 2`] = `
|
||||||
|
"/**
|
||||||
|
* This is a test method with a very very very very very very
|
||||||
|
* very very long comment
|
||||||
|
*
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawTestRequest {
|
||||||
|
_: 'test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > generates code with raw flags for constructors with flags 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
flags: number;
|
||||||
|
flags2: number;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > ignores namespace for name 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test.test';
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > marks optional fields as optional 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
a?: boolean;
|
||||||
|
b?: string;
|
||||||
|
c?: tl.TypeFoo;
|
||||||
|
d?: tl.namespace.TypeFoo[];
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > renames non-primitive types 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
foo: tl.TypeFoo;
|
||||||
|
bar: tl.TypeBar[];
|
||||||
|
baz: tl.namespace.TypeBaz;
|
||||||
|
egg: tl.namespace.TypeEgg[];
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > replaces primitive types 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
a: number;
|
||||||
|
b: Long;
|
||||||
|
c: Double;
|
||||||
|
d: string;
|
||||||
|
e: Uint8Array;
|
||||||
|
f: boolean;
|
||||||
|
g: number[];
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlEntry > writes generic types 1`] = `
|
||||||
|
"interface RawInvokeWithoutUpdatesRequest<X extends tl.TlObject = tl.TlObject> {
|
||||||
|
_: 'invokeWithoutUpdates';
|
||||||
|
query: X;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with methods 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawGetTestRequest {
|
||||||
|
_: 'getTest';
|
||||||
|
}
|
||||||
|
interface RpcCallReturn {
|
||||||
|
'getTest': tl.TypeTest
|
||||||
|
}
|
||||||
|
type TypeTest = tl.RawTest
|
||||||
|
function isAnyTest(o: object): o is TypeTest
|
||||||
|
type RpcMethod =
|
||||||
|
| tl.RawGetTestRequest
|
||||||
|
|
||||||
|
type TlObject =
|
||||||
|
| tl.RawTest
|
||||||
|
| tl.RawGetTestRequest"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with methods 2`] = `
|
||||||
|
"ns.isAnyTest = _isAny('Test');
|
||||||
|
_types = JSON.parse('{\\"test\\":\\"Test\\"}');"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with multi-unions 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}
|
||||||
|
interface RawTest2 {
|
||||||
|
_: 'test2';
|
||||||
|
}
|
||||||
|
interface RpcCallReturn {
|
||||||
|
}
|
||||||
|
type TypeTest = tl.RawTest | tl.RawTest2
|
||||||
|
function isAnyTest(o: object): o is TypeTest
|
||||||
|
|
||||||
|
type TlObject =
|
||||||
|
| tl.RawTest
|
||||||
|
| tl.RawTest2"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with multi-unions 2`] = `
|
||||||
|
"ns.isAnyTest = _isAny('Test');
|
||||||
|
_types = JSON.parse('{\\"test\\":\\"Test\\",\\"test2\\":\\"Test\\"}');"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with namespaces 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}
|
||||||
|
interface RawTest2 {
|
||||||
|
_: 'test2';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* RPC method returns {@link tl.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawGetTestRequest {
|
||||||
|
_: 'getTest';
|
||||||
|
}
|
||||||
|
interface RpcCallReturn extends test.RpcCallReturn {
|
||||||
|
'getTest': tl.TypeTest
|
||||||
|
}
|
||||||
|
type TypeTest = tl.RawTest | tl.RawTest2
|
||||||
|
function isAnyTest(o: object): o is TypeTest
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
interface RawTest {
|
||||||
|
_: 'test.test';
|
||||||
|
}
|
||||||
|
interface RawTest2 {
|
||||||
|
_: 'test.test2';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* RPC method returns {@link tl.test.TypeTest}
|
||||||
|
*/
|
||||||
|
interface RawGetTestRequest {
|
||||||
|
_: 'test.getTest';
|
||||||
|
}
|
||||||
|
interface RpcCallReturn {
|
||||||
|
'test.getTest': tl.test.TypeTest
|
||||||
|
}
|
||||||
|
type TypeTest = tl.test.RawTest | tl.test.RawTest2
|
||||||
|
function isAnyTest(o: object): o is TypeTest
|
||||||
|
}
|
||||||
|
type RpcMethod =
|
||||||
|
| tl.RawGetTestRequest
|
||||||
|
| tl.test.RawGetTestRequest
|
||||||
|
|
||||||
|
type TlObject =
|
||||||
|
| tl.RawTest
|
||||||
|
| tl.RawTest2
|
||||||
|
| tl.test.RawTest
|
||||||
|
| tl.test.RawTest2
|
||||||
|
| tl.RawGetTestRequest
|
||||||
|
| tl.test.RawGetTestRequest"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes schemas with namespaces 2`] = `
|
||||||
|
"ns.isAnyTest = _isAny('Test');
|
||||||
|
ns.test = {};
|
||||||
|
(function(ns){
|
||||||
|
ns.isAnyTest = _isAny('test.Test');
|
||||||
|
})(ns.test);
|
||||||
|
_types = JSON.parse('{\\"test\\":\\"Test\\",\\"test2\\":\\"Test\\",\\"test.test\\":\\"test.Test\\",\\"test.test2\\":\\"test.Test\\"}');"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes simple schemas 1`] = `
|
||||||
|
"interface RawTest {
|
||||||
|
_: 'test';
|
||||||
|
}
|
||||||
|
interface RpcCallReturn {
|
||||||
|
}
|
||||||
|
type TypeTest = tl.RawTest
|
||||||
|
function isAnyTest(o: object): o is TypeTest
|
||||||
|
|
||||||
|
type TlObject =
|
||||||
|
| tl.RawTest"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateTypescriptDefinitionsForTlSchema > writes simple schemas 2`] = `
|
||||||
|
"ns.isAnyTest = _isAny('Test');
|
||||||
|
_types = JSON.parse('{\\"test\\":\\"Test\\"}');"
|
||||||
|
`;
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntries > generates code for bare types 1`] = `
|
||||||
|
"var m={
|
||||||
|
'future_salt':function(w,v){w.uint(155834844);w.bytes(h(v,'salt'));},
|
||||||
|
'future_salts':function(w,v){w.uint(2924480661);w.vector(m._bare[155834844],h(v,'salts'),1);m._bare[155834844](w,h(v,'current'));},
|
||||||
|
_bare:{
|
||||||
|
155834844:function(w=this,v){w.bytes(h(v,'salt'));},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntries > should include prelude by default 1`] = `
|
||||||
|
"function h(o,p){var q=o[p];if(q===void 0)throw Error('Object '+o._+' is missing required property '+p);return q}
|
||||||
|
var m={
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntries > should include static sizes calculations 1`] = `
|
||||||
|
"function h(o,p){var q=o[p];if(q===void 0)throw Error('Object '+o._+' is missing required property '+p);return q}
|
||||||
|
var m={
|
||||||
|
'test1':function(w,v){w.uint(102026291);w.int(h(v,'foo'));w.int(h(v,'bar'));},
|
||||||
|
'test2':function(w,v){w.uint(2926357645);w.int(h(v,'foo'));w.double(h(v,'bar'));},
|
||||||
|
'test3':function(w,v){w.uint(3373702963);w.int(h(v,'foo'));w.bytes(h(v,'bar'));},
|
||||||
|
_staticSize:{
|
||||||
|
'test1':12,
|
||||||
|
'test2':16,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > automatically computes constructor ID if needed 1`] = `"'topPeerCategoryBotsPM':function(w){w.uint(2875595611);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for bare vectors 1`] = `"'msg_container':function(w,v){w.uint(1945237724);w.vector(m._bare[155834844],h(v,'messages'),1);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for bare vectors 2`] = `"'future_salts':function(w,v){w.uint(2924480661);w.vector(m._bare[155834844],h(v,'salts'));w.object(h(v,'current'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with generics 1`] = `"'invokeWithLayer':function(w,v){w.uint(3667594509);w.int(h(v,'layer'));w.object(h(v,'query'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with multiple fields using the same flag 1`] = `"'inputMediaPoll':function(w,v){w.uint(261416433);var flags=0;var _solution=v.solution!==undefined;var _solutionEntities=v.solutionEntities&&v.solutionEntities.length;var _flags_1=_solution||_solutionEntities;if(_flags_1)flags|=2;w.uint(flags);if(_flags_1)w.string(v.solution);if(_flags_1)w.vector(w.object,v.solutionEntities);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with multiple flags fields 1`] = `"'updates.channelDifferenceEmpty':function(w,v){w.uint(1041346555);var flags=0;if(v.final===true)flags|=1;var _timeout=v.timeout!==undefined;if(_timeout)flags|=2;w.uint(flags);w.int(h(v,'pts'));if(_timeout)w.int(v.timeout);var flags2=0;if(v.canDeleteChannel===true)flags2|=1;w.uint(flags2);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with optional arguments 1`] = `"'updates.channelDifferenceEmpty':function(w,v){w.uint(1041346555);var flags=0;if(v.final===true)flags|=1;var _timeout=v.timeout!==undefined;if(_timeout)flags|=2;w.uint(flags);w.int(h(v,'pts'));if(_timeout)w.int(v.timeout);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with optional vector arguments 1`] = `"'messages.getWebPagePreview':function(w,v){w.uint(2338894028);var flags=0;var _entities=v.entities&&v.entities.length;if(_entities)flags|=8;w.uint(flags);w.string(h(v,'message'));if(_entities)w.vector(w.object,v.entities);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with simple arguments 1`] = `"'inputBotInlineMessageID':function(w,v){w.uint(2299280777);w.int(h(v,'dcId'));w.long(h(v,'id'));w.long(h(v,'accessHash'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with simple arguments 2`] = `"'contact':function(w,v){w.uint(341499403);w.long(h(v,'userId'));w.boolean(h(v,'mutual'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with simple arguments 3`] = `"'maskCoords':function(w,v){w.uint(2933316530);w.int(h(v,'n'));w.double(h(v,'x'));w.double(h(v,'y'));w.double(h(v,'zoom'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with true flags 1`] = `"'messages.messageEditData':function(w,v){w.uint(649453030);var flags=0;if(v.caption===true)flags|=1;w.uint(flags);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors with vector arguments 1`] = `"'contacts.resolvedPeer':function(w,v){w.uint(2131196633);w.object(h(v,'peer'));w.vector(w.object,h(v,'chats'));w.vector(w.object,h(v,'users'));},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code for constructors without arguments 1`] = `"'topPeerCategoryBotsPM':function(w){w.uint(2875595611);},"`;
|
||||||
|
|
||||||
|
exports[`generateWriterCodeForTlEntry > generates code with raw flags for constructors with flags 1`] = `"'test':function(w,v){w.uint(1554225816);var flags=v.flags;w.uint(flags);var flags2=v.flags2;w.uint(flags2);},"`;
|
20
packages/tl-utils/src/codegen/errors.test.ts
Normal file
20
packages/tl-utils/src/codegen/errors.test.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { generateCodeForErrors } from './errors.js'
|
||||||
|
|
||||||
|
describe('generateCodeForErrors', () => {
|
||||||
|
it('should correctly generate errors', () => {
|
||||||
|
expect(
|
||||||
|
generateCodeForErrors({
|
||||||
|
base: { BAD_REQUEST: 400 },
|
||||||
|
errors: {
|
||||||
|
USER_NOT_FOUND: { code: 400, name: 'USER_NOT_FOUND' },
|
||||||
|
'FLOOD_WAIT_%d': { code: 420, name: 'FLOOD_WAIT_%d', description: 'Wait %d seconds' },
|
||||||
|
},
|
||||||
|
throws: {},
|
||||||
|
userOnly: {},
|
||||||
|
botOnly: {},
|
||||||
|
}),
|
||||||
|
).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,178 +1,130 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import { parseTlToEntries } from '../parse.js'
|
import { parseTlToEntries } from '../parse.js'
|
||||||
import { generateReaderCodeForTlEntry } from './reader.js'
|
import { generateReaderCodeForTlEntries, generateReaderCodeForTlEntry } from './reader.js'
|
||||||
|
|
||||||
describe('generateReaderCodeForTlEntry', () => {
|
describe('generateReaderCodeForTlEntry', () => {
|
||||||
const test = (tl: string, ...js: string[]) => {
|
const test = (...tl: string[]) => {
|
||||||
const entry = parseTlToEntries(tl).slice(-1)[0]
|
const entry = parseTlToEntries(tl.join('\n')).slice(-1)[0]
|
||||||
expect(generateReaderCodeForTlEntry(entry)).toEqual(`${entry.id}:function(r){${js.join('')}},`)
|
expect(generateReaderCodeForTlEntry(entry)).toMatchSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
it('generates code for constructors without arguments', () => {
|
it('generates code for constructors without arguments', () => {
|
||||||
test('topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;', "return{_:'topPeerCategoryBotsPM'}")
|
test('topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with simple arguments', () => {
|
it('generates code for constructors with simple arguments', () => {
|
||||||
test(
|
test('inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;')
|
||||||
'inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;',
|
test('contact#145ade0b user_id:long mutual:Bool = Contact;')
|
||||||
'return{',
|
test('maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;')
|
||||||
"_:'inputBotInlineMessageID',",
|
|
||||||
'dcId:r.int(),',
|
|
||||||
'id:r.long(),',
|
|
||||||
'accessHash:r.long(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
'contact#145ade0b user_id:long mutual:Bool = Contact;',
|
|
||||||
'return{',
|
|
||||||
"_:'contact',",
|
|
||||||
'userId:r.long(),',
|
|
||||||
'mutual:r.boolean(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
'maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;',
|
|
||||||
'return{',
|
|
||||||
"_:'maskCoords',",
|
|
||||||
'n:r.int(),',
|
|
||||||
'x:r.double(),',
|
|
||||||
'y:r.double(),',
|
|
||||||
'zoom:r.double(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with true flags', () => {
|
it('generates code for constructors with true flags', () => {
|
||||||
test(
|
test('messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;')
|
||||||
'messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;',
|
|
||||||
'var flags=r.uint();',
|
|
||||||
'return{',
|
|
||||||
"_:'messages.messageEditData',",
|
|
||||||
'caption:!!(flags&1),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with optional arguments', () => {
|
it('generates code for constructors with optional arguments', () => {
|
||||||
test(
|
test(
|
||||||
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;',
|
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;',
|
||||||
'var flags=r.uint();',
|
|
||||||
'return{',
|
|
||||||
"_:'updates.channelDifferenceEmpty',",
|
|
||||||
'final:!!(flags&1),',
|
|
||||||
'pts:r.int(),',
|
|
||||||
'timeout:flags&2?r.int():void 0,',
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with arguments before flags field', () => {
|
it('generates code for constructors with arguments before flags field', () => {
|
||||||
test(
|
test('poll#86e18161 id:long flags:# quiz:flags.3?true question:string = Poll;')
|
||||||
'poll#86e18161 id:long flags:# quiz:flags.3?true question:string = Poll;',
|
|
||||||
'var id=r.long(),',
|
|
||||||
'flags=r.uint();',
|
|
||||||
'return{',
|
|
||||||
"_:'poll',",
|
|
||||||
'id:id,',
|
|
||||||
'quiz:!!(flags&8),',
|
|
||||||
'question:r.string(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with multiple flags fields', () => {
|
it('generates code for constructors with multiple flags fields', () => {
|
||||||
test(
|
test(
|
||||||
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int flags2:# can_delete_channel:flags2.0?true = updates.ChannelDifference;',
|
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int flags2:# can_delete_channel:flags2.0?true = updates.ChannelDifference;',
|
||||||
'var flags=r.uint(),',
|
|
||||||
'pts=r.int(),',
|
|
||||||
'timeout=flags&2?r.int():void 0,',
|
|
||||||
'flags2=r.uint();',
|
|
||||||
'return{',
|
|
||||||
"_:'updates.channelDifferenceEmpty',",
|
|
||||||
'final:!!(flags&1),',
|
|
||||||
'pts:pts,',
|
|
||||||
'timeout:timeout,',
|
|
||||||
'canDeleteChannel:!!(flags2&1),',
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with vector arguments', () => {
|
it('generates code for constructors with vector arguments', () => {
|
||||||
test(
|
test('contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;')
|
||||||
'contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;',
|
|
||||||
'return{',
|
|
||||||
"_:'contacts.resolvedPeer',",
|
|
||||||
'peer:r.object(),',
|
|
||||||
'chats:r.vector(r.object),',
|
|
||||||
'users:r.vector(r.object),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with optional vector arguments', () => {
|
it('generates code for constructors with optional vector arguments', () => {
|
||||||
test(
|
test(
|
||||||
'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;',
|
'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;',
|
||||||
'var flags=r.uint();',
|
|
||||||
'return{',
|
|
||||||
"_:'messages.getWebPagePreview',",
|
|
||||||
'message:r.string(),',
|
|
||||||
'entities:flags&8?r.vector(r.object):void 0,',
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with generics', () => {
|
it('generates code for constructors with generics', () => {
|
||||||
test(
|
test('invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;')
|
||||||
'invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;',
|
|
||||||
'return{',
|
|
||||||
"_:'invokeWithLayer',",
|
|
||||||
'layer:r.int(),',
|
|
||||||
'query:r.object(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for bare types', () => {
|
it('generates code for bare types', () => {
|
||||||
|
test('message#0949d9dc = Message;', 'msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;')
|
||||||
test(
|
test(
|
||||||
'message#0949d9dc = Message;\n' + 'msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;',
|
'future_salt#0949d9dc = FutureSalt;',
|
||||||
'return{',
|
'future_salts#ae500895 salts:Vector<future_salt> current:FutureSalt = FutureSalts;',
|
||||||
"_:'msg_container',",
|
|
||||||
'messages:r.vector(m[155834844],1),',
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
test(
|
test(
|
||||||
'future_salt#0949d9dc = FutureSalt;\n' +
|
'future_salt#0949d9dc = FutureSalt;\n',
|
||||||
'future_salts#ae500895 salts:Vector<future_salt> current:FutureSalt = FutureSalts;',
|
'future_salts#ae500895 salts:vector<future_salt> current:future_salt = FutureSalts;',
|
||||||
'return{',
|
|
||||||
"_:'future_salts',",
|
|
||||||
'salts:r.vector(m[155834844]),',
|
|
||||||
'current:r.object(),',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
'future_salt#0949d9dc = FutureSalt;\n' +
|
|
||||||
'future_salts#ae500895 salts:vector<future_salt> current:future_salt = FutureSalts;',
|
|
||||||
'return{',
|
|
||||||
"_:'future_salts',",
|
|
||||||
'salts:r.vector(m[155834844],1),',
|
|
||||||
'current:m[155834844](r),',
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code with raw flags for constructors with flags', () => {
|
it('generates code with raw flags for constructors with flags', () => {
|
||||||
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
||||||
expect(generateReaderCodeForTlEntry(entry, { includeFlags: true })).toEqual(
|
expect(generateReaderCodeForTlEntry(entry, { includeFlags: true })).toMatchSnapshot()
|
||||||
`${entry.id}:function(r){${[
|
})
|
||||||
'var flags=r.uint(),',
|
})
|
||||||
'flags2=r.uint();',
|
|
||||||
'return{',
|
describe('generateReaderCodeForTlEntries', () => {
|
||||||
"_:'test',",
|
it('generates code for multiple entries', () => {
|
||||||
'flags:flags,',
|
const entries = parseTlToEntries('test = Test;\ntest2 = Test2;\n')
|
||||||
'flags2:flags2,',
|
|
||||||
'}',
|
expect(generateReaderCodeForTlEntries(entries)).toMatchSnapshot()
|
||||||
].join('')}},`,
|
})
|
||||||
)
|
|
||||||
|
it("doesn't generate code for methods by default", () => {
|
||||||
|
const entries = parseTlToEntries(['test = Test;', '---functions---', 'test2 = Test2;'].join('\n'))
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries)).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('generates code for methods if asked to', () => {
|
||||||
|
const entries = parseTlToEntries(['test = Test;', '---functions---', 'test2 = Test2;'].join('\n'))
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries, { includeMethods: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates readers used in bare vectors', () => {
|
||||||
|
const entries = parseTlToEntries(['test = Test;', 'test2 a:vector<test> = Test2;'].join('\n'))
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries)).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('method return readers', () => {
|
||||||
|
it('includes primitive return type parsing info', () => {
|
||||||
|
const entries = parseTlToEntries(['test1 = Test;', '---functions---', 'test = int;'].join('\n'))
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries, { includeMethodResults: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes primitive vectors return type parsing info', () => {
|
||||||
|
const entries = parseTlToEntries(['---functions---', 'test = Vector<int>;'].join('\n'), {
|
||||||
|
parseMethodTypes: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries, { includeMethodResults: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes primitive vectors return type parsing info', () => {
|
||||||
|
const entries = parseTlToEntries(['---functions---', 'test = Vector<int>;'].join('\n'), {
|
||||||
|
parseMethodTypes: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries, { includeMethodResults: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("doesn't include Bool parsing", () => {
|
||||||
|
const entries = parseTlToEntries(['---functions---', 'test = Bool;'].join('\n'), {
|
||||||
|
parseMethodTypes: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(generateReaderCodeForTlEntries(entries, { includeMethodResults: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,161 +5,89 @@ import { parseFullTlSchema } from '../schema.js'
|
||||||
import { generateTypescriptDefinitionsForTlEntry, generateTypescriptDefinitionsForTlSchema } from './types.js'
|
import { generateTypescriptDefinitionsForTlEntry, generateTypescriptDefinitionsForTlSchema } from './types.js'
|
||||||
|
|
||||||
describe('generateTypescriptDefinitionsForTlEntry', () => {
|
describe('generateTypescriptDefinitionsForTlEntry', () => {
|
||||||
const test = (tl: string, ...ts: string[]) => {
|
const test = (...tl: string[]) => {
|
||||||
const entry = parseTlToEntries(tl)[0]
|
const entry = parseTlToEntries(tl.join('\n'), { parseMethodTypes: true })[0]
|
||||||
expect(generateTypescriptDefinitionsForTlEntry(entry)).toEqual(ts.join('\n'))
|
expect(generateTypescriptDefinitionsForTlEntry(entry)).toMatchSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
it('replaces primitive types', () => {
|
it('replaces primitive types', () => {
|
||||||
test(
|
test('test a:int b:long c:double d:string e:bytes f:Bool g:vector<int> = Test;')
|
||||||
'test a:int b:long c:double d:string e:bytes f:Bool g:vector<int> = Test;',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
' a: number;',
|
|
||||||
' b: Long;',
|
|
||||||
' c: Double;',
|
|
||||||
' d: string;',
|
|
||||||
' e: Uint8Array;',
|
|
||||||
' f: boolean;',
|
|
||||||
' g: number[];',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('ignores namespace for name', () => {
|
it('ignores namespace for name', () => {
|
||||||
test('test.test = Test;', 'interface RawTest {', " _: 'test.test';", '}')
|
test('test.test = Test;')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renames non-primitive types', () => {
|
it('renames non-primitive types', () => {
|
||||||
test(
|
test('test foo:Foo bar:vector<Bar> baz:namespace.Baz egg:vector<namespace.Egg> = Test;')
|
||||||
'test foo:Foo bar:vector<Bar> baz:namespace.Baz egg:vector<namespace.Egg> = Test;',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
' foo: tl.TypeFoo;',
|
|
||||||
' bar: tl.TypeBar[];',
|
|
||||||
' baz: tl.namespace.TypeBaz;',
|
|
||||||
' egg: tl.namespace.TypeEgg[];',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('marks optional fields as optional', () => {
|
it('marks optional fields as optional', () => {
|
||||||
test(
|
test('test flags:# a:flags.0?true b:flags.1?string c:flags.2?Foo d:flags.3?vector<namespace.Foo> = Test;')
|
||||||
'test flags:# a:flags.0?true b:flags.1?string c:flags.2?Foo d:flags.3?vector<namespace.Foo> = Test;',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
' a?: boolean;',
|
|
||||||
' b?: string;',
|
|
||||||
' c?: tl.TypeFoo;',
|
|
||||||
' d?: tl.namespace.TypeFoo[];',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('comments', () => {
|
describe('comments', () => {
|
||||||
it('adds tl style comments', () => {
|
it('adds tl style comments', () => {
|
||||||
test(
|
test('// This is a test constructor', 'test = Test;')
|
||||||
'// This is a test constructor\n' + 'test = Test;',
|
})
|
||||||
'/**',
|
|
||||||
' * This is a test constructor',
|
it('adds return type comments', () => {
|
||||||
' */',
|
test('---functions---', '// This is a test method', 'test = Test;')
|
||||||
'interface RawTest {',
|
test('---functions---', '// This is a test method', 'test = Test;')
|
||||||
" _: 'test';",
|
test('---functions---\n', '// This is a test method\n', 'test = Vector<Test>;')
|
||||||
'}',
|
})
|
||||||
)
|
|
||||||
test(
|
it('adds usage info comments', () => {
|
||||||
'---functions---\n' + '// This is a test method\n' + 'test = Test;',
|
const entries = parseTlToEntries('---functions---\ntest = Test;\ntestBot = Test;')
|
||||||
'/**',
|
const [result, resultBot] = entries.map((it) =>
|
||||||
' * This is a test method',
|
generateTypescriptDefinitionsForTlEntry(it, 'tl.', {
|
||||||
' * ',
|
base: {},
|
||||||
' * RPC method returns {@link tl.TypeTest}',
|
errors: {},
|
||||||
' */',
|
throws: { test: ['FOO', 'BAR'] },
|
||||||
'interface RawTestRequest {',
|
userOnly: { test: 1 },
|
||||||
" _: 'test';",
|
botOnly: { testBot: 1 },
|
||||||
'}',
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expect(result).toMatchSnapshot()
|
||||||
|
expect(resultBot).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('adds tdlib style comments', () => {
|
it('adds tdlib style comments', () => {
|
||||||
test(
|
test('// @description This is a test constructor', '// @field Some field', 'test field:int = Test;')
|
||||||
'// @description This is a test constructor\n' + '// @field Some field\n' + 'test field:int = Test;',
|
|
||||||
'/**',
|
|
||||||
' * This is a test constructor',
|
|
||||||
' */',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
' /**',
|
|
||||||
' * Some field',
|
|
||||||
' */',
|
|
||||||
' field: number;',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('wraps long comments', () => {
|
it('wraps long comments', () => {
|
||||||
test(
|
test(
|
||||||
'// This is a test constructor with a very very very very very very very very long comment\n' +
|
'// This is a test constructor with a very very very very very very very very long comment',
|
||||||
'test = Test;',
|
'test = Test;',
|
||||||
'/**',
|
|
||||||
' * This is a test constructor with a very very very very very',
|
|
||||||
' * very very very long comment',
|
|
||||||
' */',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'---functions---\n' +
|
'---functions---',
|
||||||
'// This is a test method with a very very very very very very very very long comment\n' +
|
'// This is a test method with a very very very very very very very very long comment',
|
||||||
'test = Test;',
|
'test = Test;',
|
||||||
'/**',
|
|
||||||
' * This is a test method with a very very very very very very',
|
|
||||||
' * very very long comment',
|
|
||||||
' * ',
|
|
||||||
' * RPC method returns {@link tl.TypeTest}',
|
|
||||||
' */',
|
|
||||||
'interface RawTestRequest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not break @link tags', () => {
|
it('should not break @link tags', () => {
|
||||||
test(
|
test('// This is a test constructor with a very long comment {@link whatever} more text', 'test = Test;')
|
||||||
'// This is a test constructor with a very long comment {@link whatever} more text\n' + 'test = Test;',
|
|
||||||
'/**',
|
|
||||||
' * This is a test constructor with a very long comment',
|
|
||||||
' * {@link whatever} more text',
|
|
||||||
' */',
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes generic types', () => {
|
it('writes generic types', () => {
|
||||||
test(
|
test('---functions---\ninvokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;')
|
||||||
'---functions---\ninvokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;',
|
|
||||||
'interface RawInvokeWithoutUpdatesRequest<X extends tl.TlObject = tl.TlObject> {',
|
|
||||||
" _: 'invokeWithoutUpdates';",
|
|
||||||
' query: X;',
|
|
||||||
'}',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code with raw flags for constructors with flags', () => {
|
it('generates code with raw flags for constructors with flags', () => {
|
||||||
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
||||||
expect(generateTypescriptDefinitionsForTlEntry(entry, undefined, undefined, true)).toEqual(
|
expect(generateTypescriptDefinitionsForTlEntry(entry, undefined, undefined, true)).toMatchSnapshot()
|
||||||
['interface RawTest {', " _: 'test';", ' flags: number;', ' flags2: number;', '}'].join('\n'),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generateTypescriptDefinitionsForTlSchema', () => {
|
describe('generateTypescriptDefinitionsForTlSchema', () => {
|
||||||
const test = (tl: string, ts: string[], js: string[]) => {
|
const test = (...tl: string[]) => {
|
||||||
const entries = parseTlToEntries(tl)
|
const entries = parseTlToEntries(tl.join('\n'))
|
||||||
const schema = parseFullTlSchema(entries)
|
const schema = parseFullTlSchema(entries)
|
||||||
|
|
||||||
let [codeTs, codeJs] = generateTypescriptDefinitionsForTlSchema(schema, 0)
|
let [codeTs, codeJs] = generateTypescriptDefinitionsForTlSchema(schema, 0)
|
||||||
|
@ -172,152 +100,31 @@ describe('generateTypescriptDefinitionsForTlSchema', () => {
|
||||||
// skip prelude
|
// skip prelude
|
||||||
codeJs = codeJs.substring(codeJs.indexOf('ns.LAYER = 0;') + 14, codeJs.length - 15)
|
codeJs = codeJs.substring(codeJs.indexOf('ns.LAYER = 0;') + 14, codeJs.length - 15)
|
||||||
|
|
||||||
expect(codeTs.trim()).toEqual(ts.join('\n'))
|
expect(codeTs.trim()).toMatchSnapshot()
|
||||||
expect(codeJs.trim()).toEqual(js.join('\n'))
|
expect(codeJs.trim()).toMatchSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
it('writes simple schemas', () => {
|
it('writes simple schemas', () => {
|
||||||
test(
|
test('test = Test;')
|
||||||
'test = Test;',
|
|
||||||
[
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
'interface RpcCallReturn {',
|
|
||||||
'}',
|
|
||||||
'type TypeTest = tl.RawTest',
|
|
||||||
'function isAnyTest(o: object): o is TypeTest',
|
|
||||||
'',
|
|
||||||
'type TlObject =',
|
|
||||||
' | tl.RawTest',
|
|
||||||
],
|
|
||||||
["ns.isAnyTest = _isAny('Test');", '_types = JSON.parse(\'{"test":"Test"}\');'],
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes schemas with multi-unions', () => {
|
it('writes schemas with multi-unions', () => {
|
||||||
test(
|
test('test = Test;\ntest2 = Test;')
|
||||||
'test = Test;\ntest2 = Test;',
|
|
||||||
[
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
'interface RawTest2 {',
|
|
||||||
" _: 'test2';",
|
|
||||||
'}',
|
|
||||||
'interface RpcCallReturn {',
|
|
||||||
'}',
|
|
||||||
'type TypeTest = tl.RawTest | tl.RawTest2',
|
|
||||||
'function isAnyTest(o: object): o is TypeTest',
|
|
||||||
'',
|
|
||||||
'type TlObject =',
|
|
||||||
' | tl.RawTest',
|
|
||||||
' | tl.RawTest2',
|
|
||||||
],
|
|
||||||
["ns.isAnyTest = _isAny('Test');", '_types = JSON.parse(\'{"test":"Test","test2":"Test"}\');'],
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes schemas with methods', () => {
|
it('writes schemas with methods', () => {
|
||||||
test(
|
test('test = Test;\n---functions---\ngetTest = Test;')
|
||||||
'test = Test;\n---functions---\ngetTest = Test;',
|
|
||||||
[
|
|
||||||
'interface RawTest {',
|
|
||||||
" _: 'test';",
|
|
||||||
'}',
|
|
||||||
'/**',
|
|
||||||
' * RPC method returns {@link tl.TypeTest}',
|
|
||||||
' */',
|
|
||||||
'interface RawGetTestRequest {',
|
|
||||||
" _: 'getTest';",
|
|
||||||
'}',
|
|
||||||
'interface RpcCallReturn {',
|
|
||||||
" 'getTest': tl.TypeTest",
|
|
||||||
'}',
|
|
||||||
'type TypeTest = tl.RawTest',
|
|
||||||
'function isAnyTest(o: object): o is TypeTest',
|
|
||||||
'type RpcMethod =',
|
|
||||||
' | tl.RawGetTestRequest',
|
|
||||||
'',
|
|
||||||
'type TlObject =',
|
|
||||||
' | tl.RawTest',
|
|
||||||
' | tl.RawGetTestRequest',
|
|
||||||
],
|
|
||||||
["ns.isAnyTest = _isAny('Test');", '_types = JSON.parse(\'{"test":"Test"}\');'],
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes schemas with namespaces', () => {
|
it('writes schemas with namespaces', () => {
|
||||||
test(
|
test(
|
||||||
'test = Test;\n' +
|
'test = Test;\n',
|
||||||
'test2 = Test;\n' +
|
'test2 = Test;\n',
|
||||||
'test.test = test.Test;\n' +
|
'test.test = test.Test;\n',
|
||||||
'test.test2 = test.Test;\n' +
|
'test.test2 = test.Test;\n',
|
||||||
'---functions---\n' +
|
'---functions---\n',
|
||||||
'getTest = Test;\n' +
|
'getTest = Test;\n',
|
||||||
'test.getTest = test.Test;',
|
'test.getTest = test.Test;',
|
||||||
[
|
|
||||||
`
|
|
||||||
interface RawTest {
|
|
||||||
_: 'test';
|
|
||||||
}
|
|
||||||
interface RawTest2 {
|
|
||||||
_: 'test2';
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* RPC method returns {@link tl.TypeTest}
|
|
||||||
*/
|
|
||||||
interface RawGetTestRequest {
|
|
||||||
_: 'getTest';
|
|
||||||
}
|
|
||||||
interface RpcCallReturn extends test.RpcCallReturn {
|
|
||||||
'getTest': tl.TypeTest
|
|
||||||
}
|
|
||||||
type TypeTest = tl.RawTest | tl.RawTest2
|
|
||||||
function isAnyTest(o: object): o is TypeTest
|
|
||||||
|
|
||||||
namespace test {
|
|
||||||
interface RawTest {
|
|
||||||
_: 'test.test';
|
|
||||||
}
|
|
||||||
interface RawTest2 {
|
|
||||||
_: 'test.test2';
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* RPC method returns {@link tl.test.TypeTest}
|
|
||||||
*/
|
|
||||||
interface RawGetTestRequest {
|
|
||||||
_: 'test.getTest';
|
|
||||||
}
|
|
||||||
interface RpcCallReturn {
|
|
||||||
'test.getTest': tl.test.TypeTest
|
|
||||||
}
|
|
||||||
type TypeTest = tl.test.RawTest | tl.test.RawTest2
|
|
||||||
function isAnyTest(o: object): o is TypeTest
|
|
||||||
}
|
|
||||||
type RpcMethod =
|
|
||||||
| tl.RawGetTestRequest
|
|
||||||
| tl.test.RawGetTestRequest
|
|
||||||
|
|
||||||
type TlObject =
|
|
||||||
| tl.RawTest
|
|
||||||
| tl.RawTest2
|
|
||||||
| tl.test.RawTest
|
|
||||||
| tl.test.RawTest2
|
|
||||||
| tl.RawGetTestRequest
|
|
||||||
| tl.test.RawGetTestRequest
|
|
||||||
`.trim(),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
`
|
|
||||||
ns.isAnyTest = _isAny('Test');
|
|
||||||
ns.test = {};
|
|
||||||
(function(ns){
|
|
||||||
ns.isAnyTest = _isAny('test.Test');
|
|
||||||
})(ns.test);
|
|
||||||
_types = JSON.parse('{"test":"Test","test2":"Test","test.test":"test.Test","test.test2":"test.Test"}');
|
|
||||||
`.trim(),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -111,6 +111,8 @@ export function generateTypescriptDefinitionsForTlEntry(
|
||||||
if (errors) {
|
if (errors) {
|
||||||
if (errors.userOnly[entry.name]) {
|
if (errors.userOnly[entry.name]) {
|
||||||
comment += '\n\nThis method is **not** available for bots'
|
comment += '\n\nThis method is **not** available for bots'
|
||||||
|
} else if (errors.botOnly[entry.name]) {
|
||||||
|
comment += '\n\nThis method is **not** available for normal users'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.throws[entry.name]) {
|
if (errors.throws[entry.name]) {
|
||||||
|
@ -126,10 +128,20 @@ export function generateTypescriptDefinitionsForTlEntry(
|
||||||
if (entry.generics?.length) {
|
if (entry.generics?.length) {
|
||||||
genericsString = '<'
|
genericsString = '<'
|
||||||
entry.generics.forEach((it, idx) => {
|
entry.generics.forEach((it, idx) => {
|
||||||
const tsType = it.type === 'Type' ? 'tl.TlObject' : fullTypeName(it.type, baseNamespace)
|
/* c8 ignore next 3 */
|
||||||
|
if (it.type !== 'Type') {
|
||||||
|
throw new Error('Only Type generics are supported')
|
||||||
|
}
|
||||||
|
|
||||||
|
const tsType = `${baseNamespace}TlObject`
|
||||||
|
|
||||||
genericsIndex[it.name] = 1
|
genericsIndex[it.name] = 1
|
||||||
if (idx !== 0) genericsString += ', '
|
|
||||||
|
/* c8 ignore next 3 */
|
||||||
|
if (idx !== 0) {
|
||||||
|
throw new Error('Multiple generics are not supported')
|
||||||
|
}
|
||||||
|
|
||||||
genericsString += `${it.name} extends ${tsType} = ${tsType}`
|
genericsString += `${it.name} extends ${tsType} = ${tsType}`
|
||||||
})
|
})
|
||||||
genericsString += '>'
|
genericsString += '>'
|
||||||
|
|
57
packages/tl-utils/src/codegen/utils.test.ts
Normal file
57
packages/tl-utils/src/codegen/utils.test.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { camelToPascal, indent, jsComment, snakeToCamel } from './utils.js'
|
||||||
|
|
||||||
|
describe('snakeToCamel', () => {
|
||||||
|
it('should convert snake_case to camelCase', () => {
|
||||||
|
expect(snakeToCamel('snake_case')).toEqual('snakeCase')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should correctly handle numbers', () => {
|
||||||
|
expect(snakeToCamel('snake_case_123')).toEqual('snakeCase123')
|
||||||
|
expect(snakeToCamel('snake_case123')).toEqual('snakeCase123')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('camelToPascal', () => {
|
||||||
|
it('should convert camelCase to PascalCase', () => {
|
||||||
|
expect(camelToPascal('camelCase')).toEqual('CamelCase')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should correctly handle numbers', () => {
|
||||||
|
expect(camelToPascal('camelCase123')).toEqual('CamelCase123')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('jsComment', () => {
|
||||||
|
it('should format comments correctly', () => {
|
||||||
|
expect(jsComment('This is a comment')).toEqual('/**\n * This is a comment\n */')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should wrap long comments correctly', () => {
|
||||||
|
expect(jsComment('This is a very long comment which should be wrapped around here')).toEqual(
|
||||||
|
'/**\n * This is a very long comment which should be wrapped around\n * here\n */',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not break up links', () => {
|
||||||
|
expect(jsComment('This is a very long comment that wraps nearby a {@link link} yeah')).toEqual(
|
||||||
|
'/**\n * This is a very long comment that wraps nearby a {@link link}\n * yeah\n */',
|
||||||
|
)
|
||||||
|
expect(jsComment('This is a very long comment that wraps nearby this {@link link} yeah')).toEqual(
|
||||||
|
'/**\n * This is a very long comment that wraps nearby this\n * {@link link} yeah\n */',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('indent', () => {
|
||||||
|
it('should indent correctly', () => {
|
||||||
|
expect(indent(4, 'This is a comment')).toEqual(' This is a comment')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should indent correctly with multiple lines', () => {
|
||||||
|
expect(indent(4, 'This is a comment\nThis is another comment')).toEqual(
|
||||||
|
' This is a comment\n This is another comment',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
|
@ -4,11 +4,9 @@ import { parseTlToEntries } from '../parse.js'
|
||||||
import { generateWriterCodeForTlEntries, generateWriterCodeForTlEntry } from './writer.js'
|
import { generateWriterCodeForTlEntries, generateWriterCodeForTlEntry } from './writer.js'
|
||||||
|
|
||||||
describe('generateWriterCodeForTlEntry', () => {
|
describe('generateWriterCodeForTlEntry', () => {
|
||||||
const test = (tl: string, ...js: string[]) => {
|
const test = (...tl: string[]) => {
|
||||||
const entry = parseTlToEntries(tl).slice(-1)[0]
|
const entry = parseTlToEntries(tl.join('\n')).slice(-1)[0]
|
||||||
expect(generateWriterCodeForTlEntry(entry)).toEqual(
|
expect(generateWriterCodeForTlEntry(entry)).toMatchSnapshot()
|
||||||
`'${entry.name}':function(w${entry.arguments.length ? ',v' : ''}){w.uint(${entry.id});${js.join('')}},`,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('generates code for constructors without arguments', () => {
|
it('generates code for constructors without arguments', () => {
|
||||||
|
@ -16,148 +14,104 @@ describe('generateWriterCodeForTlEntry', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with simple arguments', () => {
|
it('generates code for constructors with simple arguments', () => {
|
||||||
test(
|
test('inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;')
|
||||||
'inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;',
|
test('contact#145ade0b user_id:long mutual:Bool = Contact;')
|
||||||
"w.int(h(v,'dcId'));",
|
test('maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;')
|
||||||
"w.long(h(v,'id'));",
|
|
||||||
"w.long(h(v,'accessHash'));",
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
'contact#145ade0b user_id:long mutual:Bool = Contact;',
|
|
||||||
"w.long(h(v,'userId'));",
|
|
||||||
"w.boolean(h(v,'mutual'));",
|
|
||||||
)
|
|
||||||
test(
|
|
||||||
'maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;',
|
|
||||||
"w.int(h(v,'n'));",
|
|
||||||
"w.double(h(v,'x'));",
|
|
||||||
"w.double(h(v,'y'));",
|
|
||||||
"w.double(h(v,'zoom'));",
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with true flags', () => {
|
it('generates code for constructors with true flags', () => {
|
||||||
test(
|
test('messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;')
|
||||||
'messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;',
|
|
||||||
'var flags=0;',
|
|
||||||
'if(v.caption===true)flags|=1;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with optional arguments', () => {
|
it('generates code for constructors with optional arguments', () => {
|
||||||
test(
|
test(
|
||||||
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;',
|
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;',
|
||||||
'var flags=0;',
|
|
||||||
'if(v.final===true)flags|=1;',
|
|
||||||
'var _timeout=v.timeout!==undefined;',
|
|
||||||
'if(_timeout)flags|=2;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
"w.int(h(v,'pts'));",
|
|
||||||
'if(_timeout)w.int(v.timeout);',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with multiple flags fields', () => {
|
it('generates code for constructors with multiple flags fields', () => {
|
||||||
test(
|
test(
|
||||||
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int flags2:# can_delete_channel:flags2.0?true = updates.ChannelDifference;',
|
'updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int flags2:# can_delete_channel:flags2.0?true = updates.ChannelDifference;',
|
||||||
'var flags=0;',
|
|
||||||
'if(v.final===true)flags|=1;',
|
|
||||||
'var _timeout=v.timeout!==undefined;',
|
|
||||||
'if(_timeout)flags|=2;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
"w.int(h(v,'pts'));",
|
|
||||||
'if(_timeout)w.int(v.timeout);',
|
|
||||||
'var flags2=0;',
|
|
||||||
'if(v.canDeleteChannel===true)flags2|=1;',
|
|
||||||
'w.uint(flags2);',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with multiple fields using the same flag', () => {
|
it('generates code for constructors with multiple fields using the same flag', () => {
|
||||||
test(
|
test(
|
||||||
'inputMediaPoll#f94e5f1 flags:# solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;',
|
'inputMediaPoll#f94e5f1 flags:# solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;',
|
||||||
'var flags=0;',
|
|
||||||
'var _solution=v.solution!==undefined;',
|
|
||||||
'var _solutionEntities=v.solutionEntities&&v.solutionEntities.length;',
|
|
||||||
'var _flags_1=_solution||_solutionEntities;',
|
|
||||||
'if(_flags_1)flags|=2;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
'if(_flags_1)w.string(v.solution);',
|
|
||||||
'if(_flags_1)w.vector(w.object,v.solutionEntities);',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with vector arguments', () => {
|
it('generates code for constructors with vector arguments', () => {
|
||||||
test(
|
test('contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;')
|
||||||
'contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;',
|
|
||||||
"w.object(h(v,'peer'));",
|
|
||||||
"w.vector(w.object,h(v,'chats'));",
|
|
||||||
"w.vector(w.object,h(v,'users'));",
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with optional vector arguments', () => {
|
it('generates code for constructors with optional vector arguments', () => {
|
||||||
test(
|
test(
|
||||||
'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;',
|
'messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;',
|
||||||
'var flags=0;',
|
|
||||||
'var _entities=v.entities&&v.entities.length;',
|
|
||||||
'if(_entities)flags|=8;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
"w.string(h(v,'message'));",
|
|
||||||
'if(_entities)w.vector(w.object,v.entities);',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for constructors with generics', () => {
|
it('generates code for constructors with generics', () => {
|
||||||
test(
|
test('invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;')
|
||||||
'invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;',
|
|
||||||
"w.int(h(v,'layer'));",
|
|
||||||
"w.object(h(v,'query'));",
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code for bare vectors', () => {
|
it('generates code for bare vectors', () => {
|
||||||
|
test('message#0949d9dc = Message;\n' + 'msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;')
|
||||||
test(
|
test(
|
||||||
'message#0949d9dc = Message;\n' + 'msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;',
|
'future_salt#0949d9dc = FutureSalt;',
|
||||||
"w.vector(m._bare[155834844],h(v,'messages'),1);",
|
'future_salts#ae500895 salts:Vector<future_salt> current:FutureSalt = FutureSalts;',
|
||||||
)
|
|
||||||
test(
|
|
||||||
'future_salt#0949d9dc = FutureSalt;\n' +
|
|
||||||
'future_salts#ae500895 salts:Vector<future_salt> current:FutureSalt = FutureSalts;',
|
|
||||||
"w.vector(m._bare[155834844],h(v,'salts'));",
|
|
||||||
"w.object(h(v,'current'));",
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('generates code with raw flags for constructors with flags', () => {
|
||||||
|
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
||||||
|
expect(generateWriterCodeForTlEntry(entry, { includeFlags: true })).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('automatically computes constructor ID if needed', () => {
|
||||||
|
const entry = parseTlToEntries('topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;')[0]
|
||||||
|
entry.id = 0
|
||||||
|
|
||||||
|
expect(generateWriterCodeForTlEntry(entry)).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws for invalid bit index', () => {
|
||||||
|
const entry = parseTlToEntries('test flags:# field:flags.33?true = Test;')[0]
|
||||||
|
expect(() => generateWriterCodeForTlEntry(entry, { includeFlags: true })).toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('generateWriterCodeForTlEntries', () => {
|
||||||
it('generates code for bare types', () => {
|
it('generates code for bare types', () => {
|
||||||
const entries = parseTlToEntries(
|
const entries = parseTlToEntries(
|
||||||
'future_salt#0949d9dc salt:bytes = FutureSalt;\n' +
|
'future_salt#0949d9dc salt:bytes = FutureSalt;\n' +
|
||||||
'future_salts#ae500895 salts:vector<future_salt> current:future_salt = FutureSalts;',
|
'future_salts#ae500895 salts:vector<future_salt> current:future_salt = FutureSalts;',
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(generateWriterCodeForTlEntries(entries, { includePrelude: false })).toEqual(
|
expect(generateWriterCodeForTlEntries(entries, { includePrelude: false })).toMatchSnapshot()
|
||||||
`
|
|
||||||
var m={
|
|
||||||
'future_salt':function(w,v){w.uint(155834844);w.bytes(h(v,'salt'));},
|
|
||||||
'future_salts':function(w,v){w.uint(2924480661);w.vector(m._bare[155834844],h(v,'salts'),1);m._bare[155834844](w,h(v,'current'));},
|
|
||||||
_bare:{
|
|
||||||
155834844:function(w=this,v){w.bytes(h(v,'salt'));},
|
|
||||||
},
|
|
||||||
}`.replace(/^\s+/gm, ''),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('generates code with raw flags for constructors with flags', () => {
|
it('throws when a bare type is not available', () => {
|
||||||
const entry = parseTlToEntries('test flags:# flags2:# = Test;')[0]
|
const entries = parseTlToEntries(
|
||||||
expect(generateWriterCodeForTlEntry(entry, { includeFlags: true })).toEqual(
|
'future_salts#ae500895 salts:vector<future_salt> current:FutureSalt = FutureSalts;',
|
||||||
`'${entry.name}':function(w,v){${[
|
|
||||||
`w.uint(${entry.id});`,
|
|
||||||
'var flags=v.flags;',
|
|
||||||
'w.uint(flags);',
|
|
||||||
'var flags2=v.flags2;',
|
|
||||||
'w.uint(flags2);',
|
|
||||||
].join('')}},`,
|
|
||||||
)
|
)
|
||||||
|
entries[0].arguments[1].typeModifiers = { isBareUnion: true }
|
||||||
|
|
||||||
|
expect(() => generateWriterCodeForTlEntries(entries, { includePrelude: false })).toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should include prelude by default', () => {
|
||||||
|
expect(generateWriterCodeForTlEntries([])).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should include static sizes calculations', () => {
|
||||||
|
const entries = parseTlToEntries(
|
||||||
|
'test1 foo:int bar:int = Test;\n' +
|
||||||
|
'test2 foo:int bar:double = Test;\n' +
|
||||||
|
'test3 foo:int bar:bytes = Test;\n', // should not be included
|
||||||
|
)
|
||||||
|
const code = generateWriterCodeForTlEntries(entries, { includeStaticSizes: true })
|
||||||
|
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -204,11 +204,7 @@ export function generateWriterCodeForTlEntries(entries: TlEntry[], params = DEFA
|
||||||
ret += '_bare:{\n'
|
ret += '_bare:{\n'
|
||||||
|
|
||||||
Object.keys(usedAsBareIds).forEach((id) => {
|
Object.keys(usedAsBareIds).forEach((id) => {
|
||||||
const entry = entries.find((e) => e.id === parseInt(id))
|
const entry = entries.find((e) => e.id === parseInt(id))!
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ret +=
|
ret +=
|
||||||
generateWriterCodeForTlEntry(entry, {
|
generateWriterCodeForTlEntry(entry, {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { parseFullTlSchema } from './schema.js'
|
||||||
import { TlEntryDiff, TlSchemaDiff } from './types.js'
|
import { TlEntryDiff, TlSchemaDiff } from './types.js'
|
||||||
|
|
||||||
describe('generateTlEntriesDifference', () => {
|
describe('generateTlEntriesDifference', () => {
|
||||||
const test = (tl: string[], expected: TlEntryDiff) => {
|
const test = (tl: string[], expected?: TlEntryDiff) => {
|
||||||
const e = parseTlToEntries(tl.join('\n'))
|
const e = parseTlToEntries(tl.join('\n'))
|
||||||
const res = generateTlEntriesDifference(e[0], e[1])
|
const res = generateTlEntriesDifference(e[0], e[1])
|
||||||
expect(res).toEqual(expected)
|
expect(res).toEqual(expected)
|
||||||
|
@ -99,281 +99,70 @@ describe('generateTlEntriesDifference', () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows args comments diff', () => {
|
||||||
|
test(['// @description a @foo Foo\ntest foo:int = Test;', '// @description a @foo Bar\ntest foo:int = Test;'], {
|
||||||
|
name: 'test',
|
||||||
|
arguments: {
|
||||||
|
added: [],
|
||||||
|
removed: [],
|
||||||
|
modified: [
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
comment: {
|
||||||
|
old: 'Foo',
|
||||||
|
new: 'Bar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws on incompatible entries', () => {
|
||||||
|
expect(() => test(['test1 = Test;', 'test2 = Test;'])).toThrow()
|
||||||
|
expect(() => test(['test = Test1;', 'test = Test2;'])).toThrow()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generateTlSchemasDifference', () => {
|
describe('generateTlSchemasDifference', () => {
|
||||||
const test = (tl1: string[], tl2: string[], expected: Partial<TlSchemaDiff>) => {
|
const test = (tl1: string[], tl2: string[]) => {
|
||||||
const a = parseFullTlSchema(parseTlToEntries(tl1.join('\n')))
|
const a = parseFullTlSchema(parseTlToEntries(tl1.join('\n')))
|
||||||
const b = parseFullTlSchema(parseTlToEntries(tl2.join('\n')))
|
const b = parseFullTlSchema(parseTlToEntries(tl2.join('\n')))
|
||||||
const res: Partial<TlSchemaDiff> = generateTlSchemasDifference(a, b)
|
const res: Partial<TlSchemaDiff> = generateTlSchemasDifference(a, b)
|
||||||
|
|
||||||
if (!('methods' in expected)) delete res.methods
|
expect(res).toMatchSnapshot()
|
||||||
if (!('classes' in expected)) delete res.classes
|
|
||||||
if (!('unions' in expected)) delete res.unions
|
|
||||||
|
|
||||||
expect(res).toEqual(expected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('shows added constructors', () => {
|
it('shows added constructors', () => {
|
||||||
test(['test1 = Test;'], ['test1 = Test;', 'test2 = Test;'], {
|
test(['test1 = Test;'], ['test1 = Test;', 'test2 = Test;'])
|
||||||
classes: {
|
|
||||||
added: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test2',
|
|
||||||
id: 3847402009,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removed: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows removed constructors', () => {
|
it('shows removed constructors', () => {
|
||||||
test(['test1 = Test;', 'test2 = Test;'], ['test1 = Test;'], {
|
test(['test1 = Test;', 'test2 = Test;'], ['test1 = Test;'])
|
||||||
classes: {
|
|
||||||
removed: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test2',
|
|
||||||
id: 3847402009,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
added: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows modified constructors', () => {
|
it('shows modified constructors', () => {
|
||||||
test(['test foo:int = Test;'], ['test foo:Foo = Test;'], {
|
test(['test foo:int = Test;'], ['test foo:Foo = Test;'])
|
||||||
classes: {
|
|
||||||
removed: [],
|
|
||||||
added: [],
|
|
||||||
modified: [
|
|
||||||
{
|
|
||||||
name: 'test',
|
|
||||||
arguments: {
|
|
||||||
added: [],
|
|
||||||
removed: [],
|
|
||||||
modified: [
|
|
||||||
{
|
|
||||||
name: 'foo',
|
|
||||||
type: {
|
|
||||||
old: 'int',
|
|
||||||
new: 'Foo',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
new: 3348640942,
|
|
||||||
old: 1331975629,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows removed unions', () => {
|
it('shows removed unions', () => {
|
||||||
test(['test foo:int = Test;', 'test1 = Test1;'], ['test foo:Foo = Test;'], {
|
test(['test foo:int = Test;', 'test1 = Test1;'], ['test foo:Foo = Test;'])
|
||||||
unions: {
|
|
||||||
removed: [
|
|
||||||
{
|
|
||||||
name: 'Test1',
|
|
||||||
classes: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 3739166976,
|
|
||||||
type: 'Test1',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
added: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows added unions', () => {
|
it('shows added unions', () => {
|
||||||
test(['test foo:int = Test;'], ['test foo:Foo = Test;', 'test1 = Test1;'], {
|
test(['test foo:int = Test;'], ['test foo:Foo = Test;', 'test1 = Test1;'])
|
||||||
unions: {
|
|
||||||
added: [
|
|
||||||
{
|
|
||||||
name: 'Test1',
|
|
||||||
classes: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 3739166976,
|
|
||||||
type: 'Test1',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removed: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows modified unions', () => {
|
it('shows modified unions', () => {
|
||||||
test(['test foo:int = Test;', 'test1 = Test;'], ['test foo:Foo = Test;', 'test2 = Test;'], {
|
test(['test foo:int = Test;', 'test1 = Test;'], ['test foo:Foo = Test;', 'test2 = Test;'])
|
||||||
unions: {
|
|
||||||
added: [],
|
|
||||||
removed: [],
|
|
||||||
modified: [
|
|
||||||
{
|
|
||||||
name: 'Test',
|
|
||||||
classes: {
|
|
||||||
added: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test2',
|
|
||||||
id: 3847402009,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removed: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 1809692154,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
added: [],
|
|
||||||
removed: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
test(['test foo:int = Test;', 'test1 = Test;'], ['test2 foo:Foo = Test;', 'test3 = Test;'], {
|
test(['test foo:int = Test;', 'test1 = Test;'], ['test2 foo:Foo = Test;', 'test3 = Test;'])
|
||||||
unions: {
|
|
||||||
added: [],
|
|
||||||
removed: [],
|
|
||||||
modified: [
|
|
||||||
{
|
|
||||||
name: 'Test',
|
|
||||||
classes: {
|
|
||||||
added: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test2',
|
|
||||||
id: 711487159,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'foo',
|
|
||||||
type: 'Foo',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test3',
|
|
||||||
id: 704164487,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removed: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test',
|
|
||||||
id: 1331975629,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
name: 'foo',
|
|
||||||
type: 'int',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 1809692154,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
added: [],
|
|
||||||
removed: [],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
test(['test = Test;', 'test1 = Test;'], ['test = Test1;', 'test1 = Test1;'], {
|
test(['test = Test;', 'test1 = Test;'], ['test = Test1;', 'test1 = Test1;'])
|
||||||
unions: {
|
})
|
||||||
added: [
|
|
||||||
{
|
it('shows modified methods', () => {
|
||||||
name: 'Test1',
|
test(['---functions---', 'test = Test;'], ['---functions---', 'test = Test2;'])
|
||||||
classes: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test',
|
|
||||||
id: 1997819349,
|
|
||||||
type: 'Test1',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 3739166976,
|
|
||||||
type: 'Test1',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
removed: [
|
|
||||||
{
|
|
||||||
name: 'Test',
|
|
||||||
classes: [
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test',
|
|
||||||
id: 471282454,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: 'class',
|
|
||||||
name: 'test1',
|
|
||||||
id: 1809692154,
|
|
||||||
type: 'Test',
|
|
||||||
arguments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
modified: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { computeConstructorIdFromEntry } from './ctor-id.js'
|
|
||||||
import { TlArgument, TlArgumentDiff, TlEntry, TlEntryDiff, TlFullSchema, TlSchemaDiff } from './types.js'
|
import { TlArgument, TlArgumentDiff, TlEntry, TlEntryDiff, TlFullSchema, TlSchemaDiff } from './types.js'
|
||||||
import { stringifyArgumentType } from './utils.js'
|
import { stringifyArgumentType } from './utils.js'
|
||||||
|
|
||||||
|
@ -25,11 +24,13 @@ export function generateTlEntriesDifference(a: TlEntry, b: TlEntry): TlEntryDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.id !== b.id) {
|
if (a.id !== b.id) {
|
||||||
let oldId = a.id
|
const oldId = a.id
|
||||||
let newId = b.id
|
const newId = b.id
|
||||||
|
|
||||||
if (oldId === 0) oldId = computeConstructorIdFromEntry(a)
|
/* c8 ignore next 3 */
|
||||||
if (newId === 0) newId = computeConstructorIdFromEntry(b)
|
if (oldId === 0 || newId === 0) {
|
||||||
|
throw new Error('Entry ID cannot be 0')
|
||||||
|
}
|
||||||
|
|
||||||
if (oldId !== newId) {
|
if (oldId !== newId) {
|
||||||
diff.id = {
|
diff.id = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
import { parseTlToEntries } from './parse.js'
|
import { parseTlToEntries } from './parse.js'
|
||||||
import { TlEntry } from './types.js'
|
import { TlEntry } from './types.js'
|
||||||
|
@ -441,4 +441,56 @@ users.getUsers id:Vector<InputUser> = Vector<User>;
|
||||||
{ prefix: 'mt_' },
|
{ prefix: 'mt_' },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('correctly handles ---types---', () => {
|
||||||
|
test('---functions---\n' + 'testFn = Test;\n' + '---types---\n' + 'testType = Test;', [
|
||||||
|
{
|
||||||
|
arguments: [],
|
||||||
|
id: 3671236185,
|
||||||
|
kind: 'method',
|
||||||
|
name: 'testFn',
|
||||||
|
type: 'Test',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: [],
|
||||||
|
id: 922588822,
|
||||||
|
kind: 'class',
|
||||||
|
name: 'testType',
|
||||||
|
type: 'Test',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
it('correctly throws errors if panicOnError is set', () => {
|
||||||
|
const invalidSchema = 'some invalid schema 🥴'
|
||||||
|
|
||||||
|
expect(() => parseTlToEntries(invalidSchema, { panicOnError: true })).toThrow(invalidSchema)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gracefully continues if onError set', () => {
|
||||||
|
const schema = 'some invalid line 🥴\ntest = Test;'
|
||||||
|
const onError = vi.fn()
|
||||||
|
|
||||||
|
const res = parseTlToEntries(schema, { onError })
|
||||||
|
|
||||||
|
expect(res).toEqual([
|
||||||
|
{
|
||||||
|
arguments: [],
|
||||||
|
id: 471282454,
|
||||||
|
kind: 'class',
|
||||||
|
name: 'test',
|
||||||
|
type: 'Test',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(onError).toHaveBeenCalledOnce()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('correctly handles incorrect %Foo usage', () => {
|
||||||
|
expect(() => parseTlToEntries('test foo:%Foo = Test;', { panicOnError: true })).toThrow('not found')
|
||||||
|
expect(() =>
|
||||||
|
parseTlToEntries('foo = Foo;\nbar = Foo;\ntest foo:%Foo = Test;', { panicOnError: true }),
|
||||||
|
).toThrow('more than one')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -66,6 +66,17 @@ export function parseTlToEntries(
|
||||||
let currentComment = ''
|
let currentComment = ''
|
||||||
const prefix = params.prefix ?? ''
|
const prefix = params.prefix ?? ''
|
||||||
|
|
||||||
|
const handleError = (err: Error, entryIdx: number) => {
|
||||||
|
if (params.panicOnError) {
|
||||||
|
throw err
|
||||||
|
} else if (params.onError) {
|
||||||
|
params.onError(err, '', entryIdx)
|
||||||
|
/* c8 ignore next 3 */
|
||||||
|
} else {
|
||||||
|
console.warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lines.forEach((line, idx) => {
|
lines.forEach((line, idx) => {
|
||||||
line = line.trim()
|
line = line.trim()
|
||||||
|
|
||||||
|
@ -108,15 +119,7 @@ export function parseTlToEntries(
|
||||||
const match = SINGLE_REGEX.exec(line)
|
const match = SINGLE_REGEX.exec(line)
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
const err = new Error(`Failed to parse line ${idx + 1}: ${line}`)
|
handleError(new Error(`Failed to parse line ${idx + 1}: ${line}`), idx + 1)
|
||||||
|
|
||||||
if (params.panicOnError) {
|
|
||||||
throw err
|
|
||||||
} else if (params.onError) {
|
|
||||||
params.onError(err, line, idx + 1)
|
|
||||||
} else {
|
|
||||||
console.warn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -214,6 +217,8 @@ export function parseTlToEntries(
|
||||||
params.onOrphanComment(currentComment)
|
params.onOrphanComment(currentComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (params.forIdComputation) return ret
|
||||||
|
|
||||||
// post-process:
|
// post-process:
|
||||||
// - add return type ctor id for methods
|
// - add return type ctor id for methods
|
||||||
// - find arguments where type is not a union and put corresponding modifiers
|
// - find arguments where type is not a union and put corresponding modifiers
|
||||||
|
@ -240,21 +245,19 @@ export function parseTlToEntries(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type in unions && arg.typeModifiers?.isBareUnion) {
|
if (arg.typeModifiers?.isBareUnion) {
|
||||||
if (unions[type].length !== 1) {
|
if (!(type in unions)) {
|
||||||
const err = new Error(
|
handleError(new Error(`Union ${type} not found (found in ${entry.name}#${arg.name})`), entryIdx)
|
||||||
`Union ${type} has more than one entry, cannot use it like %${type} (found in ${entry.name}#${arg.name})`,
|
} else if (unions[type].length !== 1) {
|
||||||
|
handleError(
|
||||||
|
new Error(
|
||||||
|
`Union ${type} has more than one entry, cannot use it like %${type} (found in ${entry.name}#${arg.name})`,
|
||||||
|
),
|
||||||
|
entryIdx,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
if (params.panicOnError) {
|
arg.typeModifiers.constructorId = unions[type][0].id
|
||||||
throw err
|
|
||||||
} else if (params.onError) {
|
|
||||||
params.onError(err, '', entryIdx)
|
|
||||||
} else {
|
|
||||||
console.warn(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
arg.typeModifiers.constructorId = unions[type][0].id
|
|
||||||
} else if (type in entries) {
|
} else if (type in entries) {
|
||||||
if (!arg.typeModifiers) arg.typeModifiers = {}
|
if (!arg.typeModifiers) arg.typeModifiers = {}
|
||||||
arg.typeModifiers.isBareType = true
|
arg.typeModifiers.isBareType = true
|
||||||
|
|
105
packages/tl-utils/src/utils.test.ts
Normal file
105
packages/tl-utils/src/utils.test.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import {
|
||||||
|
groupTlEntriesByNamespace,
|
||||||
|
parseArgumentType,
|
||||||
|
parseTdlibStyleComment,
|
||||||
|
splitNameToNamespace,
|
||||||
|
stringifyArgumentType,
|
||||||
|
} from './utils.js'
|
||||||
|
|
||||||
|
describe('splitNameToNamespace', () => {
|
||||||
|
it('should split names correctly', () => {
|
||||||
|
expect(splitNameToNamespace('foo.bar')).toEqual(['foo', 'bar'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null for namespace if there is none', () => {
|
||||||
|
expect(splitNameToNamespace('foo')).toEqual([null, 'foo'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('parseTdlibStyleComment', () => {
|
||||||
|
it('should parse comments correctly', () => {
|
||||||
|
expect(parseTdlibStyleComment('@foo Foo description @bar Bar description')).toEqual({
|
||||||
|
foo: 'Foo description',
|
||||||
|
bar: 'Bar description',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('groupTlEntriesByNamespace', () => {
|
||||||
|
it('should group entries correctly', () => {
|
||||||
|
expect(
|
||||||
|
groupTlEntriesByNamespace([
|
||||||
|
{
|
||||||
|
kind: 'class',
|
||||||
|
id: 0,
|
||||||
|
name: 'foo.bar',
|
||||||
|
type: 'FooBar',
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'class',
|
||||||
|
id: 0,
|
||||||
|
name: 'foo.baz',
|
||||||
|
type: 'FooBaz',
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'class',
|
||||||
|
id: 0,
|
||||||
|
name: 'bar',
|
||||||
|
type: 'Bar',
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('stringifyArgumentType', () => {
|
||||||
|
it('should keep type as is if there are no modifiers', () => {
|
||||||
|
expect(stringifyArgumentType('Foo')).toEqual('Foo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify bare types', () => {
|
||||||
|
expect(stringifyArgumentType('Foo', { isBareUnion: true })).toEqual('%Foo')
|
||||||
|
expect(stringifyArgumentType('foo', { isBareType: true })).toEqual('foo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify vectors', () => {
|
||||||
|
expect(stringifyArgumentType('Foo', { isVector: true })).toEqual('Vector<Foo>')
|
||||||
|
expect(stringifyArgumentType('Foo', { isVector: true, isBareUnion: true })).toEqual('Vector<%Foo>')
|
||||||
|
expect(stringifyArgumentType('Foo', { isBareVector: true })).toEqual('vector<Foo>')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should stringify predicates', () => {
|
||||||
|
expect(stringifyArgumentType('Foo', { predicate: 'foo' })).toEqual('foo?Foo')
|
||||||
|
expect(stringifyArgumentType('Foo', { predicate: 'foo', isVector: true })).toEqual('foo?Vector<Foo>')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('parseArgumentType', () => {
|
||||||
|
it('should parse bare types', () => {
|
||||||
|
expect(parseArgumentType('%Foo')).toEqual(['Foo', { isBareUnion: true }])
|
||||||
|
|
||||||
|
// not enough info to derive that
|
||||||
|
expect(parseArgumentType('foo')).toEqual([
|
||||||
|
'foo',
|
||||||
|
{
|
||||||
|
/* isBareType: true */
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse vectors', () => {
|
||||||
|
expect(parseArgumentType('Vector<Foo>')).toEqual(['Foo', { isVector: true }])
|
||||||
|
expect(parseArgumentType('Vector<%Foo>')).toEqual(['Foo', { isVector: true, isBareUnion: true }])
|
||||||
|
expect(parseArgumentType('vector<Foo>')).toEqual(['Foo', { isBareVector: true }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse predicates', () => {
|
||||||
|
expect(parseArgumentType('foo?Foo')).toEqual(['Foo', { predicate: 'foo' }])
|
||||||
|
expect(parseArgumentType('foo?Vector<Foo>')).toEqual(['Foo', { predicate: 'foo', isVector: true }])
|
||||||
|
})
|
||||||
|
})
|
|
@ -27,6 +27,8 @@ function getUint8Memory() {
|
||||||
return cachedUint8Memory
|
return cachedUint8Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* c8 ignore start */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the WASM blob synchronously (e.g. by passing a `WebAssembly.Module` instance)
|
* Init the WASM blob synchronously (e.g. by passing a `WebAssembly.Module` instance)
|
||||||
*/
|
*/
|
||||||
|
@ -43,6 +45,8 @@ export function initSync(module: SyncInitInput): void {
|
||||||
initCommon()
|
initCommon()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* c8 ignore end */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the WASM blob asynchronously (e.g. by passing a URL to the WASM file)
|
* Init the WASM blob asynchronously (e.g. by passing a URL to the WASM file)
|
||||||
*
|
*
|
||||||
|
@ -98,6 +102,7 @@ export function gunzip(bytes: Uint8Array): Uint8Array {
|
||||||
|
|
||||||
const ret = wasm.libdeflate_gzip_decompress(decompressor, inputPtr, bytes.length, outputPtr, size)
|
const ret = wasm.libdeflate_gzip_decompress(decompressor, inputPtr, bytes.length, outputPtr, size)
|
||||||
|
|
||||||
|
/* c8 ignore next 3 */
|
||||||
if (ret === -1) throw new Error('gunzip error -- bad data')
|
if (ret === -1) throw new Error('gunzip error -- bad data')
|
||||||
if (ret === -2) throw new Error('gunzip error -- short output')
|
if (ret === -2) throw new Error('gunzip error -- short output')
|
||||||
if (ret === -3) throw new Error('gunzip error -- short input') // should never happen
|
if (ret === -3) throw new Error('gunzip error -- short input') // should never happen
|
||||||
|
|
|
@ -13,6 +13,7 @@ export async function loadWasmBinary(input?: InitInput): Promise<WebAssembly.Ins
|
||||||
input = join(__dirname, '../lib/mtcute.wasm')
|
input = join(__dirname, '../lib/mtcute.wasm')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* c8 ignore next 3 */
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
throw new Error('Invalid input, for Node.js pass path to wasm blob')
|
throw new Error('Invalid input, for Node.js pass path to wasm blob')
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,5 +24,6 @@
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"**/node_modules",
|
"**/node_modules",
|
||||||
|
"**/__snapshots__",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue