mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-21 19:49:05 -05:00
jsPath (#1527)
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@
|
||||
"tingodb": "^0.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@attic/eslintrc": "^1.0.0",
|
||||
"@attic/eslintrc": "^2.0.0",
|
||||
"babel-cli": "6.6.5",
|
||||
"babel-core": "6.7.2",
|
||||
"babel-generator": "6.7.2",
|
||||
|
||||
+2
-3
@@ -47,9 +47,8 @@ export function newList<T: valueOrPrimitive>(values: Array<T>): Promise<List<T>>
|
||||
|
||||
export default class List<T: valueOrPrimitive> extends Collection<IndexedSequence> {
|
||||
async get(idx: number): Promise<T> {
|
||||
// TODO (when |length| works) invariant(idx < this.length, idx + ' >= ' + this.length);
|
||||
const cursor = await this.sequence.newCursorAt(idx);
|
||||
return cursor.getCurrent();
|
||||
invariant(idx >= 0 && idx < this.length);
|
||||
return this.sequence.newCursorAt(idx).then(cursor => cursor.getCurrent());
|
||||
}
|
||||
|
||||
splice(idx: number, deleteCount: number, ...insert: Array<T>): Promise<List<T>> {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// @flow
|
||||
|
||||
import {assert} from 'chai';
|
||||
import {suite, test} from 'mocha';
|
||||
import {equals} from './compare.js';
|
||||
|
||||
import Path from './path.js';
|
||||
import type {valueOrPrimitive} from './value.js';
|
||||
import {newList} from './list.js';
|
||||
import {newMap} from './map.js';
|
||||
import {newStruct} from './struct.js';
|
||||
|
||||
suite('Path', () => {
|
||||
|
||||
async function assertPathEqual(expect: any, ref: valueOrPrimitive, path: Path):
|
||||
Promise<void> {
|
||||
// $FlowIssue: need to be able to pass in null for ref
|
||||
const actual = await path.resolve(ref);
|
||||
if (actual === undefined || expect === undefined) {
|
||||
assert.strictEqual(expect, actual);
|
||||
return;
|
||||
}
|
||||
|
||||
assert.isTrue(equals(expect, actual));
|
||||
}
|
||||
|
||||
test('struct', async () => {
|
||||
const v = newStruct('', {
|
||||
foo: 'foo',
|
||||
bar: false,
|
||||
baz: 203,
|
||||
});
|
||||
|
||||
await assertPathEqual('foo', v, new Path().addField('foo'));
|
||||
await assertPathEqual(false, v, new Path().addField('bar'));
|
||||
await assertPathEqual(203, v, new Path().addField('baz'));
|
||||
await assertPathEqual(undefined, v, new Path().addField('notHere'));
|
||||
|
||||
const v2 = newStruct('', {
|
||||
v1: v,
|
||||
});
|
||||
|
||||
await assertPathEqual('foo', v2, new Path().addField('v1').addField('foo'));
|
||||
await assertPathEqual(false, v2, new Path().addField('v1').addField('bar'));
|
||||
await assertPathEqual(203, v2, new Path().addField('v1').addField('baz'));
|
||||
await assertPathEqual(undefined, v2, new Path().addField('v1').addField('notHere'));
|
||||
await assertPathEqual(undefined, v2, new Path().addField('notHere').addField('foo'));
|
||||
});
|
||||
|
||||
test('list', async () => {
|
||||
const v = await newList([1, 3, 'foo', false]);
|
||||
|
||||
await assertPathEqual(1, v, new Path().addIndex(0));
|
||||
await assertPathEqual(3, v, new Path().addIndex(1));
|
||||
await assertPathEqual('foo', v, new Path().addIndex(2));
|
||||
await assertPathEqual(false, v, new Path().addIndex(3));
|
||||
await assertPathEqual(undefined, v, new Path().addIndex(4));
|
||||
await assertPathEqual(undefined, v, new Path().addIndex(-4));
|
||||
});
|
||||
|
||||
test('map', async () => {
|
||||
const v = await newMap([1, 'foo', 'two', 'bar', false, 23, 2.3, 4.5]);
|
||||
|
||||
await assertPathEqual('foo', v, new Path().addIndex(1));
|
||||
await assertPathEqual('bar', v, new Path().addIndex('two'));
|
||||
await assertPathEqual(23, v, new Path().addIndex(false));
|
||||
await assertPathEqual(4.5, v, new Path().addIndex(2.3));
|
||||
await assertPathEqual(undefined, v, new Path().addIndex(4));
|
||||
});
|
||||
|
||||
test('struct.list.map', async () => {
|
||||
const m1 = await newMap(['a', 'foo', 'b','bar', 'c', 'car']);
|
||||
const m2 = await newMap(['d', 'dar', false, 'earth']);
|
||||
const l = await newList([m1, m2]);
|
||||
const s = newStruct('', {
|
||||
foo: l,
|
||||
});
|
||||
|
||||
await assertPathEqual(l, s, new Path().addField('foo'));
|
||||
await assertPathEqual(m1, s, new Path().addField('foo').addIndex(0));
|
||||
await assertPathEqual('foo', s, new Path().addField('foo').addIndex(0).addIndex('a'));
|
||||
await assertPathEqual('bar', s, new Path().addField('foo').addIndex(0).addIndex('b'));
|
||||
await assertPathEqual('car', s, new Path().addField('foo').addIndex(0).addIndex('c'));
|
||||
await assertPathEqual(undefined, s, new Path().addField('foo').addIndex(0).addIndex('x'));
|
||||
await assertPathEqual(undefined, s, new Path().addField('foo').addIndex(2).addIndex('c'));
|
||||
await assertPathEqual(undefined, s, new Path().addField('notHere').addIndex(2).addIndex('c'));
|
||||
await assertPathEqual(m2, s, new Path().addField('foo').addIndex(1));
|
||||
await assertPathEqual('dar', s, new Path().addField('foo').addIndex(1).addIndex('d'));
|
||||
await assertPathEqual('earth', s, new Path().addField('foo').addIndex(1).addIndex(false));
|
||||
});
|
||||
|
||||
function assertToString(expect: string, path: Path) {
|
||||
assert.strictEqual(expect, path.toString());
|
||||
}
|
||||
|
||||
test('toString()', () => {
|
||||
assertToString('[0][1][100]', new Path().addIndex(0).addIndex(1).addIndex(100));
|
||||
assertToString('["0"]["1"]["100"]',
|
||||
new Path().addIndex('0').addIndex('1').addIndex('100'));
|
||||
assertToString('.foo[0].bar[4.5][false]',
|
||||
new Path().addField('foo').addIndex(0).addField('bar').addIndex(4.5).addIndex(false));
|
||||
});
|
||||
});
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// @flow
|
||||
import type {valueOrPrimitive} from './value.js';
|
||||
import {Kind} from './noms-kind.js';
|
||||
import List from './list.js';
|
||||
import Map from './map.js';
|
||||
import {getTypeOfValue, StructDesc} from './type.js';
|
||||
|
||||
interface Part {
|
||||
resolve(v: Promise<?valueOrPrimitive>): Promise<?valueOrPrimitive>;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
class FieldPart {
|
||||
name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
resolve(v: Promise<?valueOrPrimitive>): Promise<?valueOrPrimitive> {
|
||||
return v.then(value => {
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const t = getTypeOfValue(value);
|
||||
if (t.kind !== Kind.Struct) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f = (t.desc: StructDesc).fields[this.name];
|
||||
if (!f) {
|
||||
return; // non-present field
|
||||
}
|
||||
|
||||
// $FlowIssue: Flow doesn't know that it's safe to just access the field name here.
|
||||
return value[this.name];
|
||||
});
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `.${this.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support value
|
||||
type indexType = boolean | number | string;
|
||||
|
||||
class IndexPart {
|
||||
idx: indexType;
|
||||
|
||||
constructor(idx: indexType) {
|
||||
const t = getTypeOfValue(idx);
|
||||
switch (t.kind) {
|
||||
case Kind.String:
|
||||
case Kind.Bool:
|
||||
case Kind.Number:
|
||||
this.idx = idx;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported');
|
||||
}
|
||||
}
|
||||
|
||||
resolve(v: Promise<?valueOrPrimitive>): Promise<?valueOrPrimitive> {
|
||||
return v.then(value => {
|
||||
if (value === null || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof List) {
|
||||
if (typeof this.idx !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.idx < 0 || this.idx >= value.length) {
|
||||
return undefined; // index out of bounds
|
||||
}
|
||||
|
||||
return value.get(this.idx);
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
return value.get(this.idx);
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
switch (typeof this.idx) {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
case 'string':
|
||||
return `[${JSON.stringify(this.idx)}]`;
|
||||
default:
|
||||
throw new Error('not reached');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class Path {
|
||||
_parts: Array<Part>;
|
||||
|
||||
constructor() {
|
||||
this._parts = [];
|
||||
}
|
||||
|
||||
_addPart(part: Part): Path {
|
||||
const p = new Path();
|
||||
p._parts = this._parts.concat(part);
|
||||
return p;
|
||||
}
|
||||
|
||||
addField(name: string): Path {
|
||||
return this._addPart(new FieldPart(name));
|
||||
}
|
||||
|
||||
addIndex(idx: indexType): Path {
|
||||
return this._addPart(new IndexPart(idx));
|
||||
}
|
||||
|
||||
resolve(v: valueOrPrimitive): Promise<?valueOrPrimitive> {
|
||||
return this._parts.reduce((v, p) => p.resolve(v), Promise.resolve(v));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this._parts.join('');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user