Skip to content

Commit

Permalink
feat: toObject(message, schema)
Browse files Browse the repository at this point in the history
  • Loading branch information
kpietraszko committed Dec 28, 2024
1 parent e659054 commit 01bc198
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 11 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
![NPM Version](https://img.shields.io/npm/v/schemind)

# schemind
Read and write to messages serialized as arrays (indexed keys) by defining a schema.
Read and write to messages serialized as arrays (aka indexed keys) by defining a schema.

### What?
TODO
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { withIndex, get, set } from './indexedKeysSchema'
export { withIndex, get, set, toObject } from './indexedKeysSchema'
29 changes: 28 additions & 1 deletion src/indexedKeysSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type ReturnedSchemaNode<TField, TNestedSchema> = TNestedSchema extends undefined
: TNestedSchema;

export function withIndex<const TIndex extends number>(index: NonNegativeInteger<TIndex>) {
return <const TField = undefined, const TNestedSchema extends IndexedKeysMessageSchema<TNestedSchema> | undefined = undefined>(nestedSchema?: TNestedSchema)
return <const TField = undefined, TNestedSchema extends IndexedKeysMessageSchema<TNestedSchema> | undefined = undefined>(nestedSchema?: TNestedSchema)
: ReturnedSchemaNode<TField, TNestedSchema> => {
if (nestedSchema) {
addIndexToPathsRecursively(nestedSchema, index);
Expand Down Expand Up @@ -80,3 +80,30 @@ export function set<const TField>(targetMessage: unknown[], schemaField: SchemaL
const lastIndexInPath = indexesPathReversed[0];
currentSlice[lastIndexInPath] = value;
}

type PlainObjectOfSchema<TSchema> = TSchema extends IndexedKeysMessageSchema<unknown> ? {
[K in keyof TSchema]: TSchema[K] extends SchemaLeaf<infer TField>
? TField
: PlainObjectOfSchema<TSchema[K]>;
}
: never;

export function toObject<TSchema extends IndexedKeysMessageSchema<TSchemaInner>, TSchemaInner>(
message: readonly unknown[],
schema: TSchema): PlainObjectOfSchema<TSchema> {

const object: Partial<PlainObjectOfSchema<TSchema>> = {};
for (const [fieldName, nestedSchemaNode] of Object.entries(schema)) {
const nestedNode = nestedSchemaNode as IndexedKeysMessageSchema<unknown> | SchemaLeaf<unknown>;
let valueToSet = undefined;
if (isSchemaLeaf(nestedNode)) {
valueToSet = get(message, nestedNode);
} else {
valueToSet = toObject(message, nestedNode);
}

object[fieldName as keyof PlainObjectOfSchema<TSchema>] = valueToSet as any;
}

return object as PlainObjectOfSchema<TSchema>;
}
69 changes: 61 additions & 8 deletions test/indexedKeysSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { describe, expect, expectTypeOf, it } from "vitest";
import { withIndex as i, get, set } from "../src";
import { describe, it, expect, expectTypeOf } from "vitest";
import { withIndex as i, get, set, toObject } from "../src/index";

describe("get", () => {
it("should return value from the index - and of type - specified by the schema", () => {
const schema = createSchema();
const schema = createTestSchema();
const someDate = new Date();
const message = [420, 69, "nice", true, [
1234567891234567,
Expand Down Expand Up @@ -55,8 +55,9 @@ describe("get", () => {

describe("set", () => {
it("should place values at indexes specified by the schema", () => {
const schema = createSchema();
const schema = createTestSchema();
const someDate = new Date();

const newMessage = [] as unknown[];

set(newMessage, schema.someNumber, 420);
Expand All @@ -68,7 +69,7 @@ describe("set", () => {
set(newMessage, schema.nestedThing.evenMoreNestedThing.moreNestedBool, false);
set(newMessage, schema.nestedThing.evenMoreNestedThing.moreNestedNumber, 2138);
set(newMessage, schema.nestedThing.evenMoreNestedThing.moreNestedArray, [2, 3, 5, 8]);

const expectedMessage = [420, 69, "nice", true, [
1234567891234567,
someDate,
Expand All @@ -78,13 +79,65 @@ describe("set", () => {
[2, 3, 5, 8]
]
]] as unknown[];

// expectTypeOf(newMessage).toEqualTypeOf(expectedMessage);

expect(newMessage).to.deep.equal(expectedMessage);
});
});

function createSchema() {
describe("toObject", () => {
it("should convert message to properly typed plain object", () => {
const schema = createTestSchema();
const someDate = new Date();
const message = [420, 69, "nice", true, [
1234567891234567,
someDate,
[
2138,
false,
[2, 3, 5, 8]
]
]] as unknown[];

type ExpectedObjectType = {
someNumber: number,
anotherNumber: number,
nestedThing: {
someNestedDate: Date,
evenMoreNestedThing: {
moreNestedNumber: number,
moreNestedBool: boolean,
moreNestedArray: number[]
},
someNestedNumber: number
},
someString: string,
someBool: boolean
};

const expectedObject = {
someNumber: 420,
anotherNumber: 69,
nestedThing: {
someNestedDate: someDate,
evenMoreNestedThing: {
moreNestedNumber: 2138,
moreNestedBool: false,
moreNestedArray: [2, 3, 5, 8]
},
someNestedNumber: 1234567891234567
},
someString: "nice",
someBool: true
}

const result = toObject(message, schema);

expectTypeOf(result).toEqualTypeOf<ExpectedObjectType>();
expect(result).to.deep.equal(expectedObject);
});
});

export function createTestSchema() {
return {
someNumber: i(0)<number>(),
anotherNumber: i(1)<number>(),
Expand Down

0 comments on commit 01bc198

Please sign in to comment.