Skip to content

Instantly share code, notes, and snippets.

@alxhub
Created December 3, 2019 19:23
Show Gist options
  • Save alxhub/bd9227706c510a5c775af221c4b103db to your computer and use it in GitHub Desktop.
Save alxhub/bd9227706c510a5c775af221c4b103db to your computer and use it in GitHub Desktop.
import * as ts from 'typescript';
import {DecoratorHandler, DetectResult} from './api';
export enum TraitState {
PENDING = 0x01,
ANALYZED = 0x02,
RESOLVED = 0x04,
ERRORED = 0x08,
SKIPPED = 0x10,
/**
* Matches both `PENDING` and `ANALYZED` states.
*/
PENDING_OR_ANALYZED = 0x03,
}
/**
* An Ivy aspect added to a class (for example, the compilation of a component definition).
*/
export type Trait<D, A, R> = PendingTrait<D, A, R>| SkippedTrait<D, A, R>| AnalyzedTrait<D, A, R>|
ResolvedTrait<D, A, R>| ErroredTrait<D, A, R>;
export const Trait = {
pending<D, A, R>(handler: DecoratorHandler<D, A, R>, detected: DetectResult<D>):
PendingTrait<D, A, R>{// Delegate to the internal TraitImpl.
return TraitImpl.pending(handler, detected);}
};
export interface TraitBase<D, A, R> {
state: TraitState;
/**
* The `DecoratorHandler` which matched on the class to create this trait.
*/
handler: DecoratorHandler<D, A, R>;
/**
* The detection result (of `handler.detect`) which indicated that this trait applied to the
* class.
*
* This is mainly used to cache the detection between pre-analysis and analysis.
*/
detected: DetectResult<D>;
}
export interface PendingTrait<D, A, R> extends TraitBase<D, A, R> {
state: TraitState.PENDING;
toAnalyzed(analysis: A): AnalyzedTrait<D, A, R>;
toErrored(errors: ts.Diagnostic[]): ErroredTrait<D, A, R>;
toSkipped(): SkippedTrait<D, A, R>;
}
export interface ErroredTrait<D, A, R> extends TraitBase<D, A, R> {
state: TraitState.ERRORED;
diagnostics: ts.Diagnostic[];
}
interface SkippedTrait<D, A, R> extends TraitBase<D, A, R> {
state: TraitState.SKIPPED;
}
export interface TraitWithAnalysis<A> {
/**
* The result of analyzing the class.
*/
analysis: Readonly<A>;
}
interface AnalyzedTrait<D, A, R> extends TraitBase<D, A, R>, TraitWithAnalysis<A> {
state: TraitState.ANALYZED;
toErrored(errors: ts.Diagnostic[]): ErroredTrait<D, A, R>;
toResolved(resolution: R): ResolvedTrait<D, A, R>;
}
interface ResolvedTrait<D, A, R> extends TraitBase<D, A, R>, TraitWithAnalysis<A> {
state: TraitState.RESOLVED;
/**
* The result of
*/
resolution: Readonly<R>;
}
/**
* An implementation of the `Trait` type which transitions safely between the various
* `TraitState`s.
*/
class TraitImpl<D, A, R> {
state: TraitState = TraitState.PENDING;
handler: DecoratorHandler<D, A, R>;
detected: DetectResult<D>;
analysis: Readonly<A>|null = null;
resolution: Readonly<R>|null = null;
diagnostics: ts.Diagnostic[]|null = null;
constructor(handler: DecoratorHandler<D, A, R>, detected: DetectResult<D>) {
this.handler = handler;
this.detected = detected;
}
toAnalyzed(analysis: A): AnalyzedTrait<D, A, R> {
this.assertTransitionLegal(TraitState.PENDING, TraitState.ANALYZED);
this.analysis = analysis;
this.state = TraitState.ANALYZED;
return this as AnalyzedTrait<D, A, R>;
}
toErrored(diagnostics: ts.Diagnostic[]): ErroredTrait<D, A, R> {
this.assertTransitionLegal(TraitState.PENDING_OR_ANALYZED, TraitState.RESOLVED);
this.diagnostics = diagnostics;
this.analysis = null;
this.state = TraitState.ERRORED;
return this as ErroredTrait<D, A, R>;
}
toResolved(resolution: R): ResolvedTrait<D, A, R> {
this.assertTransitionLegal(TraitState.ANALYZED, TraitState.RESOLVED);
this.resolution = resolution;
this.state = TraitState.RESOLVED;
return this as ResolvedTrait<D, A, R>;
}
toSkipped(): SkippedTrait<D, A, R> {
this.assertTransitionLegal(TraitState.PENDING, TraitState.SKIPPED);
this.state = TraitState.SKIPPED;
return this as SkippedTrait<D, A, R>;
}
private assertTransitionLegal(allowedState: TraitState, transitionTo: TraitState): void {
if (!(this.state & allowedState)) {
throw new Error(
`Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`)
}
}
static pending<D, A, R>(handler: DecoratorHandler<D, A, R>, detected: DetectResult<D>):
PendingTrait<D, A, R> {
return new TraitImpl(handler, detected) as PendingTrait<D, A, R>;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment