@homer0/jimple
Version:
An extended version of the Jimple lib, with extra features
109 lines (104 loc) • 4.39 kB
text/typescript
import { resourceFactory } from './resourceFactory.js';
import type { GenericFn, Resource } from './factories.types.js';
/**
* Generates a function to configure a collection of resources of an specified type. This
* function itself doesn't have logic, but it's just in charge of creating the constraint
* for the resource name and function.
*
* As all the other _"factory functions"_, this is meant to be used as a building block
* when extending the container.
*
* @returns A function that can be used to configure a collection of resources.
* @template ResourceName The literal type of the name the resources needs to have.
* @template ResourceFn The type of function the resources needs to have.
* @example
*
* type ActionFn = (c: Jimple) => void;
* const factory = resourcesCollectionFactory<'action', ActionFn>();
* const myActions = factory('action', 'fn')({ myActionA, myActionB });
*
*/
export const resourcesCollectionFactory =
<ResourceName extends string, ResourceFn extends GenericFn>() =>
/**
* Generates a function to create a collection of resources of an specified type. A
* collection is a dictionary of resources, and a resource itself, and when its
* "resource function" gets called, it calls the function of every resource (with the
* same args it recevied).
*
* @param name The resource name the items in the collection must have.
* @param key The key property the items in the collection must have.
* @param fn A custom function to process the items in the collection, when the
* collection function gets called.
* @returns A function that can be used to create a resources collection.
* @template Name The literal type of `name`, to be used in the return object.
* @template Key The literal type of `key`, to be used in the return object.
* @template ItemKey To capture the name of the resources in the collection.
* @template Items The kind of dictionary of resources the return function will
* expect.
* @template CustomFn The type of `fn`, restricted by the factory constraint.
* @example
*
* const myActions = factory('action', 'fn')({ myActionA, myActionB });
*
*/
<
Name extends string,
Key extends string,
ItemKey extends string,
Items extends Record<ItemKey, Resource<ResourceName, Key, ResourceFn>>,
CustomFn extends (items: Items, ...rest: Parameters<ResourceFn>) => void,
>(
name: Name,
key: Key,
fn?: CustomFn,
) => {
const collectionResourceFactory = resourceFactory<ResourceFn>();
/**
* The actual function that recevies the items and creates the collection.
*
* @param items A dictionary of resources for the collection.
* @returns A dictionary of the resources, that it's also a resource.
* @template ItemsParam The literal type of `items`, to be used in the return
* object.
* @throws If the dictionary contains the resource name or function key as keys.
* @throws If one of the items doesn't have the resource function.
*/
return <ItemsParam>(
items: ItemsParam & Items,
): typeof items & Resource<Name, Key, ResourceFn> => {
const invalidKeys: string[] = [name, key];
const itemsKeys = Object.keys(items) as Array<keyof Items>;
const invalidKey = itemsKeys.some((itemKey) =>
invalidKeys.includes(String(itemKey)),
);
if (invalidKey) {
throw new Error(
`No item on the collection can have the keys \`${name}\` nor \`${key}\``,
);
}
const invalidItem = itemsKeys.find(
(itemKey) => typeof items[itemKey]?.[key] !== 'function',
);
if (invalidItem) {
throw new Error(
`The item \`${String(
invalidItem,
)}\` is invalid: it doesn't have a \`${key}\` function`,
);
}
const useFn = fn
? (...args: Parameters<ResourceFn>) => fn(items, ...args)
: (...args: Parameters<ResourceFn>) => {
itemsKeys.forEach((itemK) => {
const item = items[itemK]!;
const itemFn = item[key];
itemFn(...args);
});
};
return {
...collectionResourceFactory(name, key, useFn as ResourceFn),
...items,
};
};
};