mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 01:39:21 +02:00
refactor(struct): simplify how StructInitType was computed
also allows multiple variable length `arraybuffer` fields to use same `lengthField`
This commit is contained in:
parent
f3a921857a
commit
5f0a6a8808
27 changed files with 188 additions and 141 deletions
|
@ -49,7 +49,7 @@ will install lerna locally and bootstrap all packages.
|
|||
|
||||
### Scripts
|
||||
|
||||
* `npm run build`: build `@yume-chan/event`, `@yume-chan/adb` and `@yume-chan/adb-backend-web` packages.
|
||||
* `npm run build:watch`: build and watch changes for `@yume-chan/event`, `@yume-chan/adb` and `@yume-chan/adb-backend-web` packages.
|
||||
* `npm run build`: build all npm packages.
|
||||
* `npm run build:watch`: build and watch changes for all npm packages.
|
||||
* `npm run start:demo`: start webpack-dev-server for the `demo` package.
|
||||
* `npm run build:demo`: build the `demo` package.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct, StructValueType } from "@yume-chan/struct";
|
||||
import Struct from "@yume-chan/struct";
|
||||
import { Adb } from '../adb';
|
||||
import { AdbBufferedStream } from '../stream';
|
||||
|
||||
|
@ -35,7 +35,7 @@ export const AdbFrameBufferV1 =
|
|||
.uint32('alpha_length')
|
||||
.uint8ClampedArray('data', { lengthField: 'size' });
|
||||
|
||||
export type AdbFrameBufferV1 = StructValueType<typeof AdbFrameBufferV1>;
|
||||
export type AdbFrameBufferV1 = typeof AdbFrameBufferV1['deserializedType'];
|
||||
|
||||
export const AdbFrameBufferV2 =
|
||||
new Struct({ littleEndian: true })
|
||||
|
@ -54,7 +54,7 @@ export const AdbFrameBufferV2 =
|
|||
.uint32('alpha_length')
|
||||
.uint8ClampedArray('data', { lengthField: 'size' });
|
||||
|
||||
export type AdbFrameBufferV2 = StructValueType<typeof AdbFrameBufferV2>;
|
||||
export type AdbFrameBufferV2 = typeof AdbFrameBufferV2['deserializedType'];
|
||||
|
||||
export async function framebuffer(adb: Adb) {
|
||||
const socket = await adb.createSocket('framebuffer:');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { AutoDisposable } from '@yume-chan/event';
|
||||
import { Struct } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbPacket } from '../packet';
|
||||
import { AdbIncomingSocketEventArgs, AdbPacketDispatcher, AdbSocket } from '../socket';
|
||||
import { AdbBufferedStream } from '../stream';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct, StructValueType } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
||||
import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';
|
||||
|
@ -11,7 +11,7 @@ export const AdbSyncEntryResponse =
|
|||
.string('name', { lengthField: 'nameLength' })
|
||||
.extra({ id: AdbSyncResponseId.Entry as const });
|
||||
|
||||
export type AdbSyncEntryResponse = StructValueType<typeof AdbSyncEntryResponse>;
|
||||
export type AdbSyncEntryResponse = typeof AdbSyncEntryResponse['deserializedType'];
|
||||
|
||||
const ResponseTypes = {
|
||||
[AdbSyncResponseId.Entry]: AdbSyncEntryResponse,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
||||
import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
import { chunkArrayLike, chunkAsyncIterable } from '../../utils';
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
|
||||
export enum AdbSyncRequestId {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct, StructDeserializationContext, StructValueType } from '@yume-chan/struct';
|
||||
import Struct, { StructDeserializationContext, StructLike, StructValueType } from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
|
||||
export enum AdbSyncResponseId {
|
||||
|
@ -16,7 +16,7 @@ export enum AdbSyncResponseId {
|
|||
// For example DONE responses for LIST requests are 16 bytes (same as DENT responses),
|
||||
// but DONE responses for STAT requests are 12 bytes (same as STAT responses)
|
||||
// So we need to know responses' size in advance.
|
||||
export class AdbSyncDoneResponse {
|
||||
export class AdbSyncDoneResponse implements StructLike<AdbSyncDoneResponse> {
|
||||
private length: number;
|
||||
|
||||
public readonly id = AdbSyncResponseId.Done;
|
||||
|
@ -39,7 +39,7 @@ export const AdbSyncFailResponse =
|
|||
throw new Error(object.message);
|
||||
});
|
||||
|
||||
export async function adbSyncReadResponse<T extends Record<string, { deserialize(context: StructDeserializationContext): Promise<any>; }>>(
|
||||
export async function adbSyncReadResponse<T extends Record<string, StructLike<any>>>(
|
||||
stream: AdbBufferedStream,
|
||||
types: T,
|
||||
): Promise<StructValueType<T[keyof T]>> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { placeholder, Struct, StructValueType } from '@yume-chan/struct';
|
||||
import Struct, { placeholder } from '@yume-chan/struct';
|
||||
import { AdbBufferedStream } from '../../stream';
|
||||
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
|
||||
import { adbSyncReadResponse, AdbSyncResponseId } from './response';
|
||||
|
@ -29,7 +29,7 @@ export const AdbSyncLstatResponse =
|
|||
}
|
||||
});
|
||||
|
||||
export type AdbSyncLstatResponse = StructValueType<typeof AdbSyncLstatResponse>;
|
||||
export type AdbSyncLstatResponse = typeof AdbSyncLstatResponse['deserializedType'];
|
||||
|
||||
export enum AdbSyncStatErrorCode {
|
||||
EACCES = 13,
|
||||
|
@ -78,7 +78,7 @@ export const AdbSyncStatResponse =
|
|||
}
|
||||
});
|
||||
|
||||
export type AdbSyncStatResponse = StructValueType<typeof AdbSyncStatResponse>;
|
||||
export type AdbSyncStatResponse = typeof AdbSyncStatResponse['deserializedType'];
|
||||
|
||||
const StatResponseType = {
|
||||
[AdbSyncResponseId.Stat]: AdbSyncStatResponse,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Struct, StructInitType, StructValueType } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AdbBackend } from './backend';
|
||||
import { BufferedStream } from './stream';
|
||||
|
||||
|
@ -25,9 +25,9 @@ const AdbPacketStruct =
|
|||
.fields(AdbPacketHeader)
|
||||
.arrayBuffer('payload', { lengthField: 'payloadLength' });
|
||||
|
||||
export type AdbPacket = StructValueType<typeof AdbPacketStruct>;
|
||||
export type AdbPacket = typeof AdbPacketStruct['deserializedType'];
|
||||
|
||||
export type AdbPacketInit = Omit<StructInitType<typeof AdbPacketStruct>, 'checksum' | 'magic'>;
|
||||
export type AdbPacketInit = Omit<typeof AdbPacketStruct['initType'], 'checksum' | 'magic'>;
|
||||
|
||||
export namespace AdbPacket {
|
||||
export function create(
|
||||
|
|
12
packages/demo/package-lock.json
generated
12
packages/demo/package-lock.json
generated
|
@ -769,7 +769,17 @@
|
|||
"@yume-chan/async-operation-manager": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@yume-chan/async-operation-manager/-/async-operation-manager-2.1.3.tgz",
|
||||
"integrity": "sha512-YUGrCXEADvaM1tJgQ0ek/a5eSg0ovdpL5ZhfXS6ZRnV3Xn0q5y/j/xzl+yBFFQ+nWclWh8of5f8Qm8y/y0YdQQ=="
|
||||
"integrity": "sha512-YUGrCXEADvaM1tJgQ0ek/a5eSg0ovdpL5ZhfXS6ZRnV3Xn0q5y/j/xzl+yBFFQ+nWclWh8of5f8Qm8y/y0YdQQ==",
|
||||
"requires": {
|
||||
"tslib": "2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.5",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Adb, AdbBufferedStream, AdbSocket, DataEventEmitter, EventQueue } from '@yume-chan/adb';
|
||||
import { PromiseResolver } from '@yume-chan/async-operation-manager';
|
||||
import { DisposableList, EventEmitter } from '@yume-chan/event';
|
||||
import { Struct, StructValueType } from '@yume-chan/struct';
|
||||
import Struct from '@yume-chan/struct';
|
||||
import { AndroidCodecLevel, AndroidCodecProfile } from './codec';
|
||||
import { AndroidKeyEventAction, AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpySimpleControlMessage } from './message';
|
||||
import { parse_sequence_parameter_set } from './sps';
|
||||
|
@ -159,7 +159,7 @@ const VideoPacket =
|
|||
|
||||
export const NoPts = BigInt(-1);
|
||||
|
||||
export type VideoPacket = StructValueType<typeof VideoPacket>;
|
||||
export type VideoPacket = typeof VideoPacket['deserializedType'];
|
||||
|
||||
const ClipboardMessage =
|
||||
new Struct()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { placeholder, Struct, StructInitType } from '@yume-chan/struct';
|
||||
import Struct, { placeholder } from '@yume-chan/struct';
|
||||
|
||||
export enum ScrcpyControlMessageType {
|
||||
InjectKeycode,
|
||||
|
@ -18,7 +18,7 @@ export const ScrcpySimpleControlMessage =
|
|||
new Struct()
|
||||
.uint8('type', placeholder<ScrcpyControlMessageType.BackOrScreenOn>());
|
||||
|
||||
export type ScrcpySimpleControlMessage = StructInitType<typeof ScrcpySimpleControlMessage>;
|
||||
export type ScrcpySimpleControlMessage = typeof ScrcpySimpleControlMessage['initType'];
|
||||
|
||||
export enum AndroidMotionEventAction {
|
||||
Down,
|
||||
|
@ -48,7 +48,7 @@ export const ScrcpyInjectTouchControlMessage =
|
|||
.uint16('pressure')
|
||||
.uint32('buttons');
|
||||
|
||||
export type ScrcpyInjectTouchControlMessage = StructInitType<typeof ScrcpyInjectTouchControlMessage>;
|
||||
export type ScrcpyInjectTouchControlMessage = typeof ScrcpyInjectTouchControlMessage['initType'];
|
||||
|
||||
export const ScrcpyInjectTextControlMessage =
|
||||
new Struct()
|
||||
|
@ -57,7 +57,7 @@ export const ScrcpyInjectTextControlMessage =
|
|||
.string('text', { lengthField: 'length' });
|
||||
|
||||
export type ScrcpyInjectTextControlMessage =
|
||||
StructInitType<typeof ScrcpyInjectTextControlMessage>;
|
||||
typeof ScrcpyInjectTextControlMessage['initType'];
|
||||
|
||||
export enum AndroidKeyEventAction {
|
||||
Down = 0,
|
||||
|
@ -106,7 +106,7 @@ export const ScrcpyInjectKeyCodeControlMessage =
|
|||
.uint32('metaState');
|
||||
|
||||
export type ScrcpyInjectKeyCodeControlMessage =
|
||||
StructInitType<typeof ScrcpyInjectKeyCodeControlMessage>;
|
||||
typeof ScrcpyInjectKeyCodeControlMessage['initType'];
|
||||
|
||||
export type ScrcpyControlMessage =
|
||||
ScrcpySimpleControlMessage |
|
||||
|
|
10
packages/event/.npmignore
Normal file
10
packages/event/.npmignore
Normal file
|
@ -0,0 +1,10 @@
|
|||
.github
|
||||
.vscode
|
||||
coverage
|
||||
src/**.spec.ts
|
||||
|
||||
jest.config.js
|
||||
pnpm-lock.yaml
|
||||
renovate.json
|
||||
tsconfig.json
|
||||
*.tsbuildinfo
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Simon Chan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
4
packages/event/jest.config.js
Normal file
4
packages/event/jest.config.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
};
|
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
"name": "@yume-chan/event",
|
||||
"private": true,
|
||||
"version": "0.0.4",
|
||||
"description": "Event/EventEmitter",
|
||||
"keywords": [
|
||||
"event"
|
||||
"event",
|
||||
"eventemitter",
|
||||
"typescript"
|
||||
],
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
@ -27,6 +28,7 @@
|
|||
"scripts": {
|
||||
"build": "rimraf {cjs,esm,dts,*.tsbuildinfo} && tsc -b tsconfig.esm.json tsconfig.cjs.json",
|
||||
"build:watch": "tsc -b -w tsconfig.esm.json tsconfig.cjs.json",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
|
|
21
packages/event/src/disposable.spec.ts
Normal file
21
packages/event/src/disposable.spec.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { AutoDisposable, Disposable } from './disposable';
|
||||
|
||||
describe('Event', () => {
|
||||
describe('AutoDisposable', () => {
|
||||
it('should dispose its dependencies', () => {
|
||||
const myDisposable = {
|
||||
dispose: jest.fn(),
|
||||
};
|
||||
class MyAutoDisposable extends AutoDisposable {
|
||||
constructor(disposable: Disposable) {
|
||||
super();
|
||||
this.addDisposable(disposable);
|
||||
}
|
||||
}
|
||||
|
||||
const myAutoDisposable = new MyAutoDisposable(myDisposable);
|
||||
myAutoDisposable.dispose();
|
||||
expect(myDisposable.dispose).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -581,7 +581,7 @@ All above built-in methods are alias of `field`. To add a field of a custom type
|
|||
### `FieldDefinition`
|
||||
|
||||
```ts
|
||||
abstract class FieldDefinition<TOptions = void, TValueType = unknown, TRemoveFields = never> {
|
||||
abstract class FieldDefinition<TOptions = void, TValueType = unknown, TOmitInit = never> {
|
||||
readonly options: TOptions;
|
||||
|
||||
constructor(options: TOptions);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
"description": "C-style structure serializer and deserializer.",
|
||||
"keywords": [
|
||||
"structure",
|
||||
"serialization",
|
||||
"deserialization",
|
||||
"typescript"
|
||||
],
|
||||
"license": "MIT",
|
||||
|
|
|
@ -16,12 +16,12 @@ type ValueOrPromise<T> = T | Promise<T>;
|
|||
*
|
||||
* @template TOptions TypeScript type of this definition's `options`.
|
||||
* @template TValueType TypeScript type of this field.
|
||||
* @template TRemoveInitFields Optional remove some field from the `TInit` type. Should be a union of string literal types.
|
||||
* @template TOmitInit Optionally remove some fields from the init type. Should be a union of string literal types.
|
||||
*/
|
||||
export abstract class FieldDefinition<
|
||||
TOptions = void,
|
||||
TValueType = unknown,
|
||||
TRemoveInitFields = never,
|
||||
TOmitInit = never,
|
||||
> {
|
||||
public readonly options: TOptions;
|
||||
|
||||
|
@ -33,9 +33,9 @@ export abstract class FieldDefinition<
|
|||
|
||||
/**
|
||||
* When `T` is a type initiated `FieldDefinition`,
|
||||
* use `T['removeInitFields']` to retrieve its `TRemoveInitFields` type parameter.
|
||||
* use `T['omitInitType']` to retrieve its `TOmitInit` type parameter.
|
||||
*/
|
||||
public readonly removeInitFields!: TRemoveInitFields;
|
||||
public readonly omitInitType!: TOmitInit;
|
||||
|
||||
public constructor(options: TOptions) {
|
||||
this.options = options;
|
||||
|
@ -55,7 +55,7 @@ export abstract class FieldDefinition<
|
|||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>>;
|
||||
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TOmitInit>>>;
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, creates a `FieldRuntimeValue` from a given `value`.
|
||||
|
@ -65,5 +65,5 @@ export abstract class FieldDefinition<
|
|||
context: StructSerializationContext,
|
||||
object: any,
|
||||
value: TValueType,
|
||||
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>;
|
||||
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TOmitInit>>;
|
||||
}
|
||||
|
|
9
packages/struct/src/index.spec.ts
Normal file
9
packages/struct/src/index.spec.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Struct from './index';
|
||||
|
||||
describe('Struct', () => {
|
||||
describe("Index", () => {
|
||||
it('should export default Struct', () => {
|
||||
expect(Struct).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
export * from './basic';
|
||||
export * from './struct';
|
||||
export { default as Struct } from './struct';
|
||||
export { Struct as default } from './struct';
|
||||
export * from './types';
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import { createRuntimeObject, FieldDefinition, FieldRuntimeValue, getRuntimeValue, setRuntimeValue, StructDefaultOptions, StructDeserializationContext, StructOptions, StructSerializationContext } from './basic';
|
||||
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, Evaluate, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, Identity, KeysOfType, NumberFieldDefinition, NumberFieldType, Overwrite, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
|
||||
|
||||
export interface StructLike<TValue> {
|
||||
deserialize(context: StructDeserializationContext): Promise<TValue>;
|
||||
}
|
||||
|
||||
export type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;
|
||||
|
||||
/**
|
||||
* Extract the value type of the specified `Struct`
|
||||
*
|
||||
* The lack of generic constraint is on purpose to allow `StructLike` types
|
||||
*/
|
||||
export type StructValueType<T> =
|
||||
T extends { deserialize(context: StructDeserializationContext): Promise<infer R>; } ? R : never;
|
||||
|
||||
/**
|
||||
* Extract the init type of the specified `Struct`
|
||||
*/
|
||||
export type StructInitType<T extends Struct<any, object, object, any>> =
|
||||
T extends { create(value: infer R, ...args: any): any; } ? Evaluate<R> : never;
|
||||
export type StructValueType<T extends StructLike<any>> =
|
||||
Awaited<ReturnType<T['deserialize']>>;
|
||||
|
||||
/**
|
||||
* Create a new `Struct` type with `TDescriptor` appended
|
||||
*/
|
||||
type AddFieldDescriptor<
|
||||
TValue extends object,
|
||||
TInit extends object,
|
||||
TFields extends object,
|
||||
TOmitInit extends string,
|
||||
TExtra extends object,
|
||||
TPostDeserialized,
|
||||
TFieldName extends PropertyKey,
|
||||
|
@ -28,10 +28,9 @@ type AddFieldDescriptor<
|
|||
Identity<Struct<
|
||||
// Merge two types
|
||||
// Evaluate immediately to optimize editor hover tooltip
|
||||
Evaluate<TValue & Record<TFieldName, TDefinition['valueType']>>,
|
||||
// There is no `Evaluate` here, because otherwise the type of a `Struct` with many fields
|
||||
// can become too complex for TypeScript to compute
|
||||
Evaluate<Omit<TInit, TDefinition['removeInitFields']> & Record<TFieldName, TDefinition['valueType']>>,
|
||||
Evaluate<TFields & Record<TFieldName, TDefinition['valueType']>>,
|
||||
// Merge two `TOmitInit
|
||||
TOmitInit | TDefinition['omitInitType'],
|
||||
TExtra,
|
||||
TPostDeserialized
|
||||
>>;
|
||||
|
@ -40,8 +39,8 @@ type AddFieldDescriptor<
|
|||
* Overload methods to add an array buffer like field
|
||||
*/
|
||||
interface ArrayBufferLikeFieldCreator<
|
||||
TValue extends object,
|
||||
TInit extends object,
|
||||
TFields extends object,
|
||||
TOmitInit extends string,
|
||||
TExtra extends object,
|
||||
TPostDeserialized
|
||||
> {
|
||||
|
@ -64,8 +63,8 @@ interface ArrayBufferLikeFieldCreator<
|
|||
options: FixedLengthArrayBufferLikeFieldOptions,
|
||||
typescriptType?: TTypeScriptType,
|
||||
): AddFieldDescriptor<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
TName,
|
||||
|
@ -81,7 +80,7 @@ interface ArrayBufferLikeFieldCreator<
|
|||
<
|
||||
TName extends PropertyKey,
|
||||
TType extends ArrayBufferLikeFieldType,
|
||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TInit>,
|
||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields>,
|
||||
TTypeScriptType = TType['valueType'],
|
||||
>(
|
||||
name: TName,
|
||||
|
@ -89,8 +88,8 @@ interface ArrayBufferLikeFieldCreator<
|
|||
options: TOptions,
|
||||
typescriptType?: TTypeScriptType,
|
||||
): AddFieldDescriptor<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
TName,
|
||||
|
@ -105,8 +104,8 @@ interface ArrayBufferLikeFieldCreator<
|
|||
* Similar to `ArrayBufferLikeFieldCreator`, but bind to a `ArrayBufferLikeFieldType`
|
||||
*/
|
||||
interface ArrayBufferTypeFieldDefinitionCreator<
|
||||
TValue extends object,
|
||||
TInit extends object,
|
||||
TFields extends object,
|
||||
TOmitInit extends string,
|
||||
TExtra extends object,
|
||||
TPostDeserialized,
|
||||
TType extends ArrayBufferLikeFieldType
|
||||
|
@ -119,8 +118,8 @@ interface ArrayBufferTypeFieldDefinitionCreator<
|
|||
options: FixedLengthArrayBufferLikeFieldOptions,
|
||||
typescriptType?: TTypeScriptType,
|
||||
): AddFieldDescriptor<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
TName,
|
||||
|
@ -132,16 +131,16 @@ interface ArrayBufferTypeFieldDefinitionCreator<
|
|||
|
||||
<
|
||||
TName extends PropertyKey,
|
||||
TLengthField extends KeysOfType<TInit, number | string>,
|
||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TInit, TLengthField>,
|
||||
TLengthField extends KeysOfType<TFields, number | string>,
|
||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields, TLengthField>,
|
||||
TTypeScriptType = TType['valueType'],
|
||||
>(
|
||||
name: TName,
|
||||
options: TOptions,
|
||||
typescriptType?: TTypeScriptType,
|
||||
): AddFieldDescriptor<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
TName,
|
||||
|
@ -155,18 +154,25 @@ interface ArrayBufferTypeFieldDefinitionCreator<
|
|||
export type StructPostDeserialized<TValue, TPostDeserialized> =
|
||||
(this: TValue, object: TValue) => TPostDeserialized;
|
||||
|
||||
export default class Struct<
|
||||
TValue extends object = {},
|
||||
TInit extends object = {},
|
||||
export type StructDeserializedType<TFields extends object, TExtra extends object, TPostDeserialized> =
|
||||
TPostDeserialized extends undefined ? Overwrite<TExtra, TFields> : TPostDeserialized;
|
||||
|
||||
export class Struct<
|
||||
TFields extends object = {},
|
||||
TOmitInit extends string = never,
|
||||
TExtra extends object = {},
|
||||
TPostDeserialized = undefined,
|
||||
> {
|
||||
public readonly valueType!: TValue;
|
||||
> implements StructLike<StructDeserializedType<TFields, TExtra, TPostDeserialized>>{
|
||||
public readonly fieldsType!: TFields;
|
||||
|
||||
public readonly initType!: TInit;
|
||||
public readonly omitInitType!: TOmitInit;
|
||||
|
||||
public readonly extraType!: TExtra;
|
||||
|
||||
public readonly initType!: Evaluate<Omit<TFields, TOmitInit>>;
|
||||
|
||||
public readonly deserializedType!: StructDeserializedType<TFields, TExtra, TPostDeserialized>;
|
||||
|
||||
public readonly options: Readonly<StructOptions>;
|
||||
|
||||
private _size = 0;
|
||||
|
@ -179,7 +185,7 @@ export default class Struct<
|
|||
|
||||
private _extra: PropertyDescriptorMap = {};
|
||||
|
||||
private _postDeserialized?: StructPostDeserialized<TValue, any>;
|
||||
private _postDeserialized?: StructPostDeserialized<any, any>;
|
||||
|
||||
public constructor(options?: Partial<StructOptions>) {
|
||||
this.options = { ...StructDefaultOptions, ...options };
|
||||
|
@ -195,8 +201,8 @@ export default class Struct<
|
|||
name: TName,
|
||||
definition: TDefinition,
|
||||
): AddFieldDescriptor<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
TName,
|
||||
|
@ -217,8 +223,8 @@ export default class Struct<
|
|||
public fields<TOther extends Struct<any, any, any, any>>(
|
||||
other: TOther
|
||||
): Struct<
|
||||
TValue & TOther['valueType'],
|
||||
TInit & TOther['initType'],
|
||||
TFields & TOther['fieldsType'],
|
||||
TOmitInit | TOther['omitInitType'],
|
||||
TExtra & TOther['extraType'],
|
||||
TPostDeserialized
|
||||
> {
|
||||
|
@ -385,27 +391,32 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
private arrayBufferLike: ArrayBufferLikeFieldCreator<TValue, TInit, TExtra, TPostDeserialized> = (
|
||||
private arrayBufferLike: ArrayBufferLikeFieldCreator<
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized
|
||||
> = (
|
||||
name: PropertyKey,
|
||||
type: ArrayBufferLikeFieldType,
|
||||
options: FixedLengthArrayBufferLikeFieldOptions | VariableLengthArrayBufferLikeFieldOptions
|
||||
): any => {
|
||||
if ('length' in options) {
|
||||
return this.field(
|
||||
name,
|
||||
new FixedLengthArrayBufferLikeFieldDefinition(type, options),
|
||||
);
|
||||
} else {
|
||||
return this.field(
|
||||
name,
|
||||
new VariableLengthArrayBufferLikeFieldDefinition(type, options),
|
||||
);
|
||||
}
|
||||
};
|
||||
if ('length' in options) {
|
||||
return this.field(
|
||||
name,
|
||||
new FixedLengthArrayBufferLikeFieldDefinition(type, options),
|
||||
);
|
||||
} else {
|
||||
return this.field(
|
||||
name,
|
||||
new VariableLengthArrayBufferLikeFieldDefinition(type, options),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public arrayBuffer: ArrayBufferTypeFieldDefinitionCreator<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
ArrayBufferFieldType
|
||||
|
@ -417,8 +428,8 @@ export default class Struct<
|
|||
};
|
||||
|
||||
public uint8ClampedArray: ArrayBufferTypeFieldDefinitionCreator<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
Uint8ClampedArrayFieldType
|
||||
|
@ -430,8 +441,8 @@ export default class Struct<
|
|||
};
|
||||
|
||||
public string: ArrayBufferTypeFieldDefinitionCreator<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
TExtra,
|
||||
TPostDeserialized,
|
||||
StringFieldType
|
||||
|
@ -456,14 +467,14 @@ export default class Struct<
|
|||
// This trick disallows any keys that are already in `TValue`
|
||||
Exclude<
|
||||
keyof T,
|
||||
Exclude<keyof T, keyof TValue>
|
||||
Exclude<keyof T, keyof TFields>
|
||||
>,
|
||||
never
|
||||
>>(
|
||||
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TValue>>
|
||||
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TFields>>
|
||||
): Struct<
|
||||
TValue,
|
||||
TInit,
|
||||
TFields,
|
||||
TOmitInit,
|
||||
Overwrite<TExtra, T>,
|
||||
TPostDeserialized
|
||||
> {
|
||||
|
@ -478,8 +489,8 @@ export default class Struct<
|
|||
* will also change the return type of `deserialize` to `never`.
|
||||
*/
|
||||
public postDeserialize(
|
||||
callback: StructPostDeserialized<TValue, never>
|
||||
): Struct<TValue, TInit, TExtra, never>;
|
||||
callback: StructPostDeserialized<TFields, never>
|
||||
): Struct<TFields, TOmitInit, TExtra, never>;
|
||||
/**
|
||||
* Registers (or replaces) a custom callback to be run after deserialized.
|
||||
*
|
||||
|
@ -487,8 +498,8 @@ export default class Struct<
|
|||
* (or doesn't modify it at all), so `deserialize` will still return the result object.
|
||||
*/
|
||||
public postDeserialize(
|
||||
callback?: StructPostDeserialized<TValue, void>
|
||||
): Struct<TValue, TInit, TExtra, undefined>;
|
||||
callback?: StructPostDeserialized<TFields, void>
|
||||
): Struct<TFields, TOmitInit, TExtra, undefined>;
|
||||
/**
|
||||
* Registers (or replaces) a custom callback to be run after deserialized.
|
||||
*
|
||||
|
@ -496,10 +507,10 @@ export default class Struct<
|
|||
* will `deserialize` to return that object instead.
|
||||
*/
|
||||
public postDeserialize<TPostSerialize>(
|
||||
callback?: StructPostDeserialized<TValue, TPostSerialize>
|
||||
): Struct<TValue, TInit, TExtra, TPostSerialize>;
|
||||
callback?: StructPostDeserialized<TFields, TPostSerialize>
|
||||
): Struct<TFields, TOmitInit, TExtra, TPostSerialize>;
|
||||
public postDeserialize(
|
||||
callback?: StructPostDeserialized<TValue, any>
|
||||
callback?: StructPostDeserialized<TFields, any>
|
||||
) {
|
||||
this._postDeserialized = callback;
|
||||
return this as any;
|
||||
|
@ -511,7 +522,7 @@ export default class Struct<
|
|||
return object;
|
||||
}
|
||||
|
||||
public create(init: TInit, context: StructSerializationContext): Overwrite<TExtra, TValue> {
|
||||
public create(init: Evaluate<Omit<TFields, TOmitInit>>, context: StructSerializationContext): Overwrite<TExtra, TFields> {
|
||||
const object = this.initializeObject();
|
||||
|
||||
for (const [name, definition] of this._fields) {
|
||||
|
@ -524,7 +535,7 @@ export default class Struct<
|
|||
|
||||
public async deserialize(
|
||||
context: StructDeserializationContext
|
||||
): Promise<TPostDeserialized extends undefined ? Overwrite<TExtra, TValue> : TPostDeserialized> {
|
||||
): Promise<StructDeserializedType<TFields, TExtra, TPostDeserialized>> {
|
||||
const object = this.initializeObject();
|
||||
|
||||
for (const [name, definition] of this._fields) {
|
||||
|
@ -533,7 +544,7 @@ export default class Struct<
|
|||
}
|
||||
|
||||
if (this._postDeserialized) {
|
||||
const result = this._postDeserialized.call(object as TValue, object as TValue);
|
||||
const result = this._postDeserialized.call(object as TFields, object as TFields);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
@ -542,7 +553,7 @@ export default class Struct<
|
|||
return object as any;
|
||||
}
|
||||
|
||||
public serialize(init: TInit, context: StructSerializationContext): ArrayBuffer {
|
||||
public serialize(init: Evaluate<Omit<TFields, TOmitInit>>, context: StructSerializationContext): ArrayBuffer {
|
||||
const object = this.create(init, context) as any;
|
||||
|
||||
let structSize = 0;
|
||||
|
|
|
@ -102,11 +102,11 @@ const EmptyArrayBuffer = new ArrayBuffer(0);
|
|||
export abstract class ArrayBufferLikeFieldDefinition<
|
||||
TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType,
|
||||
TOptions = void,
|
||||
TRemoveInitFields = never,
|
||||
TOmitInit = never,
|
||||
> extends FieldDefinition<
|
||||
TOptions,
|
||||
TType['valueType'],
|
||||
TRemoveInitFields
|
||||
TOmitInit
|
||||
>{
|
||||
public readonly type: TType;
|
||||
|
||||
|
@ -123,7 +123,7 @@ export abstract class ArrayBufferLikeFieldDefinition<
|
|||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): Promise<ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>> {
|
||||
): Promise<ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TOmitInit>>> {
|
||||
const size = this.getDeserializeSize(object);
|
||||
|
||||
let arrayBuffer: ArrayBuffer;
|
||||
|
@ -147,7 +147,7 @@ export abstract class ArrayBufferLikeFieldDefinition<
|
|||
context: StructSerializationContext,
|
||||
object: any,
|
||||
value: TType['valueType'],
|
||||
): ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>;
|
||||
): ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TOmitInit>>;
|
||||
}
|
||||
|
||||
export class ArrayBufferLikeFieldRuntimeValue<
|
||||
|
|
|
@ -47,8 +47,7 @@ export class NumberFieldDefinition<
|
|||
TTypeScriptType = TType['valueType'],
|
||||
> extends FieldDefinition<
|
||||
void,
|
||||
TTypeScriptType,
|
||||
never
|
||||
TTypeScriptType
|
||||
> {
|
||||
public readonly type: TType;
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldRuntimeValue, Array
|
|||
import { KeysOfType } from './utils';
|
||||
|
||||
export interface VariableLengthArrayBufferLikeFieldOptions<
|
||||
TInit = object,
|
||||
TLengthField extends KeysOfType<TInit, number | string> = any,
|
||||
TFields = object,
|
||||
TLengthField extends KeysOfType<TFields, number | string> = any,
|
||||
> {
|
||||
lengthField: TLengthField;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue