Use new unified addresses in Graph V3 (#361)

Summary:
This is basically a textual substitution.

Test Plan:
Existing unit tests suffice. Note that `_address.js` (with underscore)
is no longer imported except from its own tests.

wchargin-branch: unified-addresses-in-graph
This commit is contained in:
William Chargin 2018-06-07 09:32:06 -07:00 committed by GitHub
parent fe1dd326ed
commit 70c1f64854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 102 deletions

View File

@ -1,36 +1,27 @@
// @flow // @flow
import type {NodeAddress, EdgeAddress} from "./_address";
import * as Address from "./_address";
import {makeAddressModule, type AddressModule} from "./address"; import {makeAddressModule, type AddressModule} from "./address";
export type {NodeAddress, EdgeAddress} from "./_address"; export opaque type NodeAddressT: string = string;
export opaque type EdgeAddressT: string = string;
// New-style node and edge address types and modules. Will be made export const NodeAddress: AddressModule<NodeAddressT> = (makeAddressModule({
// public once implementation is complete.
export opaque type _NodeAddressT: string = string;
export opaque type _EdgeAddressT: string = string;
export const _NodeAddress: AddressModule<_NodeAddressT> = (makeAddressModule({
name: "NodeAddress", name: "NodeAddress",
nonce: "N", nonce: "N",
otherNonces: new Map().set("E", "EdgeAddress"), otherNonces: new Map().set("E", "EdgeAddress"),
}): AddressModule<string>); }): AddressModule<string>);
export const _EdgeAddress: AddressModule<_EdgeAddressT> = (makeAddressModule({ export const EdgeAddress: AddressModule<EdgeAddressT> = (makeAddressModule({
name: "EdgeAddress", name: "EdgeAddress",
nonce: "E", nonce: "E",
otherNonces: new Map().set("N", "NodeAddress"), otherNonces: new Map().set("N", "NodeAddress"),
}): AddressModule<string>); }): AddressModule<string>);
Object.freeze(Address);
export {Address};
export type Edge = {| export type Edge = {|
+address: EdgeAddress, +address: EdgeAddressT,
+src: NodeAddress, +src: NodeAddressT,
+dst: NodeAddress, +dst: NodeAddressT,
|}; |};
export type Neighbor = {|+node: NodeAddress, +edge: Edge|}; export type Neighbor = {|+node: NodeAddressT, +edge: Edge|};
export opaque type DirectionT = Symbol; export opaque type DirectionT = Symbol;
export const Direction: {| export const Direction: {|
@ -45,8 +36,8 @@ export const Direction: {|
export type NeighborsOptions = {| export type NeighborsOptions = {|
+direction: DirectionT, +direction: DirectionT,
+nodePrefix: NodeAddress, +nodePrefix: NodeAddressT,
+edgePrefix: EdgeAddress, +edgePrefix: EdgeAddressT,
|}; |};
export opaque type GraphJSON = any; // TODO export opaque type GraphJSON = any; // TODO
@ -59,10 +50,10 @@ export class Graph {
// //
// Invariant: If an edge `e` is in the graph, then `e.src` and `e.dst` // Invariant: If an edge `e` is in the graph, then `e.src` and `e.dst`
// are both in the graph. // are both in the graph.
_nodes: Set<NodeAddress>; _nodes: Set<NodeAddressT>;
_edges: Map<EdgeAddress, Edge>; _edges: Map<EdgeAddressT, Edge>;
_inEdges: Map<NodeAddress, Edge[]>; _inEdges: Map<NodeAddressT, Edge[]>;
_outEdges: Map<NodeAddress, Edge[]>; _outEdges: Map<NodeAddressT, Edge[]>;
constructor(): void { constructor(): void {
this._nodes = new Set(); this._nodes = new Set();
@ -71,14 +62,14 @@ export class Graph {
this._outEdges = new Map(); this._outEdges = new Map();
} }
addNode(a: NodeAddress): this { addNode(a: NodeAddressT): this {
Address.assertNodeAddress(a); NodeAddress.assertValid(a);
this._nodes.add(a); this._nodes.add(a);
return this; return this;
} }
removeNode(a: NodeAddress): this { removeNode(a: NodeAddressT): this {
Address.assertNodeAddress(a); NodeAddress.assertValid(a);
for (const e of this.edges()) { for (const e of this.edges()) {
if (e.src === a || e.dst === a) { if (e.src === a || e.dst === a) {
const srcOrDst = e.src === a ? "src" : "dst"; const srcOrDst = e.src === a ? "src" : "dst";
@ -91,19 +82,19 @@ export class Graph {
return this; return this;
} }
hasNode(a: NodeAddress): boolean { hasNode(a: NodeAddressT): boolean {
Address.assertNodeAddress(a); NodeAddress.assertValid(a);
return this._nodes.has(a); return this._nodes.has(a);
} }
*nodes(): Iterator<NodeAddress> { *nodes(): Iterator<NodeAddressT> {
yield* this._nodes; yield* this._nodes;
} }
addEdge(edge: Edge): this { addEdge(edge: Edge): this {
Address.assertNodeAddress(edge.src, "edge.src"); NodeAddress.assertValid(edge.src, "edge.src");
Address.assertNodeAddress(edge.dst, "edge.dst"); NodeAddress.assertValid(edge.dst, "edge.dst");
Address.assertEdgeAddress(edge.address, "edge.address"); EdgeAddress.assertValid(edge.address, "edge.address");
const srcMissing = !this._nodes.has(edge.src); const srcMissing = !this._nodes.has(edge.src);
const dstMissing = !this._nodes.has(edge.dst); const dstMissing = !this._nodes.has(edge.dst);
@ -129,19 +120,19 @@ export class Graph {
return this; return this;
} }
removeEdge(address: EdgeAddress): this { removeEdge(address: EdgeAddressT): this {
Address.assertEdgeAddress(address); EdgeAddress.assertValid(address);
this._edges.delete(address); this._edges.delete(address);
return this; return this;
} }
hasEdge(address: EdgeAddress): boolean { hasEdge(address: EdgeAddressT): boolean {
Address.assertEdgeAddress(address); EdgeAddress.assertValid(address);
return this._edges.has(address); return this._edges.has(address);
} }
edge(address: EdgeAddress): ?Edge { edge(address: EdgeAddressT): ?Edge {
Address.assertEdgeAddress(address); EdgeAddress.assertValid(address);
return this._edges.get(address); return this._edges.get(address);
} }
@ -149,7 +140,7 @@ export class Graph {
yield* this._edges.values(); yield* this._edges.values();
} }
neighbors(node: NodeAddress, options: NeighborsOptions): Iterator<Neighbor> { neighbors(node: NodeAddressT, options: NeighborsOptions): Iterator<Neighbor> {
const _ = {node, options}; const _ = {node, options};
throw new Error("neighbors"); throw new Error("neighbors");
} }
@ -174,8 +165,8 @@ export class Graph {
} }
export function edgeToString(edge: Edge): string { export function edgeToString(edge: Edge): string {
const address = Address.edgeToString(edge.address); const address = EdgeAddress.toString(edge.address);
const src = Address.nodeToString(edge.src); const src = NodeAddress.toString(edge.src);
const dst = Address.nodeToString(edge.dst); const dst = NodeAddress.toString(edge.dst);
return `{address: ${address}, src: ${src}, dst: ${dst}}`; return `{address: ${address}, src: ${src}, dst: ${dst}}`;
} }

View File

@ -1,48 +1,21 @@
// @flow // @flow
import { import {
type EdgeAddress, type EdgeAddressT,
type NodeAddress, type NodeAddressT,
type _EdgeAddressT,
type _NodeAddressT,
Address,
Direction, Direction,
EdgeAddress,
Graph, Graph,
NodeAddress,
edgeToString, edgeToString,
} from "./graph"; } from "./graph";
describe("core/graph", () => { describe("core/graph", () => {
const {nodeAddress, edgeAddress} = Address;
describe("Address re-exports", () => {
it("exist", () => {
expect(Address).toEqual(expect.anything());
});
it("include distinct NodeAddress and EdgeAddress types", () => {
const node: NodeAddress = nodeAddress([]);
const edge: EdgeAddress = edgeAddress([]);
// $ExpectFlowError
const _unused_badNodeAddress: NodeAddress = edge;
// $ExpectFlowError
const _unused_badEdgeAddress: EdgeAddress = node;
});
it("are read-only", () => {
const originalToParts = Address.toParts;
const wonkyToParts: typeof originalToParts = (a) => [
...originalToParts(a),
"wat",
];
expect(() => {
// $ExpectFlowError
Address.toParts = wonkyToParts;
}).toThrow(/read.only property/);
});
});
function _unused_itExportsDistinctNodeAddressAndEdgeAddressTypes() { function _unused_itExportsDistinctNodeAddressAndEdgeAddressTypes() {
// $ExpectFlowError // $ExpectFlowError
const _unused_nodeToEdge = (x: _NodeAddressT): _EdgeAddressT => x; const _unused_nodeToEdge = (x: NodeAddressT): EdgeAddressT => x;
// $ExpectFlowError // $ExpectFlowError
const _unused_edgeToNode = (x: _EdgeAddressT): _NodeAddressT => x; const _unused_edgeToNode = (x: EdgeAddressT): NodeAddressT => x;
} }
describe("Direction values", () => { describe("Direction values", () => {
@ -80,7 +53,7 @@ describe("core/graph", () => {
describe("edge addresses", () => { describe("edge addresses", () => {
function rejectsEdgeAddress(f) { function rejectsEdgeAddress(f) {
it(`${f.name} rejects EdgeAddress`, () => { it(`${f.name} rejects EdgeAddress`, () => {
const e = edgeAddress(["foo"]); const e = EdgeAddress.fromParts(["foo"]);
// $ExpectFlowError // $ExpectFlowError
expect(() => f.call(new Graph(), e)).toThrow("got EdgeAddress"); expect(() => f.call(new Graph(), e)).toThrow("got EdgeAddress");
}); });
@ -88,9 +61,9 @@ describe("core/graph", () => {
nodeMethods.forEach(rejectsEdgeAddress); nodeMethods.forEach(rejectsEdgeAddress);
}); });
describe("remove a node that is some edge's", () => { describe("remove a node that is some edge's", () => {
const src = nodeAddress(["src"]); const src = NodeAddress.fromParts(["src"]);
const dst = nodeAddress(["dst"]); const dst = NodeAddress.fromParts(["dst"]);
const address = edgeAddress(["edge"]); const address = EdgeAddress.fromParts(["edge"]);
const edge = () => ({src, dst, address}); const edge = () => ({src, dst, address});
const graph = () => const graph = () =>
new Graph() new Graph()
@ -111,7 +84,7 @@ describe("core/graph", () => {
}); });
describe("work on", () => { describe("work on", () => {
const n1 = nodeAddress(["foo"]); const n1 = NodeAddress.fromParts(["foo"]);
it("a graph with no nodes", () => { it("a graph with no nodes", () => {
const graph = new Graph(); const graph = new Graph();
expect(graph.hasNode(n1)).toBe(false); expect(graph.hasNode(n1)).toBe(false);
@ -146,7 +119,7 @@ describe("core/graph", () => {
expect(Array.from(graph.nodes())).toHaveLength(0); expect(Array.from(graph.nodes())).toHaveLength(0);
}); });
it("a graph with two nodes", () => { it("a graph with two nodes", () => {
const n2 = nodeAddress([""]); const n2 = NodeAddress.fromParts([""]);
const graph = new Graph().addNode(n1).addNode(n2); const graph = new Graph().addNode(n1).addNode(n2);
expect(graph.hasNode(n1)).toBe(true); expect(graph.hasNode(n1)).toBe(true);
expect(graph.hasNode(n2)).toBe(true); expect(graph.hasNode(n2)).toBe(true);
@ -167,7 +140,7 @@ describe("core/graph", () => {
describe("node addresses", () => { describe("node addresses", () => {
function rejectsNodeAddress(f) { function rejectsNodeAddress(f) {
it(`${f.name} rejects NodeAddress`, () => { it(`${f.name} rejects NodeAddress`, () => {
const e = nodeAddress(["foo"]); const e = NodeAddress.fromParts(["foo"]);
// $ExpectFlowError // $ExpectFlowError
expect(() => f.call(new Graph(), e)).toThrow("got NodeAddress"); expect(() => f.call(new Graph(), e)).toThrow("got NodeAddress");
}); });
@ -177,9 +150,9 @@ describe("core/graph", () => {
describe("addEdge edge validation", () => { describe("addEdge edge validation", () => {
describe("throws on absent", () => { describe("throws on absent", () => {
const src = nodeAddress(["src"]); const src = NodeAddress.fromParts(["src"]);
const dst = nodeAddress(["dst"]); const dst = NodeAddress.fromParts(["dst"]);
const address = edgeAddress(["hi"]); const address = EdgeAddress.fromParts(["hi"]);
it("src", () => { it("src", () => {
expect(() => expect(() =>
new Graph().addNode(dst).addEdge({src, dst, address}) new Graph().addNode(dst).addEdge({src, dst, address})
@ -193,8 +166,8 @@ describe("core/graph", () => {
}); });
describe("throws on edge with", () => { describe("throws on edge with", () => {
const n = nodeAddress(["foo"]); const n = NodeAddress.fromParts(["foo"]);
const e = edgeAddress(["bar"]); const e = EdgeAddress.fromParts(["bar"]);
const x = "foomlio"; const x = "foomlio";
const badEdges = [ const badEdges = [
{ {
@ -240,9 +213,9 @@ describe("core/graph", () => {
}); });
describe("on a graph", () => { describe("on a graph", () => {
const src = nodeAddress(["foo"]); const src = NodeAddress.fromParts(["foo"]);
const dst = nodeAddress(["bar"]); const dst = NodeAddress.fromParts(["bar"]);
const address = edgeAddress(["yay"]); const address = EdgeAddress.fromParts(["yay"]);
describe("that has no edges or nodes", () => { describe("that has no edges or nodes", () => {
it("`hasEdge` is false for some address", () => { it("`hasEdge` is false for some address", () => {
@ -298,8 +271,8 @@ describe("core/graph", () => {
}); });
describe("with multiple loop edges", () => { describe("with multiple loop edges", () => {
const e1 = edgeAddress(["e1"]); const e1 = EdgeAddress.fromParts(["e1"]);
const e2 = edgeAddress(["e2"]); const e2 = EdgeAddress.fromParts(["e2"]);
const edge1 = {src, dst: src, address: e1}; const edge1 = {src, dst: src, address: e1};
const edge2 = {src, dst: src, address: e2}; const edge2 = {src, dst: src, address: e2};
const quiver = () => const quiver = () =>
@ -325,9 +298,9 @@ describe("core/graph", () => {
}); });
describe("idempotency of", () => { describe("idempotency of", () => {
const src = nodeAddress(["src"]); const src = NodeAddress.fromParts(["src"]);
const dst = nodeAddress(["dst"]); const dst = NodeAddress.fromParts(["dst"]);
const address = edgeAddress(["hi"]); const address = EdgeAddress.fromParts(["hi"]);
it("`addEdge`", () => { it("`addEdge`", () => {
const g = new Graph() const g = new Graph()
.addNode(src) .addNode(src)
@ -352,15 +325,15 @@ describe("core/graph", () => {
describe("edgeToString", () => { describe("edgeToString", () => {
it("works", () => { it("works", () => {
const edge = { const edge = {
address: Address.edgeAddress(["one", "two"]), address: EdgeAddress.fromParts(["one", "two"]),
dst: Address.nodeAddress(["five", "six"]), dst: NodeAddress.fromParts(["five", "six"]),
src: Address.nodeAddress(["three", "four"]), src: NodeAddress.fromParts(["three", "four"]),
}; };
const expected = const expected =
"{" + "{" +
'address: edgeAddress(["one","two"]), ' + 'address: EdgeAddress["one","two"], ' +
'src: nodeAddress(["three","four"]), ' + 'src: NodeAddress["three","four"], ' +
'dst: nodeAddress(["five","six"])' + 'dst: NodeAddress["five","six"]' +
"}"; "}";
expect(edgeToString(edge)).toEqual(expected); expect(edgeToString(edge)).toEqual(expected);
}); });