зеркало из https://github.com/microsoft/tiny-calc.git
Types: Separate shape from data
This commit is contained in:
Родитель
446c8c6956
Коммит
a538266223
|
@ -1,3 +1,8 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { IProducer, IReader, IConsumer } from "@tiny-calc/types";
|
||||
import { ConsumerSet, addConsumer, removeConsumer, forEachConsumer } from "../consumerset";
|
||||
|
||||
|
@ -6,12 +11,12 @@ export abstract class Producer<TMap> implements IProducer<TMap>, IReader<TMap> {
|
|||
|
||||
//#region IProducer
|
||||
|
||||
open(consumer: IConsumer<TMap>): IReader<TMap> {
|
||||
public open(consumer: IConsumer<TMap>): IReader<TMap> {
|
||||
this.consumers = addConsumer(this.consumers, consumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
close(consumer: IConsumer<TMap>): void {
|
||||
public close(consumer: IConsumer<TMap>): void {
|
||||
this.consumers = removeConsumer(this.consumers, consumer);
|
||||
}
|
||||
|
||||
|
@ -19,15 +24,15 @@ export abstract class Producer<TMap> implements IProducer<TMap>, IReader<TMap> {
|
|||
|
||||
//#region IReader
|
||||
|
||||
public abstract get<K extends keyof TMap>(property: K): TMap[K];
|
||||
public abstract get<K extends keyof TMap>(key: K): TMap[K];
|
||||
|
||||
public get producer() { return this; }
|
||||
public get producer():IProducer<TMap> { return this; }
|
||||
|
||||
//#endregion IReader
|
||||
|
||||
protected invalidateValue<K extends keyof TMap>(property: K): void {
|
||||
protected invalidateValue<K extends keyof TMap>(key: K): void {
|
||||
forEachConsumer(this.consumers, (consumer) => {
|
||||
consumer.valueChanged(property, this);
|
||||
consumer.keyChanged(key, this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
import "mocha";
|
||||
import { strict as assert } from "assert";
|
||||
import { Random } from "best-random";
|
||||
import { IMatrixConsumer, IMatrixReader, IMatrixProducer, IVectorWriter, IMatrixWriter } from "@tiny-calc/types";
|
||||
import {
|
||||
IMatrixConsumer,
|
||||
IMatrixReader,
|
||||
IMatrixProducer,
|
||||
IMatrixWriter,
|
||||
IVectorShapeWriter,
|
||||
IVectorWriter,
|
||||
} from "@tiny-calc/types";
|
||||
import { DenseVector, RowMajorMatrix } from "../src";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -95,8 +102,8 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
|
||||
public constructor (
|
||||
producer: IMatrixProducer<T>,
|
||||
private readonly rowWriter: IVectorWriter<TRow>,
|
||||
private readonly colWriter: IVectorWriter<TCol>,
|
||||
private readonly rowWriter: IVectorWriter<TRow> & IVectorShapeWriter,
|
||||
private readonly colWriter: IVectorWriter<TCol> & IVectorShapeWriter,
|
||||
private readonly cellWriter: IMatrixWriter<T>
|
||||
) {
|
||||
this.reader = producer.openMatrix(this);
|
||||
|
@ -109,7 +116,7 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
|
||||
public rowsChanged(rowStart: number, removedCount: number, insertedCount: number): void {
|
||||
const rowEnd = rowStart + removedCount;
|
||||
|
||||
|
||||
assert(0 <= rowStart && rowStart <= rowEnd && rowEnd <= this.consumed.rowCount);
|
||||
|
||||
if (removedCount > 0) { this.consumed.removeRows(rowStart, removedCount); }
|
||||
|
@ -154,7 +161,7 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
|
||||
const actual = this.reader.getCell(row, col);
|
||||
const expected = this.expected.getCell(row, col);
|
||||
|
||||
|
||||
assert.equal(actual, expected);
|
||||
assert.equal(this.consumed.getCell(row, col), expected);
|
||||
|
||||
|
@ -173,7 +180,7 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
|
||||
this.expected.setCell(row, col, value);
|
||||
this.cellWriter.setCell(row, col, value);
|
||||
|
||||
|
||||
assert.equal(this.getCell(row, col), value,
|
||||
`Writer.setCell(${row},${col}) must update matrix value.`);
|
||||
}
|
||||
|
@ -239,7 +246,7 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
}
|
||||
|
||||
public extract(): ReadonlyArray<ReadonlyArray<T>> {
|
||||
const m: T[][] = [];
|
||||
const m: T[][] = [];
|
||||
for (let r = 0; r < this.rowCount; r++) {
|
||||
const row: T[] = [];
|
||||
m.push(row);
|
||||
|
@ -265,7 +272,7 @@ export class TestMatrix<T = any, TRow = never, TCol = never> implements IMatrixC
|
|||
|
||||
public expectSize(rowCount: number, colCount: number): void {
|
||||
this.check();
|
||||
|
||||
|
||||
assert.equal(this.rowCount, rowCount);
|
||||
assert.equal(this.colCount, colCount);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import "mocha";
|
||||
import { strict as assert } from "assert";
|
||||
import { Random } from "best-random";
|
||||
import { IVectorConsumer, IVectorReader, IVectorProducer, IVectorWriter } from "@tiny-calc/types";
|
||||
import { IVectorConsumer, IVectorReader, IVectorProducer, IVectorWriter, IVectorShapeWriter } from "@tiny-calc/types";
|
||||
import { DenseVector } from "../src";
|
||||
|
||||
export class TestVector<T> implements IVectorConsumer<T> {
|
||||
|
@ -14,7 +14,7 @@ export class TestVector<T> implements IVectorConsumer<T> {
|
|||
private readonly consumed: T[] = [];
|
||||
private readonly expected: T[] = [];
|
||||
|
||||
public constructor(producer: IVectorProducer<T>, private readonly writer: IVectorWriter<T>) {
|
||||
public constructor(producer: IVectorProducer<T>, private readonly writer: IVectorWriter<T> & IVectorShapeWriter) {
|
||||
this.actual = producer.openVector(this);
|
||||
|
||||
for (let i = 0; i < this.actual.length; i++) {
|
||||
|
@ -41,11 +41,11 @@ export class TestVector<T> implements IVectorConsumer<T> {
|
|||
|
||||
public itemsChanged(start: number, removedCount: number, insertedCount: number, producer: IVectorProducer<T>): void {
|
||||
const inserted = [];
|
||||
|
||||
|
||||
for (let i = start; insertedCount > 0; i++, insertedCount--) {
|
||||
inserted.push(this.actual.getItem(i));
|
||||
}
|
||||
|
||||
|
||||
this.consumed.splice(start, removedCount, ...inserted);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
import {
|
||||
IConsumer,
|
||||
IProducer,
|
||||
IVectorConsumer,
|
||||
|
@ -25,33 +25,33 @@ export class LoggingConsumer<T> implements IConsumer<T>, IVectorConsumer<T>, IMa
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(producer as any)[idSym] = value;
|
||||
}
|
||||
|
||||
|
||||
private getProducerId(producer: AnyProducer): string {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (producer as any)[idSym] ?? "Error: Missing call to LoggingConsumer.setProducerId(..)";
|
||||
}
|
||||
|
||||
// #region IConsumer<T>
|
||||
public valueChanged<U extends T, K extends keyof U>(property: K, producer: IProducer<U>): void {
|
||||
public keyChanged<U extends T, K extends keyof U>(property: K, producer: IProducer<U>): void {
|
||||
this.log.push({ property, producer: this.getProducerId(producer) });
|
||||
}
|
||||
// #endregion IConsumer<T>
|
||||
|
||||
|
||||
// #region IVectorConsumer<T>
|
||||
public itemsChanged(start: number, removedCount: number, insertedCount: number, producer: IVectorProducer<T>): void {
|
||||
this.log.push({ start, removedCount, insertedCount, producer: this.getProducerId(producer) });
|
||||
}
|
||||
// #endregion IVectorConsumer<T>
|
||||
|
||||
|
||||
// #region IMatrixConsumer<T>
|
||||
public rowsChanged(rowStart: number, removedCount: number, insertedCount: number, producer: IMatrixProducer<T>): void {
|
||||
this.log.push({ rowStart, removedCount, insertedCount, producer: this.getProducerId(producer) });
|
||||
}
|
||||
|
||||
|
||||
public colsChanged(colStart: number, removedCount: number, insertedCount: number, producer: IMatrixProducer<T>): void {
|
||||
this.log.push({ colStart, removedCount, insertedCount, producer: this.getProducerId(producer) });
|
||||
}
|
||||
|
||||
|
||||
public cellsChanged(rowStart: number, colStart: number, rowCount: number, colCount: number, producer: IMatrixProducer<T>): void {
|
||||
this.log.push({ rowStart, colStart, rowCount, colCount, producer: this.getProducerId(producer) });
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class NullConsumer<T> implements IConsumer<T>, IVectorConsumer<T>, IMatrixConsum
|
|||
public rowsChanged(): void { }
|
||||
public colsChanged(): void { }
|
||||
public cellsChanged(): void { }
|
||||
public valueChanged(): void { }
|
||||
public keyChanged(): void { }
|
||||
public itemsChanged(): void { }
|
||||
}
|
||||
|
||||
|
|
|
@ -9,5 +9,5 @@ const listFormula = new ListFormula(context, "IF(Time.Now = Time.Now, Math.Max(T
|
|||
});
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
listFormula.valueChanged();
|
||||
listFormula.keyChanged();
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export class Context implements Producer {
|
|||
|
||||
open() {
|
||||
const producer: Producer = this;
|
||||
return {
|
||||
return {
|
||||
get: (key: string) => this.fields[key],
|
||||
producer
|
||||
};
|
||||
|
@ -47,7 +47,7 @@ export class TimeProducer implements Producer {
|
|||
open() {
|
||||
const time = Date.now();
|
||||
const producer: Producer = this;
|
||||
return {
|
||||
return {
|
||||
get: () => time,
|
||||
producer
|
||||
};
|
||||
|
@ -114,7 +114,7 @@ export class ListFormula implements FormulaHost {
|
|||
}
|
||||
}
|
||||
|
||||
valueChanged() {
|
||||
keyChanged() {
|
||||
console.time("recalc");
|
||||
const scope = createCalcValue(this.scope);
|
||||
const values = [];
|
||||
|
|
|
@ -3,9 +3,36 @@
|
|||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export { IConsumer, IProducer, IReader, IWriter } from './record';
|
||||
export { IVectorConsumer, IVectorProducer, IVectorReader, IVectorWriter } from './vector';
|
||||
export { IMatrixConsumer, IMatrixProducer, IMatrixReader, IMatrixWriter } from './matrix';
|
||||
export {
|
||||
IConsumer,
|
||||
IProducer,
|
||||
IReader,
|
||||
IShapeConsumer,
|
||||
IShapeProducer,
|
||||
IShapeReader,
|
||||
IShapeWriter,
|
||||
IWriter
|
||||
} from './record';
|
||||
export {
|
||||
IVectorConsumer,
|
||||
IVectorProducer,
|
||||
IVectorReader,
|
||||
IVectorShapeConsumer,
|
||||
IVectorShapeProducer,
|
||||
IVectorShapeReader,
|
||||
IVectorShapeWriter,
|
||||
IVectorWriter
|
||||
} from './vector';
|
||||
export {
|
||||
IMatrixConsumer,
|
||||
IMatrixProducer,
|
||||
IMatrixReader,
|
||||
IMatrixShapeConsumer,
|
||||
IMatrixShapeProducer,
|
||||
IMatrixShapeReader,
|
||||
IMatrixShapeWriter,
|
||||
IMatrixWriter
|
||||
} from './matrix';
|
||||
export {
|
||||
ITreeShapeConsumer,
|
||||
ITreeShapeProducer,
|
||||
|
|
|
@ -4,27 +4,80 @@
|
|||
*/
|
||||
|
||||
/** An observable 2D numerically indexed collection. */
|
||||
export interface IMatrixProducer<T> {
|
||||
export interface IMatrixShapeProducer {
|
||||
/**
|
||||
* Acquire a reader for this matrix's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to be notified of matrix changes.
|
||||
*/
|
||||
openMatrix(consumer: IMatrixShapeConsumer): IMatrixShapeReader;
|
||||
|
||||
/**
|
||||
* Unsubscribe the consumer from this matrix's change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the matrix.
|
||||
*/
|
||||
closeMatrix(consumer: IMatrixShapeConsumer): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a matrix. */
|
||||
export interface IMatrixShapeConsumer {
|
||||
/** Notification that rows have been inserted, removed, and/or replaced in the given matrix. */
|
||||
rowsChanged(rowStart: number, removedCount: number, insertedCount: number, producer: IMatrixShapeProducer): void;
|
||||
|
||||
/** Notification that cols have been inserted, removed, and/or replaced in the given matrix. */
|
||||
colsChanged(colStart: number, removedCount: number, insertedCount: number, producer: IMatrixShapeProducer): void;
|
||||
}
|
||||
|
||||
/** Capability to read cells in a matrix. */
|
||||
export interface IMatrixShapeReader {
|
||||
readonly rowCount: number;
|
||||
readonly colCount: number;
|
||||
|
||||
/**
|
||||
* A reference to the underlying producer that provides values for this reader,
|
||||
* or undefined if the producer is immutable.
|
||||
*/
|
||||
readonly matrixProducer?: IMatrixShapeProducer;
|
||||
}
|
||||
|
||||
/** Capability to write cells in a matrix. */
|
||||
export interface IMatrixShapeWriter {
|
||||
spliceRows(rowStart: number, deleteCount: number, insertCount: number): void;
|
||||
spliceCols(colStart: number, deleteCount: number, insertCount: number): void;
|
||||
}
|
||||
|
||||
/** An observable 2D numerically indexed collection. */
|
||||
export interface IMatrixProducer<T> extends IMatrixShapeProducer {
|
||||
/**
|
||||
* Acquire a reader for this matrix's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to be notified of matrix changes.
|
||||
*/
|
||||
openMatrix(consumer: IMatrixConsumer<T>): IMatrixReader<T>;
|
||||
|
||||
/**
|
||||
* Unsubscribe the consumer from this matrix's change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the matrix.
|
||||
*/
|
||||
closeMatrix(consumer: IMatrixConsumer<T>): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a matrix. */
|
||||
export interface IMatrixConsumer<T> extends IMatrixShapeConsumer {
|
||||
/**
|
||||
* Notification that a range of cells have been replaced in the given matrix. If the source
|
||||
* matrix has the new cell values already in an array, it may optionally pass these to consumers
|
||||
* as an optimization.
|
||||
*/
|
||||
cellsChanged(rowStart: number, colStart: number, rowCount: number, colCount: number, producer: IMatrixProducer<T>): void;
|
||||
}
|
||||
|
||||
/** Capability to read cells in a matrix. */
|
||||
export interface IMatrixReader<T> {
|
||||
readonly rowCount: number;
|
||||
readonly colCount: number;
|
||||
export interface IMatrixReader<T> extends IMatrixShapeReader {
|
||||
getCell(row: number, col: number): T;
|
||||
|
||||
/**
|
||||
|
@ -39,22 +92,6 @@ export interface IMatrixWriter<T> {
|
|||
setCell(row: number, col: number, value: T): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a matrix. */
|
||||
export interface IMatrixConsumer<T> {
|
||||
/** Notification that rows have been inserted, removed, and/or replaced in the given matrix. */
|
||||
rowsChanged(rowStart: number, removedCount: number, insertedCount: number, producer: IMatrixProducer<T>): void;
|
||||
|
||||
/** Notification that cols have been inserted, removed, and/or replaced in the given matrix. */
|
||||
colsChanged(colStart: number, removedCount: number, insertedCount: number, producer: IMatrixProducer<T>): void;
|
||||
|
||||
/**
|
||||
* Notification that a range of cells have been replaced in the given matrix. If the source
|
||||
* matrix has the new cell values already in an array, it may optionally pass these to consumers
|
||||
* as an optimization.
|
||||
*/
|
||||
cellsChanged(rowStart: number, colStart: number, rowCount: number, colCount: number, producer: IMatrixProducer<T>): void;
|
||||
}
|
||||
|
||||
export interface MatrixIteratorSpec {
|
||||
/**
|
||||
* Iterates over empty cells.
|
||||
|
|
|
@ -3,45 +3,85 @@
|
|||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/** An observable key/value collection, such as a Record or Map. */
|
||||
export interface IShapeProducer<TKey> {
|
||||
/**
|
||||
* Acquire a reader for this producer's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to be notified of value changes.
|
||||
*/
|
||||
open(consumer: IShapeConsumer<TKey>): IShapeReader<TKey>;
|
||||
|
||||
/**
|
||||
* Unsubscribe the given 'consumer' from this producer's change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the producer.
|
||||
*/
|
||||
close(consumer: IShapeConsumer<TKey>): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a key/value collection, such as a Record or Map. */
|
||||
export interface IShapeConsumer<TKey> {
|
||||
keyChanged(key: TKey, producer: IShapeProducer<TKey>): void;
|
||||
}
|
||||
|
||||
/** Capability to read values of a key/value collection. */
|
||||
export interface IShapeReader<TKey> {
|
||||
keys(): IterableIterator<TKey>;
|
||||
|
||||
has(key: TKey): boolean;
|
||||
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
* A reference to the underlying producer that provides the shape for this reader,
|
||||
* or undefined if the producer is immutable.
|
||||
*/
|
||||
readonly producer?: IShapeProducer<TKey>;
|
||||
}
|
||||
|
||||
export interface IShapeWriter<TKey> {
|
||||
delete(key: TKey): void;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/** An observable key/value collection, such as a Record or Map. */
|
||||
export interface IProducer<T> {
|
||||
/**
|
||||
* Acquire a reader for this producer's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to be notified of value changes.
|
||||
*/
|
||||
open(consumer: IConsumer<T>): IReader<T>;
|
||||
open(consumer: IConsumer<T>): IReader<T> | (IReader<T> & IShapeReader<T>);
|
||||
|
||||
/**
|
||||
* Unsubscribe the given 'consumer' from this producer's change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the producer.
|
||||
*/
|
||||
close(consumer: IConsumer<T>): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a key/value collection, such as a Record or Map. */
|
||||
export interface IConsumer<T> {
|
||||
keyChanged<K extends keyof T>(key: K, producer: IProducer<T>): void;
|
||||
}
|
||||
|
||||
/** Capability to read values of a key/value collection. */
|
||||
export interface IReader<T> {
|
||||
/** Return the value associated with `property`. */
|
||||
get<K extends keyof T>(property: K): T[K];
|
||||
/** Return the value associated with `key`. */
|
||||
get<K extends keyof T>(key: K): T[K];
|
||||
|
||||
/**
|
||||
* A reference to the underlying producer that provides values for this reader,
|
||||
* or undefined if the producer is immutable.
|
||||
*/
|
||||
readonly producer?: IProducer<T>;
|
||||
readonly producer?: IProducer<T> | (IProducer<T> & IShapeProducer<keyof T>);
|
||||
}
|
||||
|
||||
/** Capability to set values of a key/value collection. */
|
||||
export interface IWriter<T> {
|
||||
set<K extends keyof T>(property: K, value: T[K]): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a key/value collection, such as a Record or Map. */
|
||||
export interface IConsumer<T> {
|
||||
/**
|
||||
* Invoked whenever the data this object is bound to is changed.
|
||||
*/
|
||||
valueChanged<U extends T, K extends keyof U>(property: K, producer: IProducer<U>): void;
|
||||
set<K extends keyof T>(key: K, value: T[K]): void;
|
||||
}
|
||||
|
|
|
@ -4,26 +4,64 @@
|
|||
*/
|
||||
|
||||
/** An observable 1D numerically indexed collection, such as an Array. */
|
||||
export interface IVectorProducer<T> {
|
||||
export interface IVectorShapeProducer {
|
||||
/**
|
||||
* Acquire a reader for this vector's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to be notified of vector changes.
|
||||
*/
|
||||
openVector(consumer: IVectorShapeConsumer): IVectorShapeReader;
|
||||
|
||||
/**
|
||||
* Unsubscribe the given 'consumer' from this vector's change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the vector.
|
||||
*/
|
||||
closeVector(consumer: IVectorShapeConsumer): void;
|
||||
}
|
||||
|
||||
/** Capability to read items in a vector. */
|
||||
export interface IVectorShapeReader {
|
||||
readonly length: number;
|
||||
|
||||
/**
|
||||
* A reference to the underlying producer that provides values for this reader,
|
||||
* or undefined if the producer is immutable.
|
||||
*/
|
||||
readonly vectorProducer?: IVectorShapeProducer;
|
||||
}
|
||||
|
||||
/** Capability to insert, replace, and remove items in a vector. */
|
||||
export interface IVectorShapeWriter {
|
||||
splice(start: number, deleteCount: number, insertCount: number): void;
|
||||
}
|
||||
|
||||
export interface IVectorShapeConsumer {
|
||||
/** Notification that a range of items have been inserted, removed, and/or replaced in the given vector. */
|
||||
itemsChanged(start: number, removedCount: number, insertedCount: number, producer: IVectorShapeProducer): void;
|
||||
}
|
||||
|
||||
/** An observable 1D numerically indexed collection, such as an Array. */
|
||||
export interface IVectorProducer<T> extends IVectorShapeProducer {
|
||||
/**
|
||||
* Acquire a reader for this vector's values and implicitly subscribe the consumer
|
||||
* to value change notifications.
|
||||
*
|
||||
* @param consumer - The consumer to be notified of vector changes.
|
||||
*/
|
||||
openVector(consumer: IVectorConsumer<T>): IVectorReader<T>;
|
||||
|
||||
/**
|
||||
* Unsubscribe the given 'consumer' from this vector's change notifications.
|
||||
*
|
||||
*
|
||||
* @param consumer - The consumer to unregister from the vector.
|
||||
*/
|
||||
closeVector(consumer: IVectorConsumer<T>): void;
|
||||
}
|
||||
|
||||
/** Capability to read items in a vector. */
|
||||
export interface IVectorReader<T> {
|
||||
readonly length: number;
|
||||
export interface IVectorReader<T> extends IVectorShapeReader {
|
||||
getItem(index: number): T;
|
||||
|
||||
/**
|
||||
|
@ -35,12 +73,11 @@ export interface IVectorReader<T> {
|
|||
|
||||
/** Capability to insert, replace, and remove items in a vector. */
|
||||
export interface IVectorWriter<T> {
|
||||
splice(start: number, deleteCount: number, insertCount: number): void;
|
||||
setItem(index: number, item: T): void;
|
||||
}
|
||||
|
||||
/** A consumer of change notifications for a vector. */
|
||||
export interface IVectorConsumer<T> {
|
||||
export interface IVectorConsumer<T> extends IVectorShapeConsumer {
|
||||
/** Notification that a range of items have been inserted, removed, and/or replaced in the given vector. */
|
||||
itemsChanged(start: number, removedCount: number, insertedCount: number, producer: IVectorProducer<T>): void;
|
||||
}
|
||||
|
|
|
@ -5,32 +5,63 @@
|
|||
|
||||
import "mocha";
|
||||
import { strict as assert } from "assert";
|
||||
import { IReader, IWriter } from "../src";
|
||||
import { IReader, IWriter, IShapeReader, IShapeWriter } from "../src";
|
||||
|
||||
describe("Record", () => {
|
||||
describe("must be compatible with ES6 map", () => {
|
||||
let map: Map<string, number>;
|
||||
let shapeReader: IShapeReader<string>;
|
||||
let shapeWriter: IShapeWriter<string>;
|
||||
let reader: IReader<Record<string, number | undefined>>;
|
||||
let writer: IWriter<Record<string, number>>;
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
map = new Map<string, number>();
|
||||
shapeReader = map;
|
||||
shapeWriter = map;
|
||||
reader = map;
|
||||
writer = map;
|
||||
});
|
||||
|
||||
|
||||
it("ES6 map must satisfy IReader", () => {
|
||||
map.set("0", 0);
|
||||
assert.equal(reader.get("0"), 0);
|
||||
});
|
||||
|
||||
|
||||
it("ES6 map must satisfy IWriter", () => {
|
||||
writer.set("0", 0);
|
||||
assert.equal(map.get("0"), 0);
|
||||
});
|
||||
|
||||
it("ES6 map must satisfy IShapeReader", () => {
|
||||
assert.deepEqual([...shapeReader.keys()], []);
|
||||
|
||||
const keys = ["a", "b", "c"];
|
||||
for (const key of keys) {
|
||||
assert.equal(shapeReader.has(key), false);
|
||||
map.set(key, key.charCodeAt(0));
|
||||
assert.equal(shapeReader.has(key), true);
|
||||
}
|
||||
|
||||
assert.deepEqual([...shapeReader.keys()], keys);
|
||||
});
|
||||
|
||||
it("ES6 map must satisfy IShapeWriter", () => {
|
||||
const keys = ["a", "b", "c"];
|
||||
for (const key of keys) {
|
||||
assert.equal(shapeReader.has(key), false);
|
||||
assert.equal(shapeWriter.delete(key), false);
|
||||
|
||||
map.set(key, key.charCodeAt(0));
|
||||
|
||||
assert.equal(shapeReader.has(key), true);
|
||||
assert.equal(shapeWriter.delete(key), true);
|
||||
assert.equal(shapeReader.has(key), false);
|
||||
}
|
||||
});
|
||||
|
||||
it("Must support key constraints", () => {
|
||||
const map = new Map([
|
||||
const map = new Map<"0" | "1", number>([
|
||||
["0", 0],
|
||||
["1", 1],
|
||||
]);
|
||||
|
@ -39,7 +70,7 @@ describe("Record", () => {
|
|||
"0": number | undefined,
|
||||
"1": number | undefined,
|
||||
}> = map;
|
||||
|
||||
|
||||
assert.equal(reader.get("0"), 0);
|
||||
assert.equal(reader.get("1"), 1);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче