Skip to content

Commit

Permalink
Support async transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
Haaxor1689 committed Dec 11, 2023
1 parent c8a334e commit 3d7b87e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 36 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import { n } from '@haaxor1689/nil';
const mySchema = n.string(14);

// Parse into Uint8Array
const buffer = mySchema.toBuffer('Hello world!!!');
const buffer = await mySchema.toBuffer('Hello world!!!');

// Prase from Uint8Array
const parsed = mySchema.fromBuffer(buffer);
const parsed = await mySchema.fromBuffer(buffer);
```

Creating an object schema
Expand All @@ -36,10 +36,10 @@ const User = n.object({
type User = n.output<typeof User>;
// { username: string; age: number; active: boolean; }

const buffer = User.toBuffer({ username: 'Jane', age: 30, active: true });
const buffer = await User.toBuffer({ username: 'Jane', age: 30, active: true });

// Prase from Uint8Array
User.fromBuffer(buffer);
await User.fromBuffer(buffer);
```

## Primitives
Expand Down Expand Up @@ -231,8 +231,8 @@ All Nil schemas contain these methods.

```ts
.transform(
afterDecode: (v: Input, ctx?: ParseContext) => Output,
beforeEncode: (v: Output, ctx?: ParseContext) => Input
afterDecode: (v: Input, ctx?: ParseContext) => Promise<Output>,
beforeEncode: (v: Output, ctx?: ParseContext) => Promise<Input>
)
```

Expand All @@ -256,7 +256,7 @@ const MySchema = n
type MySchema = z.output<typeof MySchema>;

// Resulting buffer will start with correct `itemCount` number
MySchema.toBuffer([1, 2, 3, 4]);
await MySchema.toBuffer([1, 2, 3, 4]);
```

You can also access the current context when creating transformations to reference other attributes from the parent type (if any). The easiest way to do this is by using the `resolvePath` helper function.
Expand All @@ -282,15 +282,15 @@ const MySchema = n.object({
### `.fromBuffer`

```ts
.fromBuffer(data: Uint8Array): Output
.fromBuffer(data: Uint8Array): Promise<Output>
```

Tries to parse given buffer into output type of used schema. Throws errors on failure.

### `.toBuffer`

```ts
.toBuffer(value: Output): Uint8Array
.toBuffer(value: Output): Promise<Uint8Array>
```

Tries to serialize a given object into a buffer.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@haaxor1689/nil",
"version": "0.1.2",
"version": "0.1.3",
"description": "TypeScript-first binary data parsing library with static type inference",
"author": "Maroš Beťko <[email protected]>",
"repository": {
Expand Down
8 changes: 4 additions & 4 deletions src/helpers/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ export type identity<T> = T;
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;

// FIXME: Improve types
type ObjectMapper<T, U> = (value: T, key: string) => U;
export const mapValues = <T, U>(
type ObjectMapper<T, U> = (value: T, key: string) => Promise<U>;
export const mapValues = async <T, U>(
obj: Record<string, T>,
mapper: ObjectMapper<T, U>
): Record<string, U> => {
): Promise<Record<string, U>> => {
const result: Record<string, U> = {};

for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = mapper(obj[key], key);
result[key] = await mapper(obj[key], key);
}
}

Expand Down
51 changes: 29 additions & 22 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,24 @@ export abstract class NilType<Output = any, Def = object, Input = Output> {
abstract _decode(data: DataView, ctx?: ParseContext): Input;
abstract _encode(data: DataView, value: Input, ctx?: ParseContext): void;

_afterDecode(value: Input, _ctx?: ParseContext): Output {
async _afterDecode(value: Input, _ctx?: ParseContext): Promise<Output> {
return value as never;
}

_beforeEncode(value: Output, _ctx?: ParseContext): Input {
async _beforeEncode(value: Output, _ctx?: ParseContext): Promise<Input> {
return value as never;
}

abstract size(value?: Input, ctx?: ParseContext): number;

fromBuffer(data: Uint8Array): Output {
async fromBuffer(data: Uint8Array): Promise<Output> {
const view = new DataView(data.buffer);
const val = this._decode(view);
return this._afterDecode(val);
return await this._afterDecode(val);
}

toBuffer(value: Output): Uint8Array {
const val = this._beforeEncode(value);
async toBuffer(value: Output): Promise<Uint8Array> {
const val = await this._beforeEncode(value);
const buffer = new Uint8Array(this.size(val));
const view = new DataView(buffer.buffer);
this._encode(view, val);
Expand Down Expand Up @@ -89,14 +89,14 @@ class NilEffects<
this._def.schema._encode(data, value, ctx);
}

_afterDecode(value: Input, ctx?: ParseContext): Output {
async _afterDecode(value: Input, ctx?: ParseContext): Promise<Output> {
const { schema } = this._def;
return this._def.transform[0](schema._afterDecode(value, ctx), ctx);
return this._def.transform[0](await schema._afterDecode(value, ctx), ctx);
}

_beforeEncode(value: Output, ctx?: ParseContext): Input {
async _beforeEncode(value: Output, ctx?: ParseContext): Promise<Input> {
const { schema } = this._def;
return schema._beforeEncode(this._def.transform[1](value, ctx), ctx);
return await schema._beforeEncode(this._def.transform[1](value, ctx), ctx);
}
}

Expand Down Expand Up @@ -365,30 +365,37 @@ class NilArray<T extends NilTypeAny> extends NilType<
});
}

_afterDecode(value: T['_input'][], ctx?: ParseContext) {
async _afterDecode(value: T['_input'][], ctx?: ParseContext) {
const { schema } = this._def;
return value.map((v, i) => {

const arr = [];
for (let i = 0; i < value.length; i++) {
const v = value[i];
const newCtx: ParseContext = {
// FIXME: Types
value: value as never,
path: [...(ctx?.path ?? []), i],
parent: ctx
};
return schema._afterDecode(v, newCtx);
});
arr.push(await schema._afterDecode(v, newCtx));
}
return arr;
}

_beforeEncode(value: T['_output'][], ctx?: ParseContext) {
async _beforeEncode(value: T['_output'][], ctx?: ParseContext) {
const { schema } = this._def;
return value.map((v, i) => {
const arr = [];
for (let i = 0; i < value.length; i++) {
const v = value[i];
const newCtx: ParseContext = {
// FIXME: Types
value: value as never,
path: [...(ctx?.path ?? []), i],
parent: ctx
};
return schema._beforeEncode(v, newCtx);
});
arr.push(await schema._beforeEncode(v, newCtx));
}
return arr;
}

#elementSize(value?: T['_input'][], ctx?: ParseContext) {
Expand Down Expand Up @@ -488,7 +495,7 @@ class NilObject<
parent: ctx
};
return v._afterDecode(value[k as keyof Input], newCtx);
}) as Output;
}) as Promise<Output>;
}

_beforeEncode(value: Output, ctx?: ParseContext) {
Expand All @@ -501,7 +508,7 @@ class NilObject<
parent: ctx
};
return v._beforeEncode(value[k as keyof Output], newCtx);
}) as Input;
}) as Promise<Input>;
}
}

Expand Down Expand Up @@ -574,7 +581,7 @@ export class NilEnum<
this._def.type._encode(data, value);
}

_afterDecode(value: T['_input']): O[number] {
async _afterDecode(value: T['_input']) {
const option = this._def.options[value];
if (option === undefined)
throw new Error(
Expand All @@ -583,7 +590,7 @@ export class NilEnum<
return option;
}

_beforeEncode(value: O[number]): T['_input'] {
async _beforeEncode(value: O[number]) {
const index = this._def.options.indexOf(value);
if (index === -1)
throw new Error(
Expand Down

0 comments on commit 3d7b87e

Please sign in to comment.