Much better duck typing
This commit is contained in:
parent
10351b5369
commit
329cee7bd6
|
@ -12,27 +12,30 @@ import { Tag } from './constants';
|
|||
import { PreserveOn } from './symbols';
|
||||
|
||||
export type ErrorType = 'DecodeError' | 'EncodeError' | 'ShortPacket';
|
||||
|
||||
export function isCodecError(e: any, t?: ErrorType): e is PreservesCodecError {
|
||||
return typeof e === 'object' && e !== null &&
|
||||
'_codecErrorType' in e &&
|
||||
(!t || e._codecErrorType() === t);
|
||||
}
|
||||
|
||||
export const isDecodeError = (e: any): e is DecodeError => isCodecError(e, 'DecodeError');
|
||||
export const isEncodeError = (e: any): e is EncodeError => isCodecError(e, 'EncodeError');
|
||||
export const isShortPacket = (e: any): e is ShortPacket => isCodecError(e, 'ShortPacket');
|
||||
export const ErrorType = Symbol.for('ErrorType');
|
||||
|
||||
export abstract class PreservesCodecError {
|
||||
abstract _codecErrorType(): ErrorType;
|
||||
abstract get [ErrorType](): ErrorType;
|
||||
|
||||
static isCodecError(e: any, t: ErrorType): e is PreservesCodecError {
|
||||
return (e?.[ErrorType] === t);
|
||||
}
|
||||
}
|
||||
|
||||
export class DecodeError extends Error {
|
||||
_codecErrorType(): ErrorType { return 'DecodeError' }
|
||||
get [ErrorType](): ErrorType { return 'DecodeError' }
|
||||
|
||||
static isDecodeError(e: any): e is DecodeError {
|
||||
return PreservesCodecError.isCodecError(e, 'DecodeError');
|
||||
}
|
||||
}
|
||||
|
||||
export class EncodeError extends Error {
|
||||
_codecErrorType(): ErrorType { return 'EncodeError' }
|
||||
get [ErrorType](): ErrorType { return 'EncodeError' }
|
||||
|
||||
static isEncodeError(e: any): e is EncodeError {
|
||||
return PreservesCodecError.isCodecError(e, 'EncodeError');
|
||||
}
|
||||
|
||||
readonly irritant: any;
|
||||
|
||||
|
@ -43,7 +46,11 @@ export class EncodeError extends Error {
|
|||
}
|
||||
|
||||
export class ShortPacket extends DecodeError {
|
||||
_codecErrorType(): ErrorType { return 'ShortPacket' }
|
||||
get [ErrorType](): ErrorType { return 'ShortPacket' }
|
||||
|
||||
static isShortPacket(e: any): e is ShortPacket {
|
||||
return PreservesCodecError.isCodecError(e, 'ShortPacket');
|
||||
}
|
||||
}
|
||||
|
||||
export interface DecoderOptions {
|
||||
|
@ -176,7 +183,7 @@ export class Decoder {
|
|||
try {
|
||||
return this.next();
|
||||
} catch (e) {
|
||||
if (e instanceof ShortPacket) {
|
||||
if (ShortPacket.isShortPacket(e)) {
|
||||
this.index = start;
|
||||
return void 0;
|
||||
}
|
||||
|
@ -266,7 +273,7 @@ export class Encoder {
|
|||
this.emitbyte(Tag.SignedInteger);
|
||||
this.varint(bytecount);
|
||||
}
|
||||
const enc = (n, x) => {
|
||||
const enc = (n: number, x: number) => {
|
||||
if (n > 0) {
|
||||
enc(n - 1, x >> 8);
|
||||
this.emitbyte(x & 255);
|
||||
|
@ -294,7 +301,7 @@ export class Encoder {
|
|||
}
|
||||
|
||||
push(v: any) {
|
||||
if (typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function') {
|
||||
if (typeof v?.[PreserveOn] === 'function') {
|
||||
v[PreserveOn](this);
|
||||
}
|
||||
else if (typeof v === 'boolean') {
|
||||
|
@ -326,7 +333,7 @@ export class Encoder {
|
|||
else if (Array.isArray(v)) {
|
||||
this.encodevalues(Tag.Sequence, v);
|
||||
}
|
||||
else if (typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function') {
|
||||
else if (typeof v?.[Symbol.iterator] === 'function') {
|
||||
this.encodevalues(Tag.Sequence, v as Iterable<Value>);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -22,6 +22,20 @@ export type IdentitySet<V> = Set<V>;
|
|||
export const IdentityMap = Map;
|
||||
export const IdentitySet = Set;
|
||||
|
||||
export const IsMap = Symbol.for('IsMap');
|
||||
export const IsSet = Symbol.for('IsSet');
|
||||
|
||||
declare global {
|
||||
interface Map<K, V> { [IsMap]: boolean; }
|
||||
interface MapConstructor { isMap<K, V>(x: any): x is Map<K, V>; }
|
||||
interface Set<T> { [IsSet]: boolean; }
|
||||
interface SetConstructor { isSet<T>(x: any): x is Set<T>; }
|
||||
}
|
||||
Object.defineProperty(Map.prototype, IsMap, { get() { return true; } });
|
||||
Map.isMap = <K,V> (x: any): x is Map<K, V> => !!x?.[IsMap];
|
||||
Object.defineProperty(Set.prototype, IsSet, { get() { return true; } });
|
||||
Set.isSet = <T> (x: any): x is Set<T> => !!x?.[IsSet];
|
||||
|
||||
export function _iterMap<S,T>(i: Iterator<S> | undefined, f : (s: S) => T): IterableIterator<T> {
|
||||
if (!i) return void 0;
|
||||
const _f = (r: IteratorResult<S>): IteratorResult<T> => {
|
||||
|
@ -139,6 +153,10 @@ export class FlexMap<K, V> implements Map<K, V> {
|
|||
canonicalKeys(): IterableIterator<string> {
|
||||
return this.items.keys();
|
||||
}
|
||||
|
||||
get [IsMap](): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class FlexSet<V> implements Set<V> {
|
||||
|
@ -238,4 +256,8 @@ export class FlexSet<V> implements Set<V> {
|
|||
for (let k of this) if (!other.has(k)) result.add(k);
|
||||
return result;
|
||||
}
|
||||
|
||||
get [IsSet](): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Value } from './values';
|
||||
|
||||
export function stringify(x: any): string {
|
||||
if (typeof x === 'object' && x !== null && 'asPreservesText' in x) {
|
||||
if (typeof x?.asPreservesText === 'function') {
|
||||
return x.asPreservesText();
|
||||
} else {
|
||||
try {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { PreserveOn, AsPreserve } from './symbols';
|
|||
import { Tag } from './constants';
|
||||
import { Encoder, encode } from './codec';
|
||||
import { stringify } from './text';
|
||||
import { _iterMap, FlexMap, FlexSet, IdentityMap, IdentitySet } from './flex';
|
||||
import { _iterMap, FlexMap, FlexSet } from './flex';
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
|
@ -13,9 +13,9 @@ export type Value = Atom | Compound | Annotated;
|
|||
export type Atom = boolean | Single | Double | number | string | Bytes | symbol;
|
||||
export type Compound = Record | Array<Value> | Set | Dictionary<Value>;
|
||||
|
||||
export function isRecord(x: any): x is Record {
|
||||
return Array.isArray(x) && 'label' in x;
|
||||
}
|
||||
export const IsPreservesRecord = Symbol.for('IsPreservesRecord');
|
||||
export const IsPreservesBytes = Symbol.for('IsPreservesBytes');
|
||||
export const IsPreservesAnnotated = Symbol.for('IsPreservesAnnotated');
|
||||
|
||||
export function fromJS(x: any): Value {
|
||||
switch (typeof x) {
|
||||
|
@ -41,7 +41,7 @@ export function fromJS(x: any): Value {
|
|||
if (typeof x[AsPreserve] === 'function') {
|
||||
return x[AsPreserve]();
|
||||
}
|
||||
if (isRecord(x)) {
|
||||
if (Record.isRecord(x)) {
|
||||
return x;
|
||||
}
|
||||
if (Array.isArray(x)) {
|
||||
|
@ -57,6 +57,7 @@ export function fromJS(x: any): Value {
|
|||
}
|
||||
|
||||
export type FloatType = 'Single' | 'Double';
|
||||
export const FloatType = Symbol.for('FloatType');
|
||||
|
||||
export abstract class Float {
|
||||
readonly value: number;
|
||||
|
@ -78,6 +79,14 @@ export abstract class Float {
|
|||
}
|
||||
|
||||
abstract asPreservesText(): string;
|
||||
abstract get [FloatType](): FloatType;
|
||||
|
||||
static isFloat(x: any, t: FloatType): x is Float {
|
||||
return (x?.[FloatType] === t);
|
||||
}
|
||||
|
||||
static isSingle = (x: any): x is Single => Float.isFloat(x, 'Single');
|
||||
static isDouble = (x: any): x is Double => Float.isFloat(x, 'Double');
|
||||
}
|
||||
|
||||
export class Single extends Float {
|
||||
|
@ -92,7 +101,7 @@ export class Single extends Float {
|
|||
encoder.index += 4;
|
||||
}
|
||||
|
||||
_floatType(): FloatType {
|
||||
get [FloatType](): FloatType {
|
||||
return 'Single';
|
||||
}
|
||||
|
||||
|
@ -113,7 +122,7 @@ export class Double extends Float {
|
|||
encoder.index += 8;
|
||||
}
|
||||
|
||||
_floatType(): FloatType {
|
||||
get [FloatType](): FloatType {
|
||||
return 'Double';
|
||||
}
|
||||
|
||||
|
@ -122,22 +131,13 @@ export class Double extends Float {
|
|||
}
|
||||
}
|
||||
|
||||
function isFloat(x: any, t: FloatType): x is Float {
|
||||
return typeof x === 'object' && x !== null &&
|
||||
'value' in x && typeof x.value === 'number' &&
|
||||
'_floatType' in x && x._floatType() === t;
|
||||
}
|
||||
|
||||
export const isSingle = (x: any): x is Single => isFloat(x, 'Single');
|
||||
export const isDouble = (x: any): x is Double => isFloat(x, 'Double');
|
||||
|
||||
export type BytesLike = Bytes | Uint8Array;
|
||||
|
||||
export class Bytes {
|
||||
readonly _view: Uint8Array;
|
||||
|
||||
constructor(maybeByteIterable: any = new Uint8Array()) {
|
||||
if (isBytes(maybeByteIterable)) {
|
||||
if (Bytes.isBytes(maybeByteIterable)) {
|
||||
this._view = maybeByteIterable._view;
|
||||
} else if (ArrayBuffer.isView(maybeByteIterable)) {
|
||||
this._view = new Uint8Array(maybeByteIterable.buffer,
|
||||
|
@ -182,13 +182,13 @@ export class Bytes {
|
|||
|
||||
static fromIO(io: string | BytesLike): string | Bytes {
|
||||
if (typeof io === 'string') return io;
|
||||
if (isBytes(io)) return io;
|
||||
if (Bytes.isBytes(io)) return io;
|
||||
if (io instanceof Uint8Array) return new Bytes(io);
|
||||
}
|
||||
|
||||
static toIO(b : string | BytesLike): string | Uint8Array {
|
||||
if (typeof b === 'string') return b;
|
||||
if (isBytes(b)) return b._view;
|
||||
if (Bytes.isBytes(b)) return b._view;
|
||||
if (b instanceof Uint8Array) return b;
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ export class Bytes {
|
|||
}
|
||||
|
||||
equals(other: any): boolean {
|
||||
if (!isBytes(other)) return false;
|
||||
if (!Bytes.isBytes(other)) return false;
|
||||
if (other.length !== this.length) return false;
|
||||
const va = this._view;
|
||||
const vb = other._view;
|
||||
|
@ -287,6 +287,14 @@ export class Bytes {
|
|||
encoder.varint(this.length);
|
||||
encoder.emitbytes(this._view);
|
||||
}
|
||||
|
||||
get [IsPreservesBytes](): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
static isBytes(x: any): x is Bytes {
|
||||
return !!x?.[IsPreservesBytes];
|
||||
}
|
||||
}
|
||||
|
||||
export function hexDigit(n: number): string {
|
||||
|
@ -300,12 +308,6 @@ export function unhexDigit(asciiCode: number) {
|
|||
throw new Error("Invalid hex digit: " + String.fromCharCode(asciiCode));
|
||||
}
|
||||
|
||||
export function isBytes(x: any): x is Bytes {
|
||||
return typeof x === 'object' && x !== null &&
|
||||
'_view' in x &&
|
||||
x._view instanceof Uint8Array;
|
||||
}
|
||||
|
||||
export function underlying(b: Bytes | Uint8Array): Uint8Array {
|
||||
return (b instanceof Uint8Array) ? b : b._view;
|
||||
}
|
||||
|
@ -437,7 +439,7 @@ export class Record extends Array<Value> {
|
|||
}
|
||||
|
||||
equals(other: any): boolean {
|
||||
return isRecord(other) &&
|
||||
return Record.isRecord(other) &&
|
||||
is(this.label, other.label) &&
|
||||
this.every((f, i) => is(f, other.get(i)));
|
||||
}
|
||||
|
@ -483,9 +485,9 @@ export class Record extends Array<Value> {
|
|||
}
|
||||
return new Record(label, fields);
|
||||
};
|
||||
ctor.constructorInfo = { label, arity };
|
||||
ctor.isClassOf =
|
||||
(v: any): v is Record => (isRecord(v) && is(label, v.label) && v.length === arity);
|
||||
const constructorInfo = { label, arity };
|
||||
ctor.constructorInfo = constructorInfo;
|
||||
ctor.isClassOf = (v: any): v is Record => Record.isClassOf(constructorInfo, v);
|
||||
ctor._ = {};
|
||||
fieldNames.forEach((name, i) => {
|
||||
ctor._[name] = function (r: any): Value {
|
||||
|
@ -505,6 +507,18 @@ export class Record extends Array<Value> {
|
|||
this.forEach((f) => encoder.push(f));
|
||||
encoder.emitbyte(Tag.End);
|
||||
}
|
||||
|
||||
get [IsPreservesRecord](): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
static isRecord(x: any): x is Record {
|
||||
return !!x?.[IsPreservesRecord];
|
||||
}
|
||||
|
||||
static isClassOf(ci: RecordConstructorInfo, v: any): v is Record {
|
||||
return (Record.isRecord(v)) && is(ci.label, v.label) && (ci.arity === v.length);
|
||||
}
|
||||
}
|
||||
|
||||
export interface RecordConstructor {
|
||||
|
@ -520,8 +534,8 @@ export interface RecordConstructorInfo {
|
|||
}
|
||||
|
||||
export function is(a: any, b: any): boolean {
|
||||
if (isAnnotated(a)) a = a.item;
|
||||
if (isAnnotated(b)) b = b.item;
|
||||
if (Annotated.isAnnotated(a)) a = a.item;
|
||||
if (Annotated.isAnnotated(b)) b = b.item;
|
||||
if (Object.is(a, b)) return true;
|
||||
if (typeof a !== typeof b) return false;
|
||||
if (typeof a === 'object') {
|
||||
|
@ -540,18 +554,8 @@ export function hash(a: Value): number {
|
|||
throw new Error("shouldBeImplemented"); // TODO
|
||||
}
|
||||
|
||||
export function isClassOf(ci: RecordConstructorInfo, v: any): v is Record {
|
||||
return (isRecord(v)) && is(ci.label, v.label) && (ci.arity === v.length);
|
||||
}
|
||||
|
||||
export type DictionaryType = 'Dictionary' | 'Set';
|
||||
|
||||
export function is_Dictionary(x: any, t: DictionaryType): boolean {
|
||||
return typeof x === 'object' && x !== null && x[Symbol.toStringTag] === t;
|
||||
}
|
||||
|
||||
export const isDictionary = <T> (x: any): x is Dictionary<T> => is_Dictionary(x, 'Dictionary');
|
||||
export const isSet = (x: any): x is Set => is_Dictionary(x, 'Set');
|
||||
export const DictionaryType = Symbol.for('DictionaryType');
|
||||
|
||||
export function _canonicalString(item: Value): string {
|
||||
const bs = encode(item, { canonical: true })._view;
|
||||
|
@ -560,8 +564,16 @@ export function _canonicalString(item: Value): string {
|
|||
}
|
||||
|
||||
export class Dictionary<T> extends FlexMap<Value, T> {
|
||||
get [DictionaryType](): DictionaryType {
|
||||
return 'Dictionary';
|
||||
}
|
||||
|
||||
static isDictionary<T>(x: any): x is Dictionary<T> {
|
||||
return x?.[DictionaryType] === 'Dictionary';
|
||||
}
|
||||
|
||||
static fromJS(x: object): Dictionary<Value> {
|
||||
if (isDictionary(x)) return x as Dictionary<Value>;
|
||||
if (Dictionary.isDictionary(x)) return x as Dictionary<Value>;
|
||||
const d = new Dictionary<Value>();
|
||||
for (let key in x) {
|
||||
const value = x[key];
|
||||
|
@ -619,6 +631,14 @@ export class Dictionary<T> extends FlexMap<Value, T> {
|
|||
}
|
||||
|
||||
export class Set extends FlexSet<Value> {
|
||||
get [DictionaryType](): DictionaryType {
|
||||
return 'Set';
|
||||
}
|
||||
|
||||
static isSet(x: any): x is Set {
|
||||
return x?.[DictionaryType] === 'Set';
|
||||
}
|
||||
|
||||
constructor(items?: Iterable<any>) {
|
||||
super(_canonicalString, _iterMap(items?.[Symbol.iterator](), fromJS));
|
||||
}
|
||||
|
@ -656,13 +676,6 @@ export class Set extends FlexSet<Value> {
|
|||
}
|
||||
}
|
||||
|
||||
export function isAnnotated(x: any): x is Annotated {
|
||||
return typeof x === 'object' && x !== null &&
|
||||
x.constructor.name === 'Annotated' &&
|
||||
'annotations' in x &&
|
||||
'item' in x;
|
||||
}
|
||||
|
||||
export class Annotated {
|
||||
readonly annotations: Array<Value>;
|
||||
readonly item: Value;
|
||||
|
@ -687,7 +700,7 @@ export class Annotated {
|
|||
}
|
||||
|
||||
equals(other: any): boolean {
|
||||
return is(this.item, isAnnotated(other) ? other.item : other);
|
||||
return is(this.item, Annotated.isAnnotated(other) ? other.item : other);
|
||||
}
|
||||
|
||||
hashCode(): number {
|
||||
|
@ -702,6 +715,14 @@ export class Annotated {
|
|||
const anns = this.annotations.map((a) => '@' + a.asPreservesText()).join(' ');
|
||||
return (anns ? anns + ' ' : anns) + this.item.asPreservesText();
|
||||
}
|
||||
|
||||
get [IsPreservesAnnotated](): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
static isAnnotated(x: any): x is Annotated {
|
||||
return !!x?.[IsPreservesAnnotated];
|
||||
}
|
||||
}
|
||||
|
||||
export function peel(v: Value): Value {
|
||||
|
@ -711,20 +732,20 @@ export function peel(v: Value): Value {
|
|||
export function strip(v: Value, depth: number = Infinity) {
|
||||
function step(v: Value, depth: number): Value {
|
||||
if (depth === 0) return v;
|
||||
if (!isAnnotated(v)) return v;
|
||||
if (!Annotated.isAnnotated(v)) return v;
|
||||
|
||||
const nextDepth = depth - 1;
|
||||
function walk(v: Value) { return step(v, nextDepth); }
|
||||
|
||||
if (isRecord(v.item)) {
|
||||
if (Record.isRecord(v.item)) {
|
||||
return new Record(step(v.item.label, depth), v.item.map(walk));
|
||||
} else if (Array.isArray(v.item)) {
|
||||
return v.item.map(walk);
|
||||
} else if (isSet(v.item)) {
|
||||
} else if (Set.isSet(v.item)) {
|
||||
return v.item.map(walk);
|
||||
} else if (isDictionary(v.item)) {
|
||||
} else if (Dictionary.isDictionary(v.item)) {
|
||||
return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]);
|
||||
} else if (isAnnotated(v.item)) {
|
||||
} else if (Annotated.isAnnotated(v.item)) {
|
||||
throw new Error("Improper annotation structure");
|
||||
} else {
|
||||
return v.item;
|
||||
|
@ -734,7 +755,7 @@ export function strip(v: Value, depth: number = Infinity) {
|
|||
}
|
||||
|
||||
export function annotate(v0: Value, ...anns: Value[]) {
|
||||
const v = isAnnotated(v0) ? v0 : new Annotated(v0);
|
||||
const v = Annotated.isAnnotated(v0) ? v0 : new Annotated(v0);
|
||||
anns.forEach((a) => v.annotations.push(a));
|
||||
return v;
|
||||
}
|
||||
|
|
|
@ -13,68 +13,70 @@ describe('immutable byte arrays', () => {
|
|||
expect(bs.every((b) => b !== 50)).toBe(true);
|
||||
expect(!(bs.every((b) => b !== 20))).toBe(true);
|
||||
});
|
||||
// it('should implement find', () => {
|
||||
// assert.strictEqual(bs.find((b) => b > 20), 30);
|
||||
// assert.strictEqual(bs.find((b) => b > 50), void 0);
|
||||
// });
|
||||
// it('should implement findIndex', () => {
|
||||
// assert.strictEqual(bs.findIndex((b) => b > 20), 2);
|
||||
// assert.strictEqual(bs.findIndex((b) => b > 50), -1);
|
||||
// });
|
||||
// it('should implement forEach', () => {
|
||||
// const vs = [];
|
||||
// bs.forEach((b) => vs.push(b));
|
||||
// assert(is(fromJS(vs), fromJS([10, 20, 30, 40])));
|
||||
// });
|
||||
// it('should implement includes', () => {
|
||||
// assert(bs.includes(20));
|
||||
// assert(!bs.includes(50));
|
||||
// });
|
||||
// it('should implement indexOf', () => {
|
||||
// assert.strictEqual(bs.indexOf(20), 1);
|
||||
// assert.strictEqual(bs.indexOf(50), -1);
|
||||
// });
|
||||
// it('should implement join', () => assert.strictEqual(bs.join('-'), '10-20-30-40'));
|
||||
// it('should implement keys', () => {
|
||||
// assert(is(fromJS(Array.from(bs.keys())), fromJS([0,1,2,3])));
|
||||
// });
|
||||
// it('should implement values', () => {
|
||||
// assert(is(fromJS(Array.from(bs.values())), fromJS([10,20,30,40])));
|
||||
// });
|
||||
// it('should implement filter', () => {
|
||||
// assert(is(bs.filter((b) => b !== 30), Bytes.of(10,20,40)));
|
||||
// });
|
||||
// it('should implement slice', () => {
|
||||
// const vs = bs.slice(2);
|
||||
// assert(!Object.is(vs._view.buffer, bs._view.buffer));
|
||||
// assert.strictEqual(vs._view.buffer.byteLength, 2);
|
||||
// assert.strictEqual(vs.get(0), 30);
|
||||
// assert.strictEqual(vs.get(1), 40);
|
||||
// assert.strictEqual(vs.size, 2);
|
||||
// });
|
||||
// it('should implement subarray', () => {
|
||||
// const vs = bs.subarray(2);
|
||||
// assert(Object.is(vs._view.buffer, bs._view.buffer));
|
||||
// assert.strictEqual(vs._view.buffer.byteLength, 4);
|
||||
// assert.strictEqual(vs.get(0), 30);
|
||||
// assert.strictEqual(vs.get(1), 40);
|
||||
// assert.strictEqual(vs.size, 2);
|
||||
// });
|
||||
// it('should implement reverse', () => {
|
||||
// const vs = bs.reverse();
|
||||
// assert(!Object.is(vs._view.buffer, bs._view.buffer));
|
||||
// assert.strictEqual(bs.get(0), 10);
|
||||
// assert.strictEqual(bs.get(3), 40);
|
||||
// assert.strictEqual(vs.get(0), 40);
|
||||
// assert.strictEqual(vs.get(3), 10);
|
||||
// });
|
||||
// it('should implement sort', () => {
|
||||
// const vs = bs.reverse().sort();
|
||||
// assert(!Object.is(vs._view.buffer, bs._view.buffer));
|
||||
// assert.strictEqual(bs.get(0), 10);
|
||||
// assert.strictEqual(bs.get(3), 40);
|
||||
// assert.strictEqual(vs.get(0), 10);
|
||||
// assert.strictEqual(vs.get(3), 40);
|
||||
// });
|
||||
it('should implement find', () => {
|
||||
expect(bs.find((b) => b > 20)).toBe(30);
|
||||
expect(bs.find((b) => b > 50)).toBe(void 0);
|
||||
});
|
||||
it('should implement findIndex', () => {
|
||||
expect(bs.findIndex((b) => b > 20)).toBe(2);
|
||||
expect(bs.findIndex((b) => b > 50)).toBe(-1);
|
||||
});
|
||||
it('should implement forEach', () => {
|
||||
const vs = [];
|
||||
bs.forEach((b) => vs.push(b));
|
||||
expect(fromJS(vs)).is(fromJS([10, 20, 30, 40]));
|
||||
});
|
||||
it('should implement includes', () => {
|
||||
expect(bs.includes(20)).toBe(true);
|
||||
expect(!bs.includes(50)).toBe(true);
|
||||
});
|
||||
it('should implement indexOf', () => {
|
||||
expect(bs.indexOf(20)).toBe(1);
|
||||
expect(bs.indexOf(50)).toBe(-1);
|
||||
});
|
||||
it('should implement join', () => {
|
||||
expect(bs.join('-')).toBe('10-20-30-40');
|
||||
});
|
||||
it('should implement keys', () => {
|
||||
expect(fromJS(Array.from(bs.keys()))).is(fromJS([0,1,2,3]));
|
||||
});
|
||||
it('should implement values', () => {
|
||||
expect(fromJS(Array.from(bs.values()))).is(fromJS([10,20,30,40]));
|
||||
});
|
||||
it('should implement filter', () => {
|
||||
expect(bs.filter((b) => b !== 30)).is(Bytes.of(10,20,40));
|
||||
});
|
||||
it('should implement slice', () => {
|
||||
const vs = bs.slice(2);
|
||||
expect(Object.is(vs._view.buffer, bs._view.buffer)).toBe(false);
|
||||
expect(vs._view.buffer.byteLength).toBe(2);
|
||||
expect(vs.get(0)).toBe(30);
|
||||
expect(vs.get(1)).toBe(40);
|
||||
expect(vs.length).toBe(2);
|
||||
});
|
||||
it('should implement subarray', () => {
|
||||
const vs = bs.subarray(2);
|
||||
expect(Object.is(vs._view.buffer, bs._view.buffer)).toBe(true);
|
||||
expect(vs._view.buffer.byteLength).toBe(4);
|
||||
expect(vs.get(0)).toBe(30);
|
||||
expect(vs.get(1)).toBe(40);
|
||||
expect(vs.length).toBe(2);
|
||||
});
|
||||
it('should implement reverse', () => {
|
||||
const vs = bs.reverse();
|
||||
expect(Object.is(vs._view.buffer, bs._view.buffer)).toBe(false);
|
||||
expect(bs.get(0)).toBe(10);
|
||||
expect(bs.get(3)).toBe(40);
|
||||
expect(vs.get(0)).toBe(40);
|
||||
expect(vs.get(3)).toBe(10);
|
||||
});
|
||||
it('should implement sort', () => {
|
||||
const vs = bs.reverse().sort();
|
||||
expect(Object.is(vs._view.buffer, bs._view.buffer)).toBe(false);
|
||||
expect(bs.get(0)).toBe(10);
|
||||
expect(bs.get(3)).toBe(40);
|
||||
expect(vs.get(0)).toBe(10);
|
||||
expect(vs.get(3)).toBe(40);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
Value,
|
||||
Dictionary,
|
||||
decode, decodeWithAnnotations, encodeWithAnnotations,
|
||||
isDecodeError, isShortPacket,
|
||||
DecodeError, ShortPacket,
|
||||
Bytes, Record,
|
||||
annotate,
|
||||
strip, peel,
|
||||
|
@ -150,7 +150,9 @@ describe('common test suite', () => {
|
|||
describe(tName, () => {
|
||||
it('should fail with DecodeError', () => {
|
||||
expect(() => D(strip(t[0]) as Bytes))
|
||||
.toThrowFilter(e => isDecodeError(e) && !isShortPacket(e));
|
||||
.toThrowFilter(e =>
|
||||
DecodeError.isDecodeError(e) &&
|
||||
!ShortPacket.isShortPacket(e));
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
@ -159,7 +161,7 @@ describe('common test suite', () => {
|
|||
describe(tName, () => {
|
||||
it('should fail with ShortPacket', () => {
|
||||
expect(() => D(strip(t[0]) as Bytes))
|
||||
.toThrowFilter(e => isShortPacket(e));
|
||||
.toThrowFilter(e => ShortPacket.isShortPacket(e));
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue