2024-09-29 13:48:06 -07:00
|
|
|
import type {AnyFunc, CommonSignature, GenSigs} from '@/interfaces/type'
|
|
|
|
|
|
|
|
|
|
// A base type that roughly describes the dependencies of a single factory
|
|
|
|
|
// for implementations of one operation. It is an object whose keys are the
|
|
|
|
|
// identifiers are dependencies, and whose values describe that dependency.
|
|
|
|
|
// In the value for a given key, the 'is' property gives the name of the
|
|
|
|
|
// operation that dependency should be an instance of, defaulting to the key
|
|
|
|
|
// itself when not present, and the 'sig' property gives the desired
|
|
|
|
|
// signature for that operation. When the 'sig' property is not present,
|
|
|
|
|
// the signature will default to some ambient ensemble of signatures.
|
|
|
|
|
export type RawDependencies = Record<string, {is?: string, sig?: AnyFunc}>
|
|
|
|
|
|
|
|
|
|
// The following type transform fills in any unspecified signatures in RD
|
|
|
|
|
// with the corresponding signatures from SomeSigs:
|
|
|
|
|
type PatchedDepSpec<RD extends RawDependencies, SomeSigs extends GenSigs> = {
|
|
|
|
|
[K in keyof RD]: RD[K] extends {sig: AnyFunc}
|
|
|
|
|
? RD[K]
|
|
|
|
|
: K extends keyof SomeSigs ? (RD[K] & {sig: SomeSigs[K]}) : RD[K]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A factory for building dependency specifications from the ensemble of
|
|
|
|
|
// common signatures for a specific type (and perhaps auxiliary type). This
|
|
|
|
|
// is typically used when describing implementation factories for one type
|
|
|
|
|
// that depend on the common signatures for a *different* type.
|
|
|
|
|
export function commonSpecs<
|
|
|
|
|
T,
|
|
|
|
|
Aux = T,
|
|
|
|
|
CommonSigs extends GenSigs = CommonSignature<T, Aux>
|
|
|
|
|
>() {
|
|
|
|
|
return <RD extends RawDependencies>(
|
|
|
|
|
rd: RD
|
|
|
|
|
): PatchedDepSpec<RD, CommonSigs> => Object.fromEntries(
|
|
|
|
|
Object.keys(rd).map(k => [
|
|
|
|
|
k, 'sig' in rd[k] ? rd[k]
|
|
|
|
|
: {...rd[k], sig: (() => undefined)}
|
|
|
|
|
])
|
|
|
|
|
) as PatchedDepSpec<RD, CommonSigs>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Further constraint on a dependency specification that means it is ready
|
|
|
|
|
// to use with a given set of signatures:
|
|
|
|
|
type DepSpec<Signatures extends GenSigs, Needs extends string>
|
|
|
|
|
= {
|
|
|
|
|
[K in Needs]: K extends keyof Signatures
|
|
|
|
|
? {sig?: AnyFunc}
|
|
|
|
|
: {is: keyof Signatures, sig: AnyFunc}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Just checks if an RawDependencies is really a DepSpec, and blanks it out if not
|
|
|
|
|
type DepCheck<
|
|
|
|
|
RD extends RawDependencies,
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
Needs extends string = keyof RD & string
|
|
|
|
|
> = RD extends DepSpec<Signatures, Needs> ? RD
|
|
|
|
|
: {
|
|
|
|
|
[K in Needs]: K extends keyof Signatures ? {}
|
|
|
|
|
: {is: never, sig: (q: boolean) => void}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The actual type of a dependency, given a dependency specification
|
|
|
|
|
type DepType<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
DS extends DepSpec<Signatures, (keyof DS & string)>
|
|
|
|
|
> = {[K in keyof DS]: DS[K] extends {sig: AnyFunc}
|
|
|
|
|
? DS[K]['sig']
|
|
|
|
|
: K extends keyof Signatures ? Signatures[K] : never
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A collection of dependency specifications for some of the operations in
|
|
|
|
|
// an ensemble of Signatures:
|
|
|
|
|
type Specifications<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
NeedKeys extends keyof Signatures & string,
|
|
|
|
|
NeedList extends Record<NeedKeys, string>
|
|
|
|
|
> = {[K in NeedKeys]: DepSpec<Signatures, NeedList[K]>}
|
|
|
|
|
|
|
|
|
|
// The type of a factory function for implementations of a dependent operation,
|
|
|
|
|
// given a dependency specification:
|
|
|
|
|
type FactoryType<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
K extends (keyof Signatures) & string,
|
|
|
|
|
DS extends DepSpec<Signatures, (keyof DS & string)>
|
|
|
|
|
> = (dep: DepType<Signatures, DS>) => Signatures[K]
|
|
|
|
|
|
|
|
|
|
// The type of an implementation specification for an operation given its
|
|
|
|
|
// dependency specification: either directly the implementation if there
|
|
|
|
|
// are actually no dependencies, or a factory function and collection of
|
|
|
|
|
// dependency names otherwise:
|
|
|
|
|
type ImpType<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
K extends (keyof Signatures) & string,
|
|
|
|
|
DS extends DepSpec<Signatures, (keyof DS & string)>
|
|
|
|
|
> = DS extends null ? {implementation: Signatures[K]}
|
|
|
|
|
: {factory: FactoryType<Signatures, K, DS>, dependencies: DS}
|
|
|
|
|
|
|
|
|
|
// A collection of implementations for some operations of an ensemble of
|
|
|
|
|
// Signatures, matching a given collection of dependency specifications
|
|
|
|
|
type Implementations<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
NeedKeys extends keyof Signatures & string,
|
|
|
|
|
NeedList extends Record<NeedKeys, string>,
|
|
|
|
|
Specs extends Specifications<Signatures, NeedKeys, NeedList>
|
|
|
|
|
> = {[K in NeedKeys]: ImpType<Signatures, K, Specs[K]>}
|
|
|
|
|
|
2024-10-21 17:00:58 +00:00
|
|
|
export interface ReflectedTypeInfo {
|
|
|
|
|
reflectedType5: string
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 13:48:06 -07:00
|
|
|
// The builder interface that lets us assemble narrowly-typed Implementations:
|
|
|
|
|
interface ImplementationBuilder<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
NeedKeys extends keyof Signatures & string,
|
|
|
|
|
NeedList extends Record<NeedKeys, string>,
|
|
|
|
|
Specs extends Specifications<Signatures, NeedKeys, NeedList>
|
|
|
|
|
> {
|
|
|
|
|
independent<NewKeys extends (keyof Signatures) & string>(
|
|
|
|
|
independentImps: {[K in NewKeys]: Signatures[K]}
|
|
|
|
|
): ImplementationBuilder<
|
|
|
|
|
Signatures,
|
|
|
|
|
NeedKeys | NewKeys,
|
|
|
|
|
NeedList & {[K in NewKeys]: never},
|
|
|
|
|
Specs & {[K in NewKeys]: null}
|
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
dependent<
|
|
|
|
|
RD extends RawDependencies, // Easier to infer
|
|
|
|
|
NewKeys extends (keyof Signatures) & string,
|
|
|
|
|
DepKeys extends string = keyof RD & string
|
|
|
|
|
>(
|
|
|
|
|
depSpec: RD,
|
|
|
|
|
imps: {
|
|
|
|
|
[K in NewKeys]:
|
|
|
|
|
FactoryType<Signatures, K, DepCheck<RD, Signatures>>
|
|
|
|
|
}
|
|
|
|
|
): ImplementationBuilder<
|
|
|
|
|
Signatures,
|
|
|
|
|
NeedKeys | NewKeys,
|
|
|
|
|
NeedList & {[K in NewKeys]: DepKeys},
|
|
|
|
|
Specs & {[K in NewKeys]: DepCheck<RD, Signatures>}
|
|
|
|
|
>
|
|
|
|
|
|
2024-10-21 17:00:58 +00:00
|
|
|
ship(info?: ReflectedTypeInfo): Implementations<Signatures, NeedKeys, NeedList, Specs> & ReflectedTypeInfo
|
2024-09-29 13:48:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// And a function that actually provides the builder interface:
|
|
|
|
|
function impBuilder<
|
|
|
|
|
Signatures extends GenSigs,
|
|
|
|
|
NeedKeys extends keyof Signatures & string,
|
|
|
|
|
NeedList extends Record<NeedKeys, string>,
|
|
|
|
|
Specs extends Specifications<Signatures, NeedKeys, NeedList>
|
|
|
|
|
>(
|
|
|
|
|
sofar: Implementations<Signatures, NeedKeys, NeedList, Specs>
|
|
|
|
|
): ImplementationBuilder<Signatures, NeedKeys, NeedList, Specs> {
|
|
|
|
|
return {
|
|
|
|
|
independent<NewKeys extends (keyof Signatures) & string>(
|
|
|
|
|
imps: {[K in NewKeys]: Signatures[K]}) {
|
|
|
|
|
return impBuilder({
|
|
|
|
|
...sofar,
|
|
|
|
|
...Object.fromEntries(Object.keys(imps).map(k => [k, {
|
|
|
|
|
implementation: imps[k]
|
|
|
|
|
}]))
|
|
|
|
|
} as Implementations<
|
|
|
|
|
Signatures,
|
|
|
|
|
NeedKeys | NewKeys,
|
|
|
|
|
NeedList & {[K in NewKeys]: never},
|
|
|
|
|
Specs & {[K in NewKeys]: null}
|
|
|
|
|
>)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
dependent<
|
|
|
|
|
RD extends RawDependencies,
|
|
|
|
|
NewKeys extends (keyof Signatures) & string,
|
|
|
|
|
DepKeys extends string = keyof RD & string
|
|
|
|
|
>(
|
|
|
|
|
depSpec: RD,
|
|
|
|
|
imps: {
|
|
|
|
|
[K in NewKeys]:
|
|
|
|
|
FactoryType<Signatures, K, DepCheck<RD, Signatures, DepKeys>>
|
|
|
|
|
}
|
|
|
|
|
) {
|
|
|
|
|
return impBuilder({
|
|
|
|
|
...sofar,
|
|
|
|
|
...Object.fromEntries(Object.keys(imps).map(k => [k, {
|
|
|
|
|
factory: imps[k],
|
|
|
|
|
dependencies: depSpec
|
|
|
|
|
}]))
|
|
|
|
|
}) as unknown as ImplementationBuilder<
|
|
|
|
|
Signatures,
|
|
|
|
|
NeedKeys | NewKeys,
|
|
|
|
|
NeedList & {[K in NewKeys]: DepKeys},
|
|
|
|
|
Specs & {[K in NewKeys]: DepCheck<RD, Signatures, DepKeys>}
|
|
|
|
|
>
|
|
|
|
|
},
|
2024-10-21 17:00:58 +00:00
|
|
|
ship(info?: ReflectedTypeInfo) {
|
|
|
|
|
return { ...sofar, ...info }
|
2024-09-29 13:48:06 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A convenience function that gives you an implementation builder:
|
|
|
|
|
export function implementations<Signatures extends GenSigs>(
|
|
|
|
|
): ImplementationBuilder<Signatures, never, {}, {}> {
|
|
|
|
|
return impBuilder({})
|
|
|
|
|
}
|