Created
December 3, 2019 19:23
-
-
Save alxhub/bd9227706c510a5c775af221c4b103db to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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