Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON Tree Visitor Inspector #114

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import {
ContainerInstance,
ContainerTreeVisitor,
ServiceIdentifier,
ServiceMetadata,
VisitRetrievalOptions,
} from '../../../../index.mjs';
import { SynchronousDisposable } from '../../../util/synchronous-disposable.class.mjs';
import {
TreeContainerDescriptor
} from '../types/tree-container-descriptor.interface.mjs';
import { TreeRetrievalDescriptor } from '../types/tree-retrieval-descriptor.interface.mjs';
import { TreeServiceDescriptor } from '../types/tree-service-descriptor.interface.mjs';

export class JSONContainerInspector extends SynchronousDisposable implements ContainerTreeVisitor {
public descriptor: TreeContainerDescriptor | null = null;
public container: ContainerInstance | null = null;

visitChildContainer?(childContainer: ContainerInstance): void {
/** Attach a new inspector to the child. */
const childInspector = new JSONContainerInspector();
childContainer.acceptTreeVisitor(childInspector);
const { descriptor } = childInspector;

/** Add the child container's descriptor as a child of ours. */
this.descriptor!.children.push(descriptor!);
}

visitNewService?<T = unknown>(newServiceMetadata: ServiceMetadata<T>): void {
const serviceDescriptor: TreeServiceDescriptor<T> = { metadata: newServiceMetadata };
this.descriptor!.services.push(serviceDescriptor);
}

visitContainer?(container: ContainerInstance): boolean {
if (this.disposed) {
throw new Error('A disposed JSONContainerInspector instance cannot be added as a visitor to a container.');
}

/** Prevent the visitor being added if it is already attached to a container. */
if (this.container) {
throw new Error('The JSONContainerInspector is already attached to a container.');
}

/** Lazily initialize the class' fields. */
this.container = container;
this.descriptor = {
identifier: container.id,
children: [],
services: [],
retrievals: [],
};

return true;
}

visitRetrieval?(identifier: ServiceIdentifier<unknown>, options: VisitRetrievalOptions): void {
const currentTime = this.getCurrentTime();
const retrievalDescriptor: TreeRetrievalDescriptor = {
time: currentTime,
identifier,
options,
};

this.descriptor!.retrievals.push(retrievalDescriptor);
}

dispose() {
super.dispose();
this.container?.detachTreeVisitor(this);
}

/**
* Get the current system time.
*
* @remarks
* The default implementation makes use of `performance.now()` to measure time.
*
* @returns The current time in milliseconds.
*/
protected getCurrentTime() {
return performance.now();
}

public toJSON () {
return this.descriptor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
ContainerInstance,
ContainerTreeVisitor
} from '../../../../index.mjs';
import { SynchronousDisposable } from '../../../util/synchronous-disposable.class.mjs';
import { TreeContainerDescriptor, TreeContainerRootDescriptor } from '../types/tree-container-descriptor.interface.mjs';
import { JSONContainerInspector } from './json-container-inspector.class.mjs';

export class JSONRootContainerInspector extends SynchronousDisposable implements ContainerTreeVisitor {
private orphans: JSONContainerInspector[] = [ ];
public visitor: null | JSONContainerInspector = null;

get descriptor (): TreeContainerRootDescriptor | null {
const { visitor, orphans } = this;

if (!visitor) {
return null;
}

const { descriptor } = visitor;

if (descriptor) {
return {
...descriptor,
orphans: orphans.map(x => x.descriptor as TreeContainerDescriptor)
};
}

return null;
}

visitOrphanedContainer?(container: ContainerInstance): void {
const visitor = new JSONContainerInspector();

if (container.acceptTreeVisitor(visitor)) {
this.orphans.push(visitor);
}
}

visitContainer?(container: ContainerInstance): boolean {
if (this.disposed) {
throw new Error('A disposed JSONRootContainerInspector instance cannot be added as a visitor to a container.');
}

if (this.descriptor) {
throw new Error('The JSONRootContainerInspector is already attached to a container.');
}

const visitor = new JSONContainerInspector();
this.visitor = visitor;

if(!container.acceptTreeVisitor(visitor)) {
container.detachTreeVisitor(this);
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type JSONTreeContainerIdentifier =
| { type: 'symbol', description: string }
| { type: 'string', text: string };

export interface JSONTreeContainerDescriptor {
readonly identifier: JSONTreeContainerIdentifier;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ContainerIdentifier, ContainerInstance } from '../../../../index.mjs';
import { TreeRetrievalDescriptor } from './tree-retrieval-descriptor.interface.mjs';
import { TreeServiceDescriptor } from './tree-service-descriptor.interface.mjs';

/** An object representation of a {@link ContainerInstance}. */
export interface TreeContainerDescriptor {
/** The identifier of the container. */
readonly identifier: ContainerIdentifier;

/** A sorted list of retrieval operations performed on this container. */
readonly retrievals: TreeRetrievalDescriptor[];

/** An array of services provided by this container. */
readonly services: TreeServiceDescriptor[];

/** An array of children provided by this container. */
readonly children: TreeContainerDescriptor[];
}

export interface TreeContainerRootDescriptor extends TreeContainerDescriptor {
readonly orphans: TreeContainerDescriptor[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ServiceIdentifier, VisitRetrievalOptions } from '../../../../index.mjs';

// todo: add tsdoc to all of this

export interface TreeRetrievalDescriptor {
/** The time at which the retrieval was performed. */
readonly time: number;

/** The identifier being retrieved. */
readonly identifier: ServiceIdentifier;

/** The {@link VisitRetrievalOptions} value passed to the visitor. */
readonly options: VisitRetrievalOptions;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ServiceMetadata } from '../../../../index.mjs';

// todo: add tsdoc to all of this

export interface TreeServiceDescriptor<T = unknown> {
readonly metadata: ServiceMetadata<T>;
}
Loading