Skip to content

Commit

Permalink
refactor: moved public (exported) functions to separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
kpietraszko committed Jan 1, 2025
1 parent a390d3c commit 6776454
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 88 deletions.
61 changes: 61 additions & 0 deletions src/convenient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
type IndexedKeysMessageSchema, type Invalid, isSchemaLeaf, type SchemaLeaf,
type ValidIndexedKeysMessageSchema,
type ValidSchemaLeaf
} from "./indexedKeysSchema";
import { get, set } from "./raw";

type PlainObjectOfSchema<TSchema> = TSchema extends ValidIndexedKeysMessageSchema<unknown> ? {
[K in keyof TSchema]: TSchema[K] extends ValidSchemaLeaf<infer TField>
? TField
: TSchema[K] extends SchemaLeaf<unknown>
? Invalid<"Schema needs to be validated before you use it!">
: PlainObjectOfSchema<TSchema[K]>;
}
: never;

export function toPlainObject<TSchema extends ValidIndexedKeysMessageSchema<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 ValidIndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>;
let valueToSet = undefined;
if (isSchemaLeaf(nestedNode)) {
valueToSet = get(message, nestedNode);
} else {
valueToSet = toPlainObject(message, nestedNode);
}

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

return object as PlainObjectOfSchema<TSchema>;
}

export function toIndexedKeysMessage<TSchema extends ValidIndexedKeysMessageSchema<TSchemaInner>, TSchemaInner>(
plainObject: PlainObjectOfSchema<TSchema>,
schema: TSchema): unknown[] {

const message: unknown[] = [];
populateIndexedKeysMessage(message, plainObject, schema);
return message;
}

function populateIndexedKeysMessage<TSchema extends ValidIndexedKeysMessageSchema<TSchemaInner>, TSchemaInner>(
messageToPopulate: unknown[],
plainObject: PlainObjectOfSchema<TSchema>,
schema: TSchema) {

for (const [fieldName, nestedSchemaNode] of Object.entries(schema)) {
const nestedNode = nestedSchemaNode as IndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>;
const leafValueOrSubObject = plainObject[fieldName as keyof PlainObjectOfSchema<TSchema>];
if (isSchemaLeaf(nestedNode)) {
set(messageToPopulate, nestedNode, leafValueOrSubObject);
} else {
populateIndexedKeysMessage(messageToPopulate, leafValueOrSubObject as PlainObjectOfSchema<TSchema>, nestedNode)
}
}
}
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { validateSchema, withIndex, get, set, toPlainObject, toIndexedKeysMessage, InvalidSchemaError } from './indexedKeysSchema'
export { validateSchema, withIndex, InvalidSchemaError } from './indexedKeysSchema'
export { get, set } from "./raw";
export { toPlainObject, toIndexedKeysMessage } from "./convenient";

export type { ValidIndexedKeysMessageSchema } from './indexedKeysSchema'
92 changes: 5 additions & 87 deletions src/indexedKeysSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ const isSchemaLeafTag = Symbol("isSchemaLeaf");
const isValidSchemaLeaf = Symbol("isValidSchemaLeaf");

type IndexesPath = number[];
type SchemaLeaf<TField> = {
export type SchemaLeaf<TField> = {
indexesPathReversed: IndexesPath,
fieldType: TField,
[isSchemaLeafTag]: true
};

type ValidSchemaLeaf<TField> = SchemaLeaf<TField> & { [isValidSchemaLeaf]: true };
export type ValidSchemaLeaf<TField> = SchemaLeaf<TField> & { [isValidSchemaLeaf]: true };

export type IndexedKeysMessageSchema<TSchema> = {
[K in keyof TSchema]: TSchema[K] extends SchemaLeaf<infer TField>
Expand All @@ -25,7 +25,7 @@ type ToValidIndexedKeysMessageSchema<TSchema> = {
};

const invalid = Symbol('invalid')
type Invalid<T extends string> = { [invalid]: T }
export type Invalid<T extends string> = { [invalid]: T }

export type ValidIndexedKeysMessageSchema<TSchema> = {
[K in keyof TSchema]: TSchema[K] extends ValidSchemaLeaf<infer TField>
Expand Down Expand Up @@ -103,8 +103,8 @@ export function withIndex<const TIndex extends number>(index: NonNegativeInteger
}

// intentionally not validating that it has the "isValidSchemaLeaf" symbol property, because it actually doesn't - it's just a type trick
function isSchemaLeaf(value: IndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>): value is ValidSchemaLeaf<unknown>;
function isSchemaLeaf(value: IndexedKeysMessageSchema<unknown> | SchemaLeaf<unknown>): value is SchemaLeaf<unknown> {
export function isSchemaLeaf(value: IndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>): value is ValidSchemaLeaf<unknown>;
export function isSchemaLeaf(value: IndexedKeysMessageSchema<unknown> | SchemaLeaf<unknown>): value is SchemaLeaf<unknown> {
return Object.hasOwn(value, isSchemaLeafTag);
}

Expand All @@ -122,85 +122,3 @@ function addIndexToPathsRecursively(
}
}

export function get<const TField>(message: readonly unknown[], schemaField: ValidSchemaLeaf<TField>) {
const indexesPathReversed = schemaField.indexesPathReversed;
let currentSlice: readonly unknown[] = message;

for (let pathIndex = schemaField.indexesPathReversed.length - 1; pathIndex >= 1; pathIndex--) {
currentSlice = currentSlice[indexesPathReversed[pathIndex]] as readonly unknown[];
}

const lastIndexInPath = indexesPathReversed[0];
return currentSlice[lastIndexInPath] as TField;
}

export function set<const TField>(targetMessage: unknown[], schemaField: ValidSchemaLeaf<TField>, value: TField) {
const indexesPathReversed = schemaField.indexesPathReversed;
let currentSlice: unknown[] = targetMessage;

for (let pathIndex = schemaField.indexesPathReversed.length - 1; pathIndex >= 1; pathIndex--) {
if (currentSlice[indexesPathReversed[pathIndex]] === undefined) {
currentSlice[indexesPathReversed[pathIndex]] = [];
}

currentSlice = currentSlice[indexesPathReversed[pathIndex]] as unknown[];
}

const lastIndexInPath = indexesPathReversed[0];
currentSlice[lastIndexInPath] = value;
}

type PlainObjectOfSchema<TSchema> = TSchema extends ValidIndexedKeysMessageSchema<unknown> ? {
[K in keyof TSchema]: TSchema[K] extends ValidSchemaLeaf<infer TField>
? TField
: TSchema[K] extends SchemaLeaf<unknown>
? Invalid<"Schema needs to be validated before you use it!">
: PlainObjectOfSchema<TSchema[K]>;
}
: never;

export function toPlainObject<TSchema extends ValidIndexedKeysMessageSchema<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 ValidIndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>;
let valueToSet = undefined;
if (isSchemaLeaf(nestedNode)) {
valueToSet = get(message, nestedNode);
} else {
valueToSet = toPlainObject(message, nestedNode);
}

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

return object as PlainObjectOfSchema<TSchema>;
}

export function toIndexedKeysMessage<TSchema extends ValidIndexedKeysMessageSchema<TSchemaInner>, TSchemaInner>(
plainObject: PlainObjectOfSchema<TSchema>,
schema: TSchema): unknown[] {

const message: unknown[] = [];
populateIndexedKeysMessage(message, plainObject, schema);
return message;
}

function populateIndexedKeysMessage<TSchema extends ValidIndexedKeysMessageSchema<TSchemaInner>, TSchemaInner>(
messageToPopulate: unknown[],
plainObject: PlainObjectOfSchema<TSchema>,
schema: TSchema) {

for (const [fieldName, nestedSchemaNode] of Object.entries(schema)) {
const nestedNode = nestedSchemaNode as IndexedKeysMessageSchema<unknown> | ValidSchemaLeaf<unknown>;
const leafValueOrSubObject = plainObject[fieldName as keyof PlainObjectOfSchema<TSchema>];
if (isSchemaLeaf(nestedNode)) {
set(messageToPopulate, nestedNode, leafValueOrSubObject);
} else {
populateIndexedKeysMessage(messageToPopulate, leafValueOrSubObject as PlainObjectOfSchema<TSchema>, nestedNode)
}
}
}
29 changes: 29 additions & 0 deletions src/raw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ValidSchemaLeaf } from "./indexedKeysSchema";

export function get<const TField>(message: readonly unknown[], schemaField: ValidSchemaLeaf<TField>) {
const indexesPathReversed = schemaField.indexesPathReversed;
let currentSlice: readonly unknown[] = message;

for (let pathIndex = schemaField.indexesPathReversed.length - 1; pathIndex >= 1; pathIndex--) {
currentSlice = currentSlice[indexesPathReversed[pathIndex]] as readonly unknown[];
}

const lastIndexInPath = indexesPathReversed[0];
return currentSlice[lastIndexInPath] as TField;
}

export function set<const TField>(targetMessage: unknown[], schemaField: ValidSchemaLeaf<TField>, value: TField) {
const indexesPathReversed = schemaField.indexesPathReversed;
let currentSlice: unknown[] = targetMessage;

for (let pathIndex = schemaField.indexesPathReversed.length - 1; pathIndex >= 1; pathIndex--) {
if (currentSlice[indexesPathReversed[pathIndex]] === undefined) {
currentSlice[indexesPathReversed[pathIndex]] = [];
}

currentSlice = currentSlice[indexesPathReversed[pathIndex]] as unknown[];
}

const lastIndexInPath = indexesPathReversed[0];
currentSlice[lastIndexInPath] = value;
}

0 comments on commit 6776454

Please sign in to comment.